# Account audit log
Source: https://docs.torrin.app/api-reference/account/account-audit-log
/api-reference/openapi.yaml get /api/me/audit
# Delete the account
Source: https://docs.torrin.app/api-reference/account/delete-the-account
/api-reference/openapi.yaml delete /api/me
# End-of-month recap
Source: https://docs.torrin.app/api-reference/account/end-of-month-recap
/api-reference/openapi.yaml get /api/wrapped
A year-in-review style recap. Available only in the last 7 days of each month.
# Get the current account
Source: https://docs.torrin.app/api-reference/account/get-the-current-account
/api-reference/openapi.yaml get /api/me
# Monthly ingest usage
Source: https://docs.torrin.app/api-reference/account/monthly-ingest-usage
/api-reference/openapi.yaml get /api/usage/ingest
Your uncached ingest this calendar month against your plan's cap.
# Pause the subscription
Source: https://docs.torrin.app/api-reference/account/pause-the-subscription
/api-reference/openapi.yaml post /api/me/pause
Freezes the remaining time on the subscription so it isn't consumed while unused.
# Referral code and stats
Source: https://docs.torrin.app/api-reference/account/referral-code-and-stats
/api-reference/openapi.yaml get /api/referral
# Regenerate the API key
Source: https://docs.torrin.app/api-reference/account/regenerate-the-api-key
/api-reference/openapi.yaml post /api/auth/regenerate-key
Rotates the API key. The old key stops working immediately.
# Resume the subscription
Source: https://docs.torrin.app/api-reference/account/resume-the-subscription
/api-reference/openapi.yaml post /api/me/resume
# Update email
Source: https://docs.torrin.app/api-reference/account/update-email
/api-reference/openapi.yaml put /api/me
# Watch history
Source: https://docs.torrin.app/api-reference/account/watch-history
/api-reference/openapi.yaml get /api/history
# Your usage stats
Source: https://docs.torrin.app/api-reference/account/your-usage-stats
/api-reference/openapi.yaml get /api/stats
# Begin two-factor enrollment
Source: https://docs.torrin.app/api-reference/auth/begin-two-factor-enrollment
/api-reference/openapi.yaml post /api/auth/2fa/enroll
Returns a secret / otpauth URI to add to an authenticator app, then confirm at /api/auth/2fa/confirm.
# Complete a login challenge
Source: https://docs.torrin.app/api-reference/auth/complete-a-login-challenge
/api-reference/openapi.yaml post /api/auth/login/verify
# Confirm two-factor enrollment
Source: https://docs.torrin.app/api-reference/auth/confirm-two-factor-enrollment
/api-reference/openapi.yaml post /api/auth/2fa/confirm
# Create an API key
Source: https://docs.torrin.app/api-reference/auth/create-an-api-key
/api-reference/openapi.yaml post /api/keys
# Disable two-factor
Source: https://docs.torrin.app/api-reference/auth/disable-two-factor
/api-reference/openapi.yaml post /api/auth/2fa/disable
# List API keys
Source: https://docs.torrin.app/api-reference/auth/list-api-keys
/api-reference/openapi.yaml get /api/keys
# Log in with an API key
Source: https://docs.torrin.app/api-reference/auth/log-in-with-an-api-key
/api-reference/openapi.yaml post /api/auth/login/key
# Log in with email and password
Source: https://docs.torrin.app/api-reference/auth/log-in-with-email-and-password
/api-reference/openapi.yaml post /api/auth/login
Returns a challenge to complete at /api/auth/login/verify with the emailed or authenticator code.
# Revoke an API key
Source: https://docs.torrin.app/api-reference/auth/revoke-an-api-key
/api-reference/openapi.yaml delete /api/keys/{id}
# Set or change your password
Source: https://docs.torrin.app/api-reference/auth/set-or-change-your-password
/api-reference/openapi.yaml post /api/auth/password
# Verify a two-factor code
Source: https://docs.torrin.app/api-reference/auth/verify-a-two-factor-code
/api-reference/openapi.yaml post /api/auth/2fa/verify
# Authentication
Source: https://docs.torrin.app/api-reference/authentication
How to get and use your torrin API key.
Every torrent and store endpoint is authenticated with your instance **API key** — the
same key your Stremio addon and dashboard use. Public `/api/*` endpoints need no auth.
## Get your API key
Your API key is issued when your account is created. On the hosted instance it's shown in
the dashboard; you can also create an account directly:
```bash theme={null}
curl -X POST https://api.torrin.app/api/auth/register \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com" }'
```
```json theme={null}
{ "api_key": "YOUR_API_KEY", "email": "you@example.com", "plan": "free" }
```
Treat the key like a password. Your primary key grants full account access — if it
leaks, rotate it from the dashboard. For addons, prefer a dedicated **API-only** key
(below) that can be revoked on its own and can't be used to sign in.
## Use it
Send the key as a bearer token on every authenticated request:
```bash theme={null}
curl https://api.torrin.app/rest/1.0/torrents \
-H "Authorization: Bearer YOUR_API_KEY"
```
For compatibility, two surfaces also accept alternate forms:
| Surface | Also accepts |
| - | - |
| `/rest/1.0/*` | `?auth_token=YOUR_API_KEY` query parameter |
| `/v0/store/*` | `X-StremThru-Store-Authorization: Bearer YOUR_API_KEY` header |
## Multiple keys & API-only keys
Instead of sharing one key everywhere, you can issue several. Give each addon or
integration its own labelled key, so if one leaks you revoke just that key without
disturbing the others.
Each key is one of:
* **Login-enabled** — full account access (dashboard, billing, credentials, key management).
* **API-only** — can stream, download, and search, but **cannot sign in or change your
account**. This is the default for new keys and the right choice for an addon: a leaked
API-only key can't take over your account.
Every key belongs to your account, so all of them are bound to your plan, limits, and expiry.
Managing keys requires a login-enabled key. API-only keys receive `403` on account routes.
### List your keys
```bash theme={null}
curl https://api.torrin.app/api/keys \
-H "Authorization: Bearer YOUR_LOGIN_KEY"
```
```json theme={null}
{ "keys": [
{ "id": "k_123", "label": "AIOStreams", "login_allowed": false,
"created_at": "2026-08-13T00:00:00Z", "last_used_at": "2026-08-13T12:00:00Z" }
] }
```
### Create a key
```bash theme={null}
curl -X POST https://api.torrin.app/api/keys \
-H "Authorization: Bearer YOUR_LOGIN_KEY" \
-H "Content-Type: application/json" \
-d '{ "label": "AIOStreams", "login_allowed": false }'
```
```json theme={null}
{ "id": "k_123", "label": "AIOStreams", "key": "YOUR_NEW_KEY", "login_allowed": false }
```
The `key` is returned **once** — store it now. Up to 20 keys per account.
### Revoke a key
```bash theme={null}
curl -X DELETE https://api.torrin.app/api/keys/k_123 \
-H "Authorization: Bearer YOUR_LOGIN_KEY"
```
The key stops working immediately.
## Errors
| Status | Meaning |
| - | - |
| `401` | Missing or invalid key. |
| `403` | The subscription is paused or expired — resume or renew, then retry. |
Both error shapes differ by surface: `/rest/1.0` returns `{ "error": "...", "error_code": 8 }`,
and `/v0/store` returns `{ "error": { "code": 401, "message": "..." } }`.
# Get a crypto invoice
Source: https://docs.torrin.app/api-reference/billing/get-a-crypto-invoice
/api-reference/openapi.yaml get /api/billing/crypto/invoice/{id}
Poll the status of a crypto checkout invoice.
# Get an invoice payment address
Source: https://docs.torrin.app/api-reference/billing/get-an-invoice-payment-address
/api-reference/openapi.yaml post /api/billing/crypto/invoice/{id}/address
Returns a pay-to address for the chosen coin on a crypto invoice.
# Open the Bachs billing portal
Source: https://docs.torrin.app/api-reference/billing/open-the-bachs-billing-portal
/api-reference/openapi.yaml post /api/billing/bachs/portal
# Redeem a code
Source: https://docs.torrin.app/api-reference/billing/redeem-a-code
/api-reference/openapi.yaml post /api/redeem
Redeem a reseller/gift code to activate a plan.
# Start a card checkout
Source: https://docs.torrin.app/api-reference/billing/start-a-card-checkout
/api-reference/openapi.yaml post /api/billing/bachs/checkout
Creates a Bachs hosted checkout and returns its URL.
# Start a crypto checkout
Source: https://docs.torrin.app/api-reference/billing/start-a-crypto-checkout
/api-reference/openapi.yaml post /api/billing/crypto/checkout
Creates a Bitcart invoice; poll the returned invoice id for payment status.
# Bring your own key (BYOK)
Source: https://docs.torrin.app/api-reference/byok
Add your own RealDebrid, AllDebrid, Premiumize, or TorBox key.
Torrin can download through its own shared debrid pool, but you can also attach your own
provider keys. When you do, torrin checks and pulls through **your** key first, falling back
to the shared pool — so you get your provider's cache and limits, not just torrin's.
## Why
* **Your cache, your limits.** Availability checks and downloads use your account, so a title
cached on your RealDebrid is instant for you even if torrin has never seen it.
* **More providers.** Add Premiumize or TorBox and torrin's availability check
(`/v0/store/magnets/check`) fans out across all of them.
## The endpoints
Each provider has the same three calls, under its own prefix
(`/api/rd`, `/api/alldebrid`, `/api/premiumize`, `/api/torbox`):
| Call | Does |
| - | - |
| `GET /api/{provider}/credentials` | Report whether a key is set (returns it **masked**). |
| `POST /api/{provider}/credentials` | Validate and save a key. A bad key is rejected with `400`. |
| `DELETE /api/{provider}/credentials` | Remove the key. |
Set a key:
```bash theme={null}
curl -X POST https://api.torrin.app/api/rd/credentials \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "api_key": "YOUR_REALDEBRID_KEY" }'
```
`GET /api/debrid/usage` reports which providers are configured and how they've been used.
Keys are stored encrypted at rest and only ever returned masked. Removing a key takes effect
immediately — the next request falls back to torrin's shared pool.
# Bring your own storage (BYOS)
Source: https://docs.torrin.app/api-reference/byos
Stream from your own cloud — S3 or an rclone remote.
By default torrin caches content in its own shared store and evicts it over time. With
**bring-your-own-storage**, content you stream is also written to *your* cloud, encrypted,
and served back through torrin — so it's permanent, private, and counts against your space,
not torrin's.
## How it works
When BYOS is connected, a completed stream is copied to your backend under an encrypted
key (torrin never stores your filenames in the clear). Playback still goes through torrin's
signed, geo-routed URLs, so the origin and your credentials stay hidden — the client can't
tell BYOS content from shared-cache content.
## Connecting
Two shapes, depending on the backend:
* **S3-compatible** (S3, Backblaze, Wasabi, Garage...) — pass the endpoint and keys directly:
```bash theme={null}
curl -X POST https://api.torrin.app/api/storage/connect \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "s3",
"endpoint": "https://s3.example.com",
"region": "us-east-1",
"bucket": "my-torrin",
"access_key": "...",
"secret_key": "..."
}'
```
* **OAuth backends** (Google Drive, Dropbox, OneDrive, pCloud) — start the consent flow at
`GET /api/storage/oauth/{provider}/start` and torrin finishes the connection on callback.
## Managing it
| Call | Does |
| - | - |
| `GET /api/storage` | Whether BYOS is connected, plus the provider count and pool policy. |
| `POST /api/storage/connect` | Connect the primary backend. |
| `DELETE /api/storage` | Disconnect. Existing content stays in your cloud. |
| `PUT /api/storage/providers` | Add providers to the pool and set the policy (`epmfs` pool or `all` mirror). |
| `DELETE /api/storage/providers/{idx}` | Remove one provider from the pool. |
| `GET /api/storage/rclone-config` | Download your `rclone.conf` to mount the storage yourself. |
| `PUT /api/storage/evict-policy` | Set cleanup rules: `evict_after_days`, `evict_max_gb` (0 = keep everything). |
| `POST /api/storage/evict` | Remove one release (`info_hash`) from your storage. |
## Pooling and mirroring
Add more providers to act as one encrypted pool. `union_policy` picks placement:
`epmfs` (default) spreads files across providers by free space; `all` mirrors every
file to every provider for redundancy.
```bash theme={null}
curl -X PUT https://api.torrin.app/api/storage/providers \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"union_policy": "all",
"providers": [
{ "endpoint": "https://s3.example.com", "bucket": "backup", "access_key": "...", "secret_key": "..." }
]
}'
```
Everything written to your storage is encrypted with a key torrin holds, so a raw browse of
your bucket shows opaque blobs, not media.
# Cairn
Source: https://docs.torrin.app/api-reference/cairn
Permanent, Usenet-backed archive of your cached content.
Torrin's shared cache is a cache — content gets evicted when it's cold or over capacity.
**Cairn** is the escape hatch: it posts a cached item to Usenet, so even after the hot copy
is evicted, the item can be re-hydrated on demand instead of re-downloaded from the source.
## Why
* **Permanence without the storage bill.** Keeping everything hot is expensive; Usenet
retention is cheap and long. Cairn trades an instant hot copy for a durable cold one.
* **Survives eviction.** An archived title comes back even if it's since dropped out of the
live cache and the original torrent is dead.
## How it works
```
POST /api/cairn archive an item
│
torrin posts the cached blobs to Usenet, stores the NZB
│
(later) the hot copy is evicted
│
POST /api/cairn/{hash}/restore re-hydrate from the stored NZB → back in the cache
```
The NZB (the index needed to pull it back) is kept durably and backed up, so a restore is a
Usenet re-download, not a re-leech of the original.
## The endpoints
| Call | Does |
| - | - |
| `GET /api/cairn` | List your archived items. |
| `POST /api/cairn` | Archive a cached item by info-hash (queued, `202`). |
| `POST /api/cairn/{hash}/restore` | Re-hydrate an archived item back into the cache. |
| `DELETE /api/cairn/{hash}` | Drop the archive. |
Archiving is queued and happens in the background — posting to Usenet takes minutes per title.
The item stays streamable from cache in the meantime.
# Archive an item
Source: https://docs.torrin.app/api-reference/cairn/archive-an-item
/api-reference/openapi.yaml post /api/cairn
Posts a cached item to Usenet so it survives cache eviction.
# List archives
Source: https://docs.torrin.app/api-reference/cairn/list-archives
/api-reference/openapi.yaml get /api/cairn
Items you've archived to torrin's permanent Usenet-backed store.
# Remove an archive
Source: https://docs.torrin.app/api-reference/cairn/remove-an-archive
/api-reference/openapi.yaml delete /api/cairn/{hash}
# Restore from archive
Source: https://docs.torrin.app/api-reference/cairn/restore-from-archive
/api-reference/openapi.yaml post /api/cairn/{hash}/restore
Re-hydrates an archived item back into the live cache from Usenet.
# How the API works
Source: https://docs.torrin.app/api-reference/concepts
Torrin's model — jobs, the shared cache, and signed streams — and how the two API dialects map onto it.
Under the RealDebrid-shaped responses, torrin is not a debrid reseller — it downloads
on its own box behind a VPN, caches the result, and hands you a signed HTTPS stream.
Understanding that model makes the endpoints obvious.
## The lifecycle
Every source you add — a magnet, a hash, a release link — becomes a **job**. A job moves
through a handful of states, and what you get back depends on where it is:
```
add magnet/hash
│
cached in torrin's shared store?
│
├── yes ──> job is "downloaded" immediately ──> links are ready
│
└── no ───> job downloads on the box (behind a VPN)
│ pending → downloading → uploading → downloaded
│
└── once downloaded ──> cached for everyone ──> links ready
```
* **Cached hits are instant.** The bytes are already on disk, so the job is created as
`downloaded` and its `links` are populated on the same call.
* **Misses download server-side.** You poll the torrent until its status is `downloaded`,
then unrestrict a link. No local torrent client, no seeding, no waiting on your own bandwidth.
* **The cache is shared.** Once anyone downloads a title, it's cached for every user — which
is why the [availability check](#instant-availability) matters before you add.
## Two dialects, one engine
Torrin exposes the *same* jobs through two API shapes. Pick the one your client speaks —
they sit on the same cache and the same downloads.
| | `/rest/1.0` | `/v0/store` |
| - | - | - |
| **Speaks** | RealDebrid API | StremThru store API |
| **Use it for** | Stremio RD addons, RD-compatible clients, direct integration | StremThru clients like Comet |
| **Auth header** | `Authorization: Bearer ` | `X-StremThru-Store-Authorization: Bearer ` |
| **Add a source** | `POST /torrents/addMagnet` (form) | `POST /magnets` (JSON) |
| **Availability** | `GET /torrents/instantAvailability/{hashes}` | `GET /magnets/check` |
| **Play a file** | `POST /unrestrict/link` | the file's `link` (already playable) |
If you're integrating from scratch, use `/rest/1.0` — it's the most widely supported shape
and every RD tutorial applies.
## Instant availability
Before adding a magnet, ask whether it's already cached. This is the cheapest call in the
API and it decides whether playback is instant or a download:
* `/rest/1.0/torrents/instantAvailability/{hashes}` returns a key for **every** hash you
asked about — a `rd` array when cached, or an empty `{}` when it isn't. Key *presence* is
not enough; check that the `rd` array is non-empty.
* `/v0/store/magnets/check` is richer. Each item is one of:
| Status | Meaning |
| - | - |
| `cached` | Streamable right now from torrin's shared store. `files` are populated. |
| `acceleratable` | Not in the shared store, but reachable via a connected provider (system AD/RD or your own key) and can be pulled quickly. |
| `unknown` | Not available. Adding it starts a fresh download. |
## Streaming
A torrent's `links` are `torrin://` references, not URLs. To play one, **unrestrict** it:
```bash theme={null}
curl -X POST https://api.torrin.app/rest/1.0/unrestrict/link \
-H "Authorization: Bearer YOUR_API_KEY" \
--data-urlencode "link=torrin://blobs/b_afb2a849"
```
You get back a `download` URL: a **signed HTTPS link valid for 24 hours**, served from the
nearest cache node with the origin IP hidden. It supports range requests, so players can
seek. (StremThru `files[].link` is already this signed URL — no unrestrict step.)
Torrin doesn't know a file's exact byte size until it's read, so `unrestrict` returns
`filesize: 0`. Take the real size from the torrent's file entry (`files[].bytes`) instead.
## A typical flow
Adding a magnet and playing the biggest file, end to end:
`GET /rest/1.0/torrents/instantAvailability/{hash}` — if the `rd` array is non-empty,
playback will be instant.
`POST /rest/1.0/torrents/addMagnet` with the magnet. You get back an `id`.
Poll `GET /rest/1.0/torrents/info/{id}` until `status` is `downloaded`. A cached add is
already `downloaded`.
Pick a `link` from the torrent, `POST /rest/1.0/unrestrict/link`, and open the returned
`download` URL.
## Job statuses
The RealDebrid `status` field is torrin's internal job state, mapped to RD's vocabulary:
| Torrin state | RD `status` | Meaning |
| - | - | - |
| pending | `magnet_conversion` | Accepted, resolving the magnet. |
| queued | `queued` | Waiting for a download slot. |
| downloading / processing | `downloading` | Fetching + unpacking on the box. |
| publishing | `uploading` | Writing the finished media to the cache. |
| complete / seeding | `downloaded` | Cached and ready — `links` are populated. |
| failed | `error` | The download failed. |
Your plan's concurrency limit applies to jobs that are actively downloading; cached adds
don't consume a slot. Hitting the limit returns `503` (RD) / `429` (store) with a
`slot limit reached` message.
# Debrid usage
Source: https://docs.torrin.app/api-reference/debrid-keys/debrid-usage
/api-reference/openapi.yaml get /api/debrid/usage
Reports which providers are configured and how they've been used.
# Get AllDebrid key status
Source: https://docs.torrin.app/api-reference/debrid-keys/get-alldebrid-key-status
/api-reference/openapi.yaml get /api/alldebrid/credentials
# Get Premiumize key status
Source: https://docs.torrin.app/api-reference/debrid-keys/get-premiumize-key-status
/api-reference/openapi.yaml get /api/premiumize/credentials
# Get RealDebrid key status
Source: https://docs.torrin.app/api-reference/debrid-keys/get-realdebrid-key-status
/api-reference/openapi.yaml get /api/rd/credentials
# Get TorBox key status
Source: https://docs.torrin.app/api-reference/debrid-keys/get-torbox-key-status
/api-reference/openapi.yaml get /api/torbox/credentials
# Remove AllDebrid key
Source: https://docs.torrin.app/api-reference/debrid-keys/remove-alldebrid-key
/api-reference/openapi.yaml delete /api/alldebrid/credentials
# Remove Premiumize key
Source: https://docs.torrin.app/api-reference/debrid-keys/remove-premiumize-key
/api-reference/openapi.yaml delete /api/premiumize/credentials
# Remove RealDebrid key
Source: https://docs.torrin.app/api-reference/debrid-keys/remove-realdebrid-key
/api-reference/openapi.yaml delete /api/rd/credentials
# Remove TorBox key
Source: https://docs.torrin.app/api-reference/debrid-keys/remove-torbox-key
/api-reference/openapi.yaml delete /api/torbox/credentials
# Set AllDebrid key
Source: https://docs.torrin.app/api-reference/debrid-keys/set-alldebrid-key
/api-reference/openapi.yaml post /api/alldebrid/credentials
# Set Premiumize key
Source: https://docs.torrin.app/api-reference/debrid-keys/set-premiumize-key
/api-reference/openapi.yaml post /api/premiumize/credentials
# Set RealDebrid key
Source: https://docs.torrin.app/api-reference/debrid-keys/set-realdebrid-key
/api-reference/openapi.yaml post /api/rd/credentials
# Set TorBox key
Source: https://docs.torrin.app/api-reference/debrid-keys/set-torbox-key
/api-reference/openapi.yaml post /api/torbox/credentials
# qBittorrent add torrent
Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-add-torrent
/api-reference/openapi.yaml post /api/v2/torrents/add
Adds a magnet (urls field) or an uploaded .torrent (torrents field), optionally under a category. Torrin ingests it through its normal pipeline.
# qBittorrent delete torrent
Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-delete-torrent
/api-reference/openapi.yaml post /api/v2/torrents/delete
# qBittorrent list torrents
Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-list-torrents
/api-reference/openapi.yaml get /api/v2/torrents/info
Returns your jobs in qBittorrent's torrent-list shape so the *arr app can track progress and detect completion.
# qBittorrent login
Source: https://docs.torrin.app/api-reference/download-client/qbittorrent-login
/api-reference/openapi.yaml post /api/v2/auth/login
qBittorrent WebUI auth. Send your Torrin API key as the username or password. Returns the text "Ok." and a session cookie, matching qBittorrent so Sonarr/Radarr connect unchanged. See the Download client guide.
# SABnzbd API
Source: https://docs.torrin.app/api-reference/download-client/sabnzbd-api
/api-reference/openapi.yaml get /api
SABnzbd-compatible endpoint (also served at /sabnzbd/api). The mode parameter selects the function. Authenticate with apikey set to your Torrin API key.
# SABnzbd API (file upload)
Source: https://docs.torrin.app/api-reference/download-client/sabnzbd-api-file-upload
/api-reference/openapi.yaml post /api
SABnzbd addfile. POST an .nzb as multipart field "name" with mode=addfile.
# API Overview
Source: https://docs.torrin.app/api-reference/introduction
Torrin's HTTP API — RealDebrid-compatible, StremThru store, and public endpoints.
Torrin speaks the **RealDebrid API**. Most clients and Stremio addons that already
support RealDebrid work by pointing their base URL at your instance and using your
torrin API key. On top of that, torrin exposes a **StremThru store** API for clients
like Comet, and a few **public** endpoints for signup and instance metadata.
## Base URL
All requests go to your instance:
```
https://api.torrin.app
```
If you self-host, use your own base URL instead. Every path below is relative to it.
## The three surfaces
`/rest/1.0/*` — user, torrents, and link unrestriction in RealDebrid's exact
response shapes. This is what Stremio addons and RD clients use.
`/v0/store/*` — the StremThru store contract used by clients such as Comet.
`/api/*` — create an account and read instance metadata. No auth required.
## A first request
Fetch the authenticated user with your API key:
```bash theme={null}
curl https://api.torrin.app/rest/1.0/user \
-H "Authorization: Bearer YOUR_API_KEY"
```
```json theme={null}
{
"id": 1779700568426389947,
"username": "you",
"email": "you@example.com",
"type": "premium",
"premium": 2419200,
"expiration": "2026-08-26T12:00:00Z"
}
```
`premium` is the number of seconds left on the subscription — this is how RD clients
detect an active account, so torrin returns it in the same shape.
## Conventions
* **Auth** is your API key as a bearer token. See [Authentication](/api-reference/authentication).
* **Times** are RFC 3339 (`2026-08-26T12:00:00Z`).
* **Errors** use the surface's native shape — RealDebrid's `{ error, error_code }` on
`/rest/1.0`, and StremThru's `{ error: { code, message } }` on `/v0/store`. Each endpoint
below lists the status codes it can return.
Use the endpoint pages in the sidebar to see every parameter, response schema, and a
runnable example you can try with your own key.
# Add a file-host link
Source: https://docs.torrin.app/api-reference/jobs/add-a-file-host-link
/api-reference/openapi.yaml post /api/jobs/hoster
Unrestrict a supported file-host URL. Requires a paid plan.
# Add a job
Source: https://docs.torrin.app/api-reference/jobs/add-a-job
/api-reference/openapi.yaml post /api/jobs
Add a magnet, info-hash, or a supported web/file-host URL. Cached content returns
`200` with a ready job (stream URLs populated); a new download returns `202`.
# Add an NZB
Source: https://docs.torrin.app/api-reference/jobs/add-an-nzb
/api-reference/openapi.yaml post /api/jobs/nzb
Upload an `.nzb` file body to download from Usenet.
# Add anything (auto-detected)
Source: https://docs.torrin.app/api-reference/jobs/add-anything-auto-detected
/api-reference/openapi.yaml post /api/add
One endpoint that detects what you send and routes it automatically, so nothing
has to pick a type. Send a magnet URI, a 40-char info-hash, or a supported
web/file-host URL as the raw request body, or upload a `.torrent` or `.nzb` file
as `multipart/form-data`. Cached content returns `200` with a ready job (stream
URLs populated); a new download returns `202`.
# Delete a job
Source: https://docs.torrin.app/api-reference/jobs/delete-a-job
/api-reference/openapi.yaml delete /api/jobs/{id}
# Download all files as a zip
Source: https://docs.torrin.app/api-reference/jobs/download-all-files-as-a-zip
/api-reference/openapi.yaml get /api/jobs/{id}/zip
# Get a job
Source: https://docs.torrin.app/api-reference/jobs/get-a-job
/api-reference/openapi.yaml get /api/jobs/{id}
# Grab a scene-rls release
Source: https://docs.torrin.app/api-reference/jobs/grab-a-scene-rls-release
/api-reference/openapi.yaml post /api/scenerls/grab
Starts a download of a chosen scene-rls release. Paid plans only.
# Grab an HDEncode release
Source: https://docs.torrin.app/api-reference/jobs/grab-an-hdencode-release
/api-reference/openapi.yaml post /api/hdencode/grab
Starts a download of a chosen HDEncode release. Paid plans only.
# Import hashes from your debrid library
Source: https://docs.torrin.app/api-reference/jobs/import-hashes-from-your-debrid-library
/api-reference/openapi.yaml post /api/{provider}/import
Bulk-import provider hashes as Torrin download jobs. Paid plans only. Partial failures are reported in `errors`, not the status code.
# Job progress
Source: https://docs.torrin.app/api-reference/jobs/job-progress
/api-reference/openapi.yaml get /api/jobs/{id}/progress
Lightweight status/progress for polling an active download.
# List jobs
Source: https://docs.torrin.app/api-reference/jobs/list-jobs
/api-reference/openapi.yaml get /api/jobs
# Recheck a job
Source: https://docs.torrin.app/api-reference/jobs/recheck-a-job
/api-reference/openapi.yaml post /api/jobs/{id}/recheck
Re-verifies a completed torrent or Usenet download.
# Retry a job
Source: https://docs.torrin.app/api-reference/jobs/retry-a-job
/api-reference/openapi.yaml post /api/jobs/{id}/retry
Requeues a failed or evicted job.
# Sign stream URLs
Source: https://docs.torrin.app/api-reference/jobs/sign-stream-urls
/api-reference/openapi.yaml post /api/jobs/{id}/sign
Returns fresh signed, geo-routed stream URLs for a completed job's files.
# Upload a .torrent file
Source: https://docs.torrin.app/api-reference/jobs/upload-a-torrent-file
/api-reference/openapi.yaml post /api/torrents/upload
Upload a .torrent (multipart field `torrent`, max 5 MiB) to start a download or a private-tracker seed.
# Create an account
Source: https://docs.torrin.app/api-reference/public/create-an-account
/api-reference/openapi.yaml post /api/auth/register
# List plans
Source: https://docs.torrin.app/api-reference/public/list-plans
/api-reference/openapi.yaml get /api/plans
# List supported file hosters
Source: https://docs.torrin.app/api-reference/public/list-supported-file-hosters
/api-reference/openapi.yaml get /api/hosters
# List supported link sites
Source: https://docs.torrin.app/api-reference/public/list-supported-link-sites
/api-reference/openapi.yaml get /api/sites
# Platform-wide wrapped stats
Source: https://docs.torrin.app/api-reference/public/platform-wide-wrapped-stats
/api-reference/openapi.yaml get /api/wrapped/platform
# Public instance configuration
Source: https://docs.torrin.app/api-reference/public/public-instance-configuration
/api-reference/openapi.yaml get /api/config
Feature flags and settings the web app reads before login.
# Public instance stats
Source: https://docs.torrin.app/api-reference/public/public-instance-stats
/api-reference/openapi.yaml get /api/stats/public
# Add a magnet
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/add-a-magnet
/api-reference/openapi.yaml post /rest/1.0/torrents/addMagnet
# Delete a torrent
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/delete-a-torrent
/api-reference/openapi.yaml delete /rest/1.0/torrents/delete/{id}
# Get the current user
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/get-the-current-user
/api-reference/openapi.yaml get /rest/1.0/user
# Get torrent info
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/get-torrent-info
/api-reference/openapi.yaml get /rest/1.0/torrents/info/{id}
# Instant availability
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/instant-availability
/api-reference/openapi.yaml get /rest/1.0/torrents/instantAvailability/{hashes}
Returns a key for every hash — a `rd` array when cached, `{}` when not.
# List torrents
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/list-torrents
/api-reference/openapi.yaml get /rest/1.0/torrents
# Select files
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/select-files
/api-reference/openapi.yaml post /rest/1.0/torrents/selectFiles/{id}
Compatibility no-op — torrin auto-selects video files server-side.
# Unrestrict a link
Source: https://docs.torrin.app/api-reference/realdebrid-compatible/unrestrict-a-link
/api-reference/openapi.yaml post /rest/1.0/unrestrict/link
Turns a `torrin://` file reference into a signed HTTPS URL valid 24h.
# Reseller / Partner API
Source: https://docs.torrin.app/api-reference/reseller
Provision and activate Torrin accounts from your own backend.
The reseller API lets a partner create Torrin accounts and activate plans
entirely from their own backend — no customer ever visits Torrin or pastes a
key. It's how white-label apps bundle Torrin into their own product and billing.
## Getting access
Reseller access is issued per partner — contact the operator to get your
**partner key**. There's no self-serve signup. Your single key works for both:
* **`X-Partner-Key`** — create accounts (skips captcha and per-IP signup limits).
* **`X-Reseller-Key`** — mint, list, and settle plan codes.
Your key is **scoped to your own codes** — you only ever see and settle codes
you minted.
## Provisioning flow
Three calls create a customer and put them on a plan:
### 1. Create the account
```bash theme={null}
curl -X POST https://api.torrin.app/api/auth/register \
-H "X-Partner-Key: YOUR_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"customer@example.com"}'
```
Returns the customer's API key:
```json theme={null}
{ "api_key": "YOUR_CUSTOMER_API_KEY", "email": "customer@example.com", "plan": "free" }
```
### 2. Mint a plan code
```bash theme={null}
curl -X POST https://api.torrin.app/api/reseller/codes \
-H "X-Reseller-Key: YOUR_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{"plan_id":"standard","period":"monthly","quantity":1}'
```
```json theme={null}
{ "codes": ["TRN-XXXX-XXXX-XXXX"], "plan_id": "standard", "period": "monthly" }
```
`plan_id` is `standard` or `pro` — both include system Usenet + indexer with
nothing extra to buy (`starter` does not bundle Usenet). `period` is `monthly`,
`yearly`, `lifetime`, or `days` (see [Trials](#trials)).
### 3. Activate it on the account
```bash theme={null}
curl -X POST https://api.torrin.app/api/redeem \
-H "Authorization: Bearer YOUR_CUSTOMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code":"TRN-XXXX-XXXX-XXXX"}'
```
```json theme={null}
{ "plan_id": "standard", "expires_at": "2026-11-15T00:00:00Z" }
```
The customer is now on Standard and ready to stream.
## Trials
Mint a short **`days`** code for a trial that auto-expires, then upgrade the same
account after payment:
```bash theme={null}
# 24-hour Standard trial
curl -X POST https://api.torrin.app/api/reseller/codes \
-H "X-Reseller-Key: YOUR_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{"plan_id":"standard","period":"days","days":1,"quantity":1}'
```
Redeem it exactly as in step 3. After the window expires, redeem a `monthly`
code on the **same account** to continue — the new term starts from redemption.
## Billing
* **Codes never expire** until redeemed — prepay a batch and use it over time.
* **The plan term starts only when a code is redeemed**, not when it's minted.
* **Settlement** reconciles the codes you've activated so you can be invoiced.
```bash theme={null}
# list your codes (scoped to you)
curl "https://api.torrin.app/api/reseller/codes?redeemed=true" \
-H "X-Reseller-Key: YOUR_PARTNER_KEY"
# mark your redeemed codes settled
curl -X POST https://api.torrin.app/api/reseller/settle \
-H "X-Reseller-Key: YOUR_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{"ref":"invoice-2026-10"}'
```
## Reconciliation
Calls are safe to retry after an uncertain response:
* **Redeem** is idempotent — redeeming an already-used code returns
`409 this code has already been used`, confirming the first attempt worked.
* **List your codes** (`GET /api/reseller/codes`) to see exactly what was minted
or redeemed.
## Included service & limits
| | Standard | Pro |
| - | - | - |
| System Usenet + indexer | Yes | Yes |
| Simultaneous streams | Unlimited | Unlimited |
| Device / IP limits | None | None |
| Monthly uncached downloads | 4 TB | 8 TB |
## White-label
Partner-created accounts can have the Torrin welcome email **suppressed** so the
experience stays fully your brand. Ask the operator to enable it on your key.
# Add a feed
Source: https://docs.torrin.app/api-reference/rss/add-a-feed
/api-reference/openapi.yaml post /api/rss/feeds
Adds an RSS feed; matching items are auto-added as jobs.
# Delete a feed
Source: https://docs.torrin.app/api-reference/rss/delete-a-feed
/api-reference/openapi.yaml delete /api/rss/feeds/{id}
# Enable or disable a feed
Source: https://docs.torrin.app/api-reference/rss/enable-or-disable-a-feed
/api-reference/openapi.yaml post /api/rss/feeds/{id}/toggle
# List feed tags
Source: https://docs.torrin.app/api-reference/rss/list-feed-tags
/api-reference/openapi.yaml get /api/rss/tags
# List feeds
Source: https://docs.torrin.app/api-reference/rss/list-feeds
/api-reference/openapi.yaml get /api/rss/feeds
# Update a feed
Source: https://docs.torrin.app/api-reference/rss/update-a-feed
/api-reference/openapi.yaml put /api/rss/feeds/{id}
# Browse your debrid library
Source: https://docs.torrin.app/api-reference/search-&-availability/browse-your-debrid-library
/api-reference/openapi.yaml get /api/{provider}/library
Paginated view of your existing library on a debrid provider (20 per page). Paid plans only.
# Check many hashes
Source: https://docs.torrin.app/api-reference/search-&-availability/check-many-hashes
/api-reference/openapi.yaml post /api/availability
# Check one hash
Source: https://docs.torrin.app/api-reference/search-&-availability/check-one-hash
/api-reference/openapi.yaml get /api/availability/{hash}
Whether a single info-hash is cached in torrin's shared store.
# Grab a Usenet result
Source: https://docs.torrin.app/api-reference/search-&-availability/grab-a-usenet-result
/api-reference/openapi.yaml post /api/usenet/grab
# Newznab indexer
Source: https://docs.torrin.app/api-reference/search-&-availability/newznab-indexer
/api-reference/openapi.yaml get /newznab/api
Exposes Torrin's Usenet search as a Newznab indexer for Prowlarr, NZBHydra2, Jackett, and the *arr apps. Add as a "Generic Newznab" indexer with base URL `/newznab` and your Torrin API key. Returns a Newznab RSS/XML feed (or caps XML).
# Newznab NZB download
Source: https://docs.torrin.app/api-reference/search-&-availability/newznab-nzb-download
/api-reference/openapi.yaml get /newznab/getnzb
Resolves a search result to its .nzb file (this is the download link inside the feed).
# Queue a metadata scrape
Source: https://docs.torrin.app/api-reference/search-&-availability/queue-a-metadata-scrape
/api-reference/openapi.yaml post /api/scrape
Asks torrin to fetch metadata for the given hashes in the background.
# Search HDEncode
Source: https://docs.torrin.app/api-reference/search-&-availability/search-hdencode
/api-reference/openapi.yaml get /api/hdencode/search
# Search scene-rls
Source: https://docs.torrin.app/api-reference/search-&-availability/search-scene-rls
/api-reference/openapi.yaml get /api/scenerls/search
# Search the cache
Source: https://docs.torrin.app/api-reference/search-&-availability/search-the-cache
/api-reference/openapi.yaml get /api/search
The public search API. Give an IMDB id (or a title) and get back everything torrin has
cached for it, ready to stream — build addons, tools, whatever you want. One person's
grab is cached for everyone. Requires an active monthly, yearly, or lifetime plan.
# Search Usenet
Source: https://docs.torrin.app/api-reference/search-&-availability/search-usenet
/api-reference/openapi.yaml get /api/usenet/search
Searches the configured Usenet indexer. Requires an indexer to be set.
# Torznab indexer
Source: https://docs.torrin.app/api-reference/search-&-availability/torznab-indexer
/api-reference/openapi.yaml get /torznab/api
Exposes Torrin's torrent search as a Torznab indexer for Prowlarr, Jackett, and the *arr apps. Add as a "Generic Torznab" indexer with base URL `/torznab` and your Torrin API key. Returns a Torznab RSS/XML feed of magnets (or caps XML). Cached releases are tagged and ranked first. Requires any paid plan; the operator must set COMET_URL to enable it.
# Configure storage (S3-style)
Source: https://docs.torrin.app/api-reference/storage/configure-storage-s3-style
/api-reference/openapi.yaml post /api/storage
Set the primary BYOS credentials directly (S3-compatible bucket, or an rclone backend). For pasting a full rclone config block, use `/api/storage/connect` instead.
# Connect storage
Source: https://docs.torrin.app/api-reference/storage/connect-storage
/api-reference/openapi.yaml post /api/storage/connect
Connect a bring-your-own-storage backend (S3-compatible or an rclone remote). Content
you stream is written to your storage, encrypted, and served back through torrin.
# Disconnect storage
Source: https://docs.torrin.app/api-reference/storage/disconnect-storage
/api-reference/openapi.yaml delete /api/storage
# Export rclone config
Source: https://docs.torrin.app/api-reference/storage/export-rclone-config
/api-reference/openapi.yaml get /api/storage/rclone-config
Download an rclone.conf for your storage so you can mount it yourself. Holds your
access keys and encryption passphrase, so keep it private.
# Get storage status
Source: https://docs.torrin.app/api-reference/storage/get-storage-status
/api-reference/openapi.yaml get /api/storage
# Remove content from your storage
Source: https://docs.torrin.app/api-reference/storage/remove-content-from-your-storage
/api-reference/openapi.yaml post /api/storage/evict
Remove one release from your storage. It stays in your library and is served from the
shared cache if still available.
# Remove pool provider
Source: https://docs.torrin.app/api-reference/storage/remove-pool-provider
/api-reference/openapi.yaml delete /api/storage/providers/{idx}
Remove one provider from the pool by its index (0-based over the added providers).
# Set storage cleanup rules
Source: https://docs.torrin.app/api-reference/storage/set-storage-cleanup-rules
/api-reference/openapi.yaml put /api/storage/evict-policy
Auto-clean your own storage by age or size. 0 disables a rule.
# Set storage pool
Source: https://docs.torrin.app/api-reference/storage/set-storage-pool
/api-reference/openapi.yaml put /api/storage/providers
Add providers to the storage pool and set placement. Submitted providers are merged
into the existing pool. `union_policy` is `epmfs` (spread by free space) or `all` (mirror).
# Start storage OAuth
Source: https://docs.torrin.app/api-reference/storage/start-storage-oauth
/api-reference/openapi.yaml get /api/storage/oauth/{provider}/start
Begins the OAuth flow for a provider that needs it (e.g. Google Drive).
# Add a magnet
Source: https://docs.torrin.app/api-reference/stremthru-store/add-a-magnet
/api-reference/openapi.yaml post /v0/store/magnets
# Add an NZB
Source: https://docs.torrin.app/api-reference/stremthru-store/add-an-nzb
/api-reference/openapi.yaml post /v0/store/newz
Upload an .nzb file (multipart field "file") or pass JSON with a "link" to fetch.
# Check cached NZBs
Source: https://docs.torrin.app/api-reference/stremthru-store/check-cached-nzbs
/api-reference/openapi.yaml get /v0/store/newz/check
StremThru usenet store. torz aliases (/v0/store/torz/*) mirror the magnet store.
# Check magnets
Source: https://docs.torrin.app/api-reference/stremthru-store/check-magnets
/api-reference/openapi.yaml get /v0/store/magnets/check
Per-magnet status: cached, acceleratable, or unknown.
# Delete a magnet
Source: https://docs.torrin.app/api-reference/stremthru-store/delete-a-magnet
/api-reference/openapi.yaml delete /v0/store/magnets/{id}
# Generate a download link for an NZB store item
Source: https://docs.torrin.app/api-reference/stremthru-store/generate-a-download-link-for-an-nzb-store-item
/api-reference/openapi.yaml post /v0/store/newz/link/generate
# Generate link
Source: https://docs.torrin.app/api-reference/stremthru-store/generate-link
/api-reference/openapi.yaml post /v0/store/link/generate
# Get a magnet
Source: https://docs.torrin.app/api-reference/stremthru-store/get-a-magnet
/api-reference/openapi.yaml get /v0/store/magnets/{id}
# Get an NZB store item
Source: https://docs.torrin.app/api-reference/stremthru-store/get-an-nzb-store-item
/api-reference/openapi.yaml get /v0/store/newz/{id}
# Get store user
Source: https://docs.torrin.app/api-reference/stremthru-store/get-store-user
/api-reference/openapi.yaml get /v0/store/user
# List magnets
Source: https://docs.torrin.app/api-reference/stremthru-store/list-magnets
/api-reference/openapi.yaml get /v0/store/magnets
# List your NZB store items
Source: https://docs.torrin.app/api-reference/stremthru-store/list-your-nzb-store-items
/api-reference/openapi.yaml get /v0/store/newz
# Remove an NZB store item
Source: https://docs.torrin.app/api-reference/stremthru-store/remove-an-nzb-store-item
/api-reference/openapi.yaml delete /v0/store/newz/{id}
# Generate a Telegram link code
Source: https://docs.torrin.app/api-reference/telegram/generate-a-telegram-link-code
/api-reference/openapi.yaml post /api/telegram/link-code
Returns a short-lived code (15 min) to link the account from the Telegram bot.
# Telegram link status
Source: https://docs.torrin.app/api-reference/telegram/telegram-link-status
/api-reference/openapi.yaml get /api/telegram/status
# Unlink Telegram
Source: https://docs.torrin.app/api-reference/telegram/unlink-telegram
/api-reference/openapi.yaml delete /api/telegram/link
# Usenet
Source: https://docs.torrin.app/api-reference/usenet
Add a Usenet provider and indexer, search, and grab.
Torrin can pull from Usenet as well as torrents. You attach a **provider** (the NNTP server
that serves the bytes) and an **indexer** (the Newznab-compatible search that finds releases),
then search and grab — the result becomes a normal job and streams like anything else.
## Two pieces
| Piece | What it is | Endpoint |
| - | - | - |
| **Provider** | Your Usenet server (host, port, login, connection count). Serves the actual data. | `/api/usenet/credentials` |
| **Indexer** | A Newznab-compatible indexer (URL + API key). Finds releases to grab. | `/api/usenet/indexer` |
Each has `GET` (status), `POST` (set), `DELETE` (remove), and a test call
(`POST /api/usenet/test`, `POST /api/usenet/indexer/test`) to verify credentials before saving.
## Search and grab
Once both are set:
```bash theme={null}
# search the indexer
curl "https://api.torrin.app/api/usenet/search?imdb=tt1234567" \
-H "Authorization: Bearer YOUR_API_KEY"
# grab a result → it becomes a job
curl -X POST https://api.torrin.app/api/usenet/grab \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "RESULT_ID", "title": "..." }'
```
You can also skip the indexer and upload an `.nzb` file directly to
[`POST /api/jobs/nzb`](/api-reference/jobs/add-an-nzb).
A grab returns a `202` with a job — poll `GET /api/jobs/{id}` until it's `complete`, then
stream it exactly like a torrent.
# Add a Usenet indexer
Source: https://docs.torrin.app/api-reference/usenet/add-a-usenet-indexer
/api-reference/openapi.yaml post /api/usenet/indexers
Add a Newznab indexer (paid plans only). Up to 8 per account.
# Add a Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/add-a-usenet-provider
/api-reference/openapi.yaml post /api/usenet/providers
# Built-in indexer state
Source: https://docs.torrin.app/api-reference/usenet/built-in-indexer-state
/api-reference/openapi.yaml get /api/usenet/system-indexer
The built-in Torrin indexer's availability (needs Standard/Pro) and on/off state.
# Check cached Usenet releases
Source: https://docs.torrin.app/api-reference/usenet/check-cached-usenet-releases
/api-reference/openapi.yaml get /api/usenet/cached
Returns already-cached, playable files for a title.
# Delete a Usenet indexer
Source: https://docs.torrin.app/api-reference/usenet/delete-a-usenet-indexer
/api-reference/openapi.yaml delete /api/usenet/indexers/{id}
# Delete a Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/delete-a-usenet-provider
/api-reference/openapi.yaml delete /api/usenet/providers/{id}
# Edit a Usenet indexer
Source: https://docs.torrin.app/api-reference/usenet/edit-a-usenet-indexer
/api-reference/openapi.yaml put /api/usenet/indexers/{id}
# Enable or disable a Usenet indexer
Source: https://docs.torrin.app/api-reference/usenet/enable-or-disable-a-usenet-indexer
/api-reference/openapi.yaml post /api/usenet/indexers/{id}/toggle
# Enable or disable a Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/enable-or-disable-a-usenet-provider
/api-reference/openapi.yaml post /api/usenet/providers/{id}/toggle
# Get indexer status
Source: https://docs.torrin.app/api-reference/usenet/get-indexer-status
/api-reference/openapi.yaml get /api/usenet/indexer
# Get Usenet provider status
Source: https://docs.torrin.app/api-reference/usenet/get-usenet-provider-status
/api-reference/openapi.yaml get /api/usenet/credentials
# List your Usenet indexers
Source: https://docs.torrin.app/api-reference/usenet/list-your-usenet-indexers
/api-reference/openapi.yaml get /api/usenet/indexers
# List your Usenet providers
Source: https://docs.torrin.app/api-reference/usenet/list-your-usenet-providers
/api-reference/openapi.yaml get /api/usenet/providers
# Remove indexer
Source: https://docs.torrin.app/api-reference/usenet/remove-indexer
/api-reference/openapi.yaml delete /api/usenet/indexer
# Remove Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/remove-usenet-provider
/api-reference/openapi.yaml delete /api/usenet/credentials
# Set indexer
Source: https://docs.torrin.app/api-reference/usenet/set-indexer
/api-reference/openapi.yaml post /api/usenet/indexer
# Set Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/set-usenet-provider
/api-reference/openapi.yaml post /api/usenet/credentials
# Test a Usenet indexer
Source: https://docs.torrin.app/api-reference/usenet/test-a-usenet-indexer
/api-reference/openapi.yaml post /api/usenet/indexers/test
Validates and live-tests an indexer config before saving.
# Test an indexer
Source: https://docs.torrin.app/api-reference/usenet/test-an-indexer
/api-reference/openapi.yaml post /api/usenet/indexer/test
# Test the Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/test-the-usenet-provider
/api-reference/openapi.yaml post /api/usenet/test
# Test Usenet provider credentials
Source: https://docs.torrin.app/api-reference/usenet/test-usenet-provider-credentials
/api-reference/openapi.yaml post /api/usenet/providers/test
# Toggle the built-in indexer
Source: https://docs.torrin.app/api-reference/usenet/toggle-the-built-in-indexer
/api-reference/openapi.yaml post /api/usenet/system-indexer/toggle
# Update a Usenet provider
Source: https://docs.torrin.app/api-reference/usenet/update-a-usenet-provider
/api-reference/openapi.yaml put /api/usenet/providers/{id}
# Buy a plan from wallet balance
Source: https://docs.torrin.app/api-reference/wallet/buy-a-plan-from-wallet-balance
/api-reference/openapi.yaml post /api/wallet/buy-plan
# Get wallet balance
Source: https://docs.torrin.app/api-reference/wallet/get-wallet-balance
/api-reference/openapi.yaml get /api/wallet
# Top up the wallet
Source: https://docs.torrin.app/api-reference/wallet/top-up-the-wallet
/api-reference/openapi.yaml post /api/wallet/topup
Starts a checkout to add credit to your balance.
# List WebDAV naming overrides
Source: https://docs.torrin.app/api-reference/webdav/list-webdav-naming-overrides
/api-reference/openapi.yaml get /api/webdav/overrides
# Set a WebDAV naming override
Source: https://docs.torrin.app/api-reference/webdav/set-a-webdav-naming-override
/api-reference/openapi.yaml post /api/webdav/override
Changes how a release's folder is named in the WebDAV library.
# Accounts & plans
Source: https://docs.torrin.app/self-hosting/accounts
How users sign up and how plan limits work.
## The first account
Registration is open by default — open the web UI at your `PUBLIC_URL` and sign
up. The first account is a normal user; there's no separate admin bootstrap.
Each account adds its **own debrid key** (RealDebrid / AllDebrid / TorBox) in
settings — that's what makes cached content stream instantly.
## Plans
Every user is on a **plan** that sets their limits. New accounts land on the
built-in **Free** plan:
| Limit | Free |
| - | - |
| Concurrent downloads | 1 |
| Max torrent size | 50 GB |
| WebDAV / Stremio / shared cache | yes |
Plans are defined in code (`shared/plans/plans.go`). For a personal instance the
Free limits are often too tight, so you have two options:
Assign your user a higher plan directly in the database:
```sql theme={null}
UPDATE users SET plan_id='pro', expires_at='2099-01-01' WHERE email='you@example.com';
```
(Run inside the postgres container: `docker compose exec postgres psql -U torrin -d torrin`.)
Change the limits on the Free plan (or add your own) in
`shared/plans/plans.go` and rebuild the images. Best if you want everyone on
your instance to get the same limits.
Billing (Gumroad / Bitcart / etc.) is only relevant if you're running Torrin as
a paid service. A private self-host can ignore it entirely and manage plans by
hand.
# Bring Your Own Storage
Source: https://docs.torrin.app/self-hosting/byos
Pipe content to your own cloud provider via rclone.
BYOS lets a user send their content to **their own cloud storage** instead of
(or alongside) the shared cache. It's built on [rclone](https://rclone.org), and
everything is **encrypted** before it leaves the server (rclone `crypt`), so the
provider only ever sees scrambled files.
## Supported providers
Google Drive, Dropbox, OneDrive, and pCloud via OAuth; Mega, PikPak, and Gofile
via account login; any S3-compatible bucket (S3, Backblaze B2, Wasabi, R2) via
keys; and anything else rclone supports by pasting an rclone remote config.
## Enabling OAuth providers
For the OAuth cloud providers, register an app with each provider and set its
client ID/secret, plus the api's public base so the OAuth redirect works:
| Variable | Description |
| - | - |
| `API_PUBLIC_URL` | The api's externally-reachable base — the OAuth redirect target. Must be reachable by the provider. |
| `GDRIVE_CLIENT_ID` / `GDRIVE_CLIENT_SECRET` | Google Drive app. |
| `DROPBOX_CLIENT_ID` / `DROPBOX_CLIENT_SECRET` | Dropbox app. |
| `ONEDRIVE_CLIENT_ID` / `ONEDRIVE_CLIENT_SECRET` | OneDrive app. |
| `PCLOUD_CLIENT_ID` / `PCLOUD_CLIENT_SECRET` | pCloud app. |
An S3-compatible bucket needs no app registration — the user just enters the
endpoint, bucket, and keys in settings.
## Using it
Once a provider is configured, a user connects their storage in the app's
settings. New downloads are encrypted and written to their provider, and
playback streams back from it, geo-routed and decrypted on the fly.
## Pooling multiple providers
Several providers can act as one encrypted pool. Add extra providers in
settings, then choose how files are placed:
* **Pool** (default): each file goes to whichever provider has the most free space, so usable space is the sum of all providers.
* **Mirror**: every file is written to all providers for redundancy (uses Nx the space).
## Exporting your config
Since everything is encrypted with your own key, you can download a ready-made
`rclone.conf` from settings and mount your storage yourself, independent of the
server. Keep it private: it holds your keys and encryption passphrase.
## Cleanup
Content on your storage stays until you remove it. You can remove a single
release from the app, or set optional rules to auto-clean by age (remove after N
idle days) or size (keep under N GB). The sweep runs daily; see `BYOS_EVICT_HOUR`
in [Configuration](/self-hosting/configuration).
Content on your own storage is **not** subject to the shared-cache eviction; it
stays until you remove it. See [Storage](/self-hosting/storage).
# Configuration
Source: https://docs.torrin.app/self-hosting/configuration
Environment variables for a self-hosted instance.
All configuration lives in `deploy/.env` (copied from `deploy/.env.example`).
Anything you leave blank falls back to the default baked into the compose files.
## Required
| Variable | Description |
| - | - |
| `SIGNING_KEY` | Secret that signs stream URLs and internal calls. Generate with `openssl rand -hex 32`. **Change from the default.** |
| `PUBLIC_URL` | Externally-reachable base for the stream service (your domain or IP). Signed URLs are minted against it. |
`DATABASE_URL` and `NATS_URL` are wired to the bundled PostgreSQL and NATS by
the compose file, so you don't set them yourself.
## Storage
| Variable | Description |
| - | - |
| `STORAGE_KEY` | Optional hex key to encrypt stored **content** at rest (default off). |
| `CREDS_KEY` | Optional hex key to encrypt stored **debrid credentials** at rest. |
The cache location is a volume mount, not an env var. See [Storage](/self-hosting/storage).
## Cache limits
| Variable | Default | Description |
| - | - | - |
| `BUDGET_MAX_BYTES` | `1000000000000` | Cap on in-flight (downloading) bytes. |
| `EVICTION_CAP_BYTES` | `300000000000` | Soft cap on the cache; oldest content is evicted past it. |
| `EVICTION_HOUR` | `4` | Hour of day the eviction sweep runs. |
| `BYOS_EVICT_HOUR` | `5` | Hour of day the [BYOS](/self-hosting/byos) cleanup sweep runs (only for users who set cleanup rules). |
## Debrid (optional)
Users add their own provider keys in the app. These system keys are only used
for the instant "is it cached" probe and can be left empty.
| Variable | Description |
| - | - |
| `RD_API_KEY` | System RealDebrid key for cache checks. |
| `AD_API_KEY` | System AllDebrid key for cache checks. |
| `TORRENTCLAW_API_KEY` | Non-jamming live cache-check service. |
## Usenet (optional)
| Variable | Description |
| - | - |
| `USENET_HOST` / `USENET_PORT` / `USENET_USER` / `USENET_PASS` | Your Usenet provider. |
| `USENET_MAXCONNS` | Max connections (default 20). |
| `USENET_INDEXER_URL` / `USENET_INDEXER_KEY` | A Newznab indexer, as a fallback source. |
| `INDEXER_USER_AGENT` | Default User-Agent for indexer searches (default `Prowlarr/1.30.2`). Users can override per BYOK indexer. |
| `INDEXER_GRAB_USER_AGENT` | Fallback User-Agent for NZB grabs (default `SABnzbd/5.0.4`). Some indexers block unknown agents. |
| `SONARR_USER_AGENT` / `RADARR_USER_AGENT` | Grab User-Agent by category when the indexer's grab UA is blank: TV uses Sonarr, movies use Radarr (defaults `Sonarr/4.0.15.2941` / `Radarr/5.26.2.10099`). |
## Torrent search (optional)
Enables the [Torznab indexer](/self-hosting/search-indexer) endpoint. Without
these, `/torznab` returns "not available".
| Variable | Description |
| - | - |
| `COMET_URL` | Base URL of the addon that backs torrent search (e.g. `https://addon.torrin.app`). |
| `COMET_SEARCH_KEY` | Shared secret the addon validates on search requests. Must match the addon's `TORZNAB_API_KEY`. |
## BYOS OAuth (optional)
Only needed if users will connect a cloud provider. See [BYOS](/self-hosting/byos).
| Variable | Description |
| - | - |
| `API_PUBLIC_URL` | The api's externally-reachable base (used as the OAuth redirect target). |
| `GDRIVE_CLIENT_ID` / `GDRIVE_CLIENT_SECRET` | Google Drive OAuth app. |
| `DROPBOX_CLIENT_ID` / `DROPBOX_CLIENT_SECRET` | Dropbox OAuth app. |
| `ONEDRIVE_CLIENT_ID` / `ONEDRIVE_CLIENT_SECRET` | OneDrive OAuth app. |
| `PCLOUD_CLIENT_ID` / `PCLOUD_CLIENT_SECRET` | pCloud OAuth app. |
## Rate limiting (optional)
| Variable | Default | Description |
| - | - | - |
| `RATE_LIMIT_RPS` | `30` | Per-key requests/sec; `0` disables. |
| `RATE_LIMIT_BURST` | `60` | Burst allowance. |
# Sonarr / Radarr download client
Source: https://docs.torrin.app/self-hosting/download-clients
Use Torrin as a drop-in download client in an *arr stack.
Torrin can present itself as a **qBittorrent** or **SABnzbd** download client, so
Sonarr, Radarr, and the rest of the \*arr stack can hand releases straight to
Torrin. Torrin does the downloading (debrid, Usenet, hosters), and the finished
files show up on a path the \*arr app imports from over [WebDAV](/self-hosting/webdav).
No bridging middleware needed: point the \*arr app at Torrin directly.
## How it fits together
Sonarr/Radarr talks to Torrin's fake qBittorrent or SABnzbd API and sends it
a magnet or NZB, exactly as it would to a real download client.
Torrin resolves and caches the release through its normal pipeline (debrid,
Usenet, hosters), subject to your plan limits.
Torrin reports the completed download at a folder path. The \*arr app reads
that path over a mounted WebDAV share and imports (hardlink/copy) the files.
## 1. Mount Torrin's WebDAV on the \*arr host
The \*arr app imports by **file path**, so it needs to *see* Torrin's finished
files locally. Mount Torrin's [WebDAV](/self-hosting/webdav) share on the same
host that runs Sonarr/Radarr, e.g. with rclone:
```bash theme={null}
rclone mount torrin: /mnt/torrin \
--dir-cache-time 10s --vfs-cache-mode off --allow-other
```
where the `torrin:` remote is a `webdav` remote pointing at
`http://:9092` with your Torrin account credentials.
The WebDAV folder names match the paths Torrin reports to the \*arr app, so the
import "just works" once the mount and the path mapping (step 3) line up.
## 2. Add Torrin as a download client
In **Sonarr/Radarr → Settings → Download Clients → Add**, pick one:
Choose **qBittorrent** and set:
| Field | Value |
| - | - |
| Host | your Torrin host, e.g. `api.torrin.app` (or your instance) |
| Port | `443` (or your port) |
| Use SSL | on, if your instance is HTTPS |
| Username | your Torrin **API key** |
| Password | your Torrin **API key** |
| Category | e.g. `tv-sonarr` / `radarr` |
Torrin serves the qBittorrent WebUI API at `/api/v2/*` on your main API
host, so leave **URL Base** empty. Username and password both accept your
API key (the password is used if both are set).
Choose **SABnzbd** and set:
| Field | Value |
| - | - |
| Host | your Torrin host, e.g. `api.torrin.app` |
| Port | `443` (or your port) |
| Use SSL | on, if your instance is HTTPS |
| API Key | your Torrin **API key** |
| URL Base | leave empty (or `/sabnzbd`) |
| Category | e.g. `tv` / `movies` |
Torrin answers the SABnzbd API at `/api` and `/sabnzbd/api`.
Hit **Test**; it should go green. Categories round-trip, so a Sonarr and a
Radarr instance pointed at the same Torrin stay separated.
## 3. Map the download path
The \*arr app sees the download at Torrin's reported save path (default
`/downloads`), but the files actually live on your **WebDAV mount**. Bridge the
two with a **Remote Path Mapping**:
**Settings → Download Clients → Remote Path Mappings → Add**
| Field | Value |
| - | - |
| Host | the same host you set on the download client |
| Remote Path | `/downloads/` |
| Local Path | `/mnt/torrin/` (your WebDAV mount) |
Torrin's reported save path is set by the `ARR_SAVE_PATH` env on the `api`
service (default `/downloads`). If you change it, change the Remote Path here to
match.
## That's it
Search in Sonarr/Radarr as usual. When it grabs a release, Torrin ingests it,
and once it's cached the \*arr app imports it straight from the WebDAV mount.
After that it streams from Torrin like anything else.
Grabs count against your plan's limits (concurrent jobs, monthly ingest, cold
pulls) just like any other Torrin download. A busy \*arr library can burn through
those quickly.
# Exposing publicly
Source: https://docs.torrin.app/self-hosting/exposing
Put Torrin behind HTTPS so you can use it remotely and with Stremio.
The quickstart runs Torrin on plain HTTP, which is fine on your LAN. To use it
from outside your network — and to use the **Stremio addon**, which requires
HTTPS — put it behind TLS. The `docker-compose.public.yml` overlay does this for
you, two ways.
## Access it locally first (no domain)
Before exposing anything, you can reach the dashboard on the machine itself. The
web UI runs in the `web` container on port `5000`, but that port isn't published
to your host by default. Publish it with a `docker-compose.override.yml` next to
your compose files:
```yaml theme={null}
services:
web:
ports:
- "127.0.0.1:5000:5000"
```
Re-run your `up -d` command, then open **[http://localhost:5000](http://localhost:5000)** to create your
account and add your debrid key. Drop the `127.0.0.1:` prefix to reach it from
other devices on your LAN.
Local access is HTTP only. For remote use and the Stremio addon (which requires
HTTPS), use one of the options below.
## Option A: Caddy with a domain (automatic TLS)
Point a domain at your server, then let the bundled Caddy fetch and renew a
Let's Encrypt certificate automatically.
Create an A/AAAA record for your domain (e.g. `stream.example.com`) pointing
at the server's public IP. Open ports **80** and **443**.
In `deploy/.env`:
```bash theme={null}
STREAM_DOMAIN=stream.example.com
ACME_EMAIL=you@example.com
PUBLIC_URL=https://stream.example.com
```
```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.vpn.yml \
-f docker-compose.public.yml up -d
```
Caddy reverse-proxies the public services (see `deploy/Caddyfile`) and manages
TLS. Adjust the Caddyfile if you want separate subdomains for the api, addon, or
WebDAV.
## Option B: Cloudflare Tunnel (no open ports)
If you can't open ports or prefer not to expose your IP, use a Cloudflare
Tunnel. Create a tunnel in the Cloudflare dashboard, then:
```bash theme={null}
CLOUDFLARE_TUNNEL_TOKEN=your-tunnel-token
```
and bring the stack up with the same `docker-compose.public.yml` overlay. Route
the tunnel's hostnames to the internal services in the Cloudflare dashboard.
`PUBLIC_URL` must match the HTTPS address you actually serve on — signed stream
URLs are minted against it, so a mismatch breaks playback.
# Introduction
Source: https://docs.torrin.app/self-hosting/introduction
What Torrin is and how it works.
Torrin is an open-source, self-hostable debrid + streaming service. You add a
magnet, NZB, or file-host link, and get back an instant HTTPS stream. No local
torrent client, no seeding, no IP exposure.
## What it does
Torrin downloads torrents, Usenet, and file-host links on the server (behind a
VPN), caches the result on disk, and serves it back as signed HTTPS stream URLs.
Cached content is shared across users, so repeat plays are instant.
* **Instant streaming** for cached content; downloads behind a VPN when it isn't.
* **Multiple sources** — torrents (qBittorrent), debrid providers, Usenet (NZB), file hosters, and scene/HDEncode releases.
* **Stremio addon**, **WebDAV** mount (VLC / Infuse / Kodi), and a web UI.
* **RSS auto-download** with criteria filters (resolution, source, codec, HDR, group, ...).
* **BYOS** — Bring Your Own Storage: pipe downloads to your own cloud via rclone.
## How it works
1. A user submits a magnet / NZB / link, or searches for a title.
2. `api` checks the cache and content providers. If available, it returns signed stream URLs instantly.
3. If not, a job is queued and `ingest` downloads it (behind a VPN).
4. On completion the files are published to the object store.
5. The user streams via a signed HTTPS URL from `stream`, or mounts the library via `webdav`.
## Architecture
Torrin is a Go monorepo of small services over [NATS](https://nats.io), backed
by PostgreSQL for metadata and a filesystem-backed object store for content.
| Service | Role |
| - | - |
| `api` | REST API — auth, jobs, RSS, storage |
| `ingest` | Download pipeline — torrent / debrid / Usenet / hoster → publish |
| `stream` | Signed HTTPS streaming of cached files |
| `webdav` | WebDAV server for mounting the library |
| `catalog` | Content catalog / metadata |
| `scheduler` | Background scheduling + cache eviction |
| `stremio` | Stremio addon server |
| `byos` | Bring Your Own Storage (rclone) |
Get a Torrin instance up with Docker Compose.
# Quick start
Source: https://docs.torrin.app/self-hosting/quickstart
Run a Torrin instance with Docker Compose.
## Prerequisites
* **Docker** and **Docker Compose**
* A **disk** with room for the content cache
* A **WireGuard VPN** provider for download egress (recommended, so your host IP is never exposed)
## 1. Clone
```bash theme={null}
git clone https://github.com/torrin-app/torrin
cd torrin/deploy
```
## 2. Configure
Copy the example env and fill in the required values:
```bash theme={null}
cp .env.example .env
```
At minimum, set:
Secret that signs stream URLs. Generate one with `openssl rand -hex 32`.
Where the stream service is reachable from the outside (your domain or IP), e.g. `https://stream.example.com`.
Your WireGuard details, if you use the VPN overlay (recommended). See [VPN](/self-hosting/vpn).
See [Configuration](/self-hosting/configuration) for the full list.
## 3. Storage
Cached content lives on the local filesystem. The compose mounts the host path
`/mnt/cache/store` into the services — mount your cache disk there, or change
that volume path in `docker-compose.yml`. See [Storage](/self-hosting/storage).
## 4. Start
Base stack plus the VPN overlay (downloads route through gluetun):
```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.vpn.yml up -d
```
Leave off `-f docker-compose.vpn.yml` to run without a VPN — downloads will use
your host IP directly, which is not recommended.
This brings up PostgreSQL, NATS, qBittorrent, the object store, and the Torrin
services (`api`, `ingest`, `stream`, `stremio`, ...).
## 5. Expose it (for remote access + Stremio)
The above runs on plain HTTP, fine for a LAN test. To reach it from outside your
network — and to use the Stremio addon, which requires HTTPS — add the public
overlay (Caddy with automatic TLS on your domain, or a Cloudflare tunnel):
```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.vpn.yml \
-f docker-compose.public.yml up -d
```
See [Exposing publicly](/self-hosting/exposing) for the DNS/domain and `PUBLIC_URL` details.
## 6. First use
1. Open the web UI at your `PUBLIC_URL` and create an account.
2. Add your debrid provider key (RealDebrid / AllDebrid / TorBox) in settings.
3. Paste a magnet, NZB, or link — or connect the [Stremio addon](/self-hosting/stremio).
Point the addon at your instance and stream from Stremio.
# Requirements
Source: https://docs.torrin.app/self-hosting/requirements
What you need before installing Torrin.
## Host
* A **Linux** server with **Docker** and **Docker Compose**.
* **\~2-4 GB RAM** for the base stack (more if you run heavy usenet/torrent loads).
* **Disk** sized for your cache: at least `EVICTION_CAP_BYTES` (default 300 GB)
plus headroom for the download scratch area. A separate data disk mounted at
`/mnt/cache/store` is recommended.
## Accounts & services
* A **debrid account** (RealDebrid, AllDebrid, or TorBox) — this is what makes
streaming instant. Users add their own key in the app.
* A **WireGuard VPN** provider for download egress (recommended). See [VPN](/self-hosting/vpn).
* A **domain** if you want to reach it from outside your LAN / use it with
Stremio. See [Exposing publicly](/self-hosting/exposing).
## Optional
* A **Usenet provider + indexer** for NZB downloads.
* **Cloud storage** (Google Drive / Dropbox / OneDrive / pCloud / S3) if you want
users to bring their own storage. See [BYOS](/self-hosting/byos).
Everything else — PostgreSQL, NATS, qBittorrent, the object store, and the
Torrin services — is bundled in the compose stack. You don't install them
separately.
# Torrin as a search indexer (Newznab + Torznab)
Source: https://docs.torrin.app/self-hosting/search-indexer
Use Torrin's built-in search as a Newznab (usenet) or Torznab (torrent) indexer in Prowlarr, NZBHydra2, Jackett, and the *arr stack.
Torrin can present its search as a **Newznab** (usenet) and a **Torznab**
(torrent) indexer, so Prowlarr, NZBHydra2, Jackett, AIOStreams, and the \*arr apps
can search Torrin the same way they'd search any indexer. It's the companion to
the [download client](/self-hosting/download-clients) integration: use both
together and an \*arr app can **search *and* grab** entirely through Torrin.
Two endpoints: **`/newznab`** for usenet and **`/torznab`** for torrents. Add
whichever you need (or both). Torznab requires the operator to enable it, see
[Torznab requirements](#requirements-torznab).
## What you get
* A standard **Newznab** endpoint backed by Torrin's usenet search — your own
indexers (BYOK) and the built-in indexer both flow through it.
* A standard **Torznab** endpoint backed by Torrin's torrent search — results are
returned as magnets, and anything already in Torrin's cache is tagged so your
\*arr app can prefer instant content.
* Works anywhere "Generic Newznab" / "Generic Torznab" is supported (Prowlarr,
NZBHydra2, Jackett, the \*arr apps, AIOStreams).
## Newznab (usenet)
### Requirements
* A plan with usenet search, and at least one indexer available to your account
(a BYOK Newznab indexer or the built-in one enabled) — the same gate as
searching inside Torrin.
* Your Torrin **API key**.
### Add Torrin in Prowlarr
**Settings → Indexers → Add Indexer → Generic Newznab**, then set:
| Field | Value |
| - | - |
| URL | `https:///newznab` (e.g. `https://api.torrin.app/newznab`) |
| API Path | `/api` (the default) |
| API Key | your Torrin **API key** |
| Categories | Movies (2000) and/or TV (5000) |
Hit **Test** — it should go green. The same URL + key work in **NZBHydra2** (add a
"Newznab" indexer), **Jackett**, or directly in any \*arr app's indexer settings.
## Torznab (torrents)
### Requirements
* The operator must enable it by setting `COMET_URL` (and `COMET_SEARCH_KEY`) on
the API service, see [Configuration](/self-hosting/configuration). Until then
the endpoint returns "not available".
* Any **paid plan**, and your Torrin **API key**.
### Add Torrin in Prowlarr
**Settings → Indexers → Add Indexer → Generic Torznab**, then set:
| Field | Value |
| - | - |
| URL | `https:///torznab` (e.g. `https://api.torrin.app/torznab`) |
| API Path | `/api` (the default) |
| API Key | your Torrin **API key** |
| Categories | Movies (2000) and/or TV (5000) |
Hit **Test** — it should go green. Results carry seeders, size, and infohash, and
cached releases get a `[CACHED]` tag on the title plus higher ranking. To
auto-prefer them, add a Custom Format in Sonarr/Radarr matching `CACHED` and give
it a positive score.
## How it works
Prowlarr/\*arr calls `/newznab/api` or `/torznab/api` with `t=search|tvsearch|movie`
and your API key. Supported params: `q`, `imdbid`, `season`, `ep`.
For usenet, Torrin searches your configured indexers (BYOK + built-in). For
torrents, it runs its torrent search and flags anything already in the cache.
Usenet results come back as a Newznab RSS feed. Torrent results come back as a
Torznab feed of magnets, cached-first, with `[CACHED]` tags on releases
already in Torrin.
On a usenet grab the link resolves to the real `.nzb` through Torrin; a torrent
grab hands the magnet to the download client. Either way it works with any
client, including [Torrin itself](/self-hosting/download-clients).
## Endpoints
| Endpoint | Purpose |
| - | - |
| `GET /newznab/api?t=caps` | Usenet capabilities (no key required) |
| `GET /newznab/api?t=search&q=&apikey=` | Usenet text search |
| `GET /newznab/api?t=tvsearch&imdbid=&season=&ep=&apikey=` | Usenet TV search |
| `GET /newznab/api?t=movie&imdbid=&apikey=` | Usenet movie search |
| `GET /newznab/getnzb?id=&apikey=` | NZB download passthrough |
| `GET /torznab/api?t=caps` | Torrent capabilities (no key required) |
| `GET /torznab/api?t=search&q=&apikey=` | Torrent text search |
| `GET /torznab/api?t=tvsearch&imdbid=&season=&ep=&apikey=` | Torrent TV search |
| `GET /torznab/api?t=movie&imdbid=&apikey=` | Torrent movie search |
Pair this with the [Sonarr/Radarr download client](/self-hosting/download-clients)
to search **and** download through Torrin end-to-end.
# Storage
Source: https://docs.torrin.app/self-hosting/storage
Where cached content lives and how eviction works.
Torrin caches downloaded content on the **local filesystem**. There's no
external object store to run for a single-box setup.
## The cache directory
The compose mounts the host path `/mnt/cache/store` into the services that read
and write cached content. To use a specific disk, mount it there:
```bash theme={null}
# example: mount a dedicated disk at the cache path
mount /dev/sdX1 /mnt/cache/store
```
Or point the volume somewhere else by editing the mount in
`deploy/docker-compose.yml`:
```yaml theme={null}
volumes:
- /your/big/disk:/mnt/cache/store
```
The download **scratch** area (`/mnt/cache/ingest-scratch`) is separate and
temporary — it holds a job's files only until they're published to the cache.
## Encryption at rest
Set `STORAGE_KEY` (a hex key from `openssl rand -hex 32`) to encrypt stored
content at rest. It's off by default. Stored debrid credentials can be
encrypted independently with `CREDS_KEY`.
## Eviction
The cache is capped by `EVICTION_CAP_BYTES` (default 300 GB). A daily sweep
(at `EVICTION_HOUR`) removes the least-recently-used content once the cache
grows past the cap, so it never fills the disk. Popular content stays warm;
rarely-watched content is dropped and simply re-fetched on the next play.
## Bring Your Own Storage
Instead of (or in addition to) the shared cache, users can pipe their content to
their own cloud provider via rclone. See [BYOS](/self-hosting/byos).
# Stremio addon
Source: https://docs.torrin.app/self-hosting/stremio
Stream your Torrin instance from Stremio.
Torrin ships a Stremio addon server (the `stremio` service) so you can browse
and play cached content directly in Stremio.
## Install the addon
The addon is served on port **7000**. Its manifest is at:
```
http://:7000/manifest.json
```
Add it to Stremio one of two ways:
Visit `http://:7000/` in a browser and follow the prompt to
link it to your Torrin account.
Paste the manifest URL into Stremio's **Addons → Add addon** field, or use
the **Install** button on the configure page.
For anything beyond a LAN setup, put the addon behind HTTPS (a reverse proxy /
the `docker-compose.public.yml` overlay). Stremio requires HTTPS for remote
addons.
## How it works
When you pick a title in Stremio, the addon asks `api` whether it's cached. If
so you get an instant signed stream; if not, a download is queued and the stream
becomes available once it completes.
# Troubleshooting
Source: https://docs.torrin.app/self-hosting/troubleshooting
Verify your instance and fix common issues.
## Verify it's running
```bash theme={null}
docker compose ps # all services healthy?
docker compose logs -f api # follow a service's logs
docker compose logs -f ingest
```
Then open your `PUBLIC_URL`, sign up, add a debrid key, and paste a magnet — it
should start downloading (or return an instant stream if it's already cached).
## Common issues
Check gluetun: `docker compose logs -f gluetun`. A bad `VPN_PRIVATE_KEY`,
`VPN_ADDRESSES`, or provider name is the usual cause. `ingest` and
qBittorrent share gluetun's network, so if the VPN is down, downloads stall.
The store path (`/mnt/cache/store`) and scratch dir must be writable by the
containers. Make sure the mounted disk exists and isn't read-only.
Almost always a `PUBLIC_URL` mismatch — signed URLs are minted against it, so
it must be the exact HTTPS address you serve on. See [Exposing publicly](/self-hosting/exposing).
Stremio requires HTTPS for remote addons. Put the instance behind TLS first
([Exposing publicly](/self-hosting/exposing)), then add the manifest URL.
New accounts are on the Free plan (1 concurrent, 50 GB). Bump your account or
edit the plan limits — see [Accounts & plans](/self-hosting/accounts).
## Getting help
* Check [CONTRIBUTING.md](https://github.com/torrin-app/torrin/blob/main/CONTRIBUTING.md)
* Report security issues via [SECURITY.md](https://github.com/torrin-app/torrin/blob/main/SECURITY.md), not a public issue.
# Updating
Source: https://docs.torrin.app/self-hosting/updating
Move your instance to a newer version.
Torrin ships as versioned container images. To update:
```bash theme={null}
cd torrin/deploy
git pull
docker compose pull
docker compose up -d
```
Use the same overlay flags you started with, e.g.:
```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.vpn.yml \
-f docker-compose.public.yml pull
docker compose -f docker-compose.yml -f docker-compose.vpn.yml \
-f docker-compose.public.yml up -d
```
`docker compose up -d` alone does **not** re-pull images — always run `pull`
first, or a service can keep running an old image.
## Notes
* **Database** changes apply automatically on startup (the schema is embedded),
so there's no manual migration step.
* **Config** is your `.env`; new options land in `.env.example` — diff it against
your `.env` after pulling to pick up anything new.
* To pin a specific version instead of the latest, set `TORRIN_TAG` in `.env`.
# VPN
Source: https://docs.torrin.app/self-hosting/vpn
Route downloads through a VPN so your host IP is never exposed.
Torrin runs its download engine (qBittorrent + `ingest`) inside
[gluetun](https://github.com/qdm12/gluetun), so all torrent and download traffic
egresses through your VPN — your server's real IP is never exposed.
This is enabled by the `docker-compose.vpn.yml` overlay:
```bash theme={null}
docker compose -f docker-compose.yml -f docker-compose.vpn.yml up -d
```
## WireGuard config
Fill these in `deploy/.env` (values come from your VPN provider's WireGuard
config):
| Variable | Description |
| - | - |
| `VPN_PROVIDER` | gluetun provider name (e.g. `airvpn`, `mullvad`, `custom`). |
| `VPN_TYPE` | `wireguard` (recommended) or `openvpn`. |
| `VPN_PRIVATE_KEY` | Your WireGuard private key. |
| `VPN_PRESHARED_KEY` | Preshared key, if your provider uses one. |
| `VPN_ADDRESSES` | The address block from your WireGuard config, e.g. `10.x.x.x/32`. |
| `VPN_COUNTRIES` | Preferred exit country/countries (default `Germany`). |
gluetun supports many providers — see its
[wiki](https://github.com/qdm12/gluetun-wiki) for the exact `VPN_PROVIDER`
value and any provider-specific settings.
## Running without a VPN
Leave off the overlay to run without one:
```bash theme={null}
docker compose up -d
```
Without the VPN overlay, downloads use your host's IP directly. Only do this if
you understand the exposure — a VPN is strongly recommended.
# WebDAV
Source: https://docs.torrin.app/self-hosting/webdav
Mount your Torrin library in VLC, Infuse, Kodi, or a file manager.
The `webdav` service exposes your cached library as a WebDAV share, so you can
mount it in players like **VLC**, **Infuse**, **Kodi**, or your OS file manager.
## Connect
WebDAV is served on port **9092**:
```
http://:9092
```
Authenticate with your Torrin account credentials.
Add a network share of type **WebDAV (HTTP)**, host ``,
port `9092`, and your Torrin login.
Add a video source with a **WebDAV server (HTTP)** path:
`http://:9092/`.
Connect to server `http://:9092` and sign in with your account.
For remote access, put WebDAV behind HTTPS via a reverse proxy — most players
require it, and it keeps your credentials off the wire.
## Browse in a browser
No client needed to look around. Open the WebDAV URL in any browser and sign in
with your account to get a clickable file listing you can navigate and play
straight from the page.
## What you'll see
Your library is presented as a folder per release, each holding its playable
files. Streaming pulls from the cache (or your [BYOS](/self-hosting/byos)
provider) on demand, and nothing is stored on the client.