rest api - degoya/rDownloader GitHub Wiki
Everything the web interface does, it does over a documented REST API under /api/v1, plus a
Server-Sent Events stream for live updates. The interface has no private back channel.
Every operation declares the permission area it costs.
GET /api/v1/openapi.json
Or from the command line, without a running service:
rdownloader openapi --output openapi.jsonThe document is generated from the code, so it matches the running build. Use it to generate a client in any language — the web interface's own client is generated from it.
Issue an API token under Settings → Clients & API → API & MCP. It is shown once and stored only as a SHA-256 digest.
Authorization: Bearer <TOKEN>
Tokens are managed over /api/v1/api-tokens — create, list, revoke. Creating one takes an
optional expires_in_days (1 to 3650; left out, the token never expires); an expired token is
refused like a revoked one and is listed with its expires_at until it is revoked. A capture
token does not work here and cannot be turned into an API token.
A browser session (cookie) also authenticates, which is what the web interface uses.
Seven, one per kind of delegation:
| Area | Reaches |
|---|---|
api:read |
Queue, progress, post-processing and storage status |
api:intake |
Adding links, files and containers |
api:queue |
Controlling queued and running work |
api:config |
Configuration that carries no credential |
api:secrets |
Stored credentials and the resources built on them |
api:admin |
Plugins, setup, reconnect, power, whole-configuration import and export, clearing the log, the audit log and the statistics |
api:metrics |
The Prometheus metrics endpoint, and nothing else |
api:* means every area. Full details are on
Accounts, sessions and permissions.
A refusal names the permission that was missing, as a stable auth.scope_insufficient code.
The groups under /api/v1, roughly by size:
collector · downloads · subscriptions · packages · capture · plugins · automations ·
mfa · streams · accounts · notifications · auth · site-rules · system ·
auth-profiles · bandwidth · nzb · remote-credentials · captchas · categories ·
diagnostics · postprocess · remote-jobs · settings · torrents · api-tokens ·
category-rules · sessions · usenet · audit · captcha-config · hotfolders · power ·
proxy-profiles · routing · setup · storage-roots · storage · captcha-answerers ·
containers · dlc · health · metrics · oauth · providers · reconnect ·
site-rule-groups · stats
Seven of these carry the operational features: site-rules and
site-rule-groups (page recognition), diagnostics (the log store and the bundle), audit (the
audit log), remote-jobs (work running at a provider) and stats with metrics (the statistics
page and a Prometheus endpoint).
The OpenAPI document lists the operations in each.
A few worth knowing by name:
| Endpoint | Does |
|---|---|
GET /api/v1/health |
Is the service answering; names only the version |
GET /api/v1/system/about |
The running build — version, commit, build time, plugin contract, platform — and the licenses, behind the sign-in (api:read) |
GET /api/v1/torrents/capabilities |
What the embedded torrent engine can actually do |
GET /api/v1/bandwidth/capabilities |
Which transports a limit can be enforced on |
GET, PUT /api/v1/packages/{id}/speed-limit
|
A package's own download limit (download_bytes_per_second, null for none); refused with torrent.capability_unsupported for a package holding a torrent |
GET, PUT /api/v1/packages/{id}/download-window
|
A package's own download window (download_window with its weekly windows and ignore_schedule_pause; null follows the category's); the read also names the category's window and held — window, schedule or null
|
PUT /api/v1/categories/{id}/download-window |
The window a category's packages follow (null leaves them to the bandwidth schedule); both refuse a bad window with download_window.window_invalid or download_window.too_many_windows (at most 28 spans) |
POST /api/v1/storage/capacity/{target}/resume |
Release a storage root blocked on free space |
POST /api/v1/packages/{id}/extract/force |
Post-process anyway, despite a failed verification |
POST /api/v1/power/cancel |
Cancel a standby or shutdown countdown |
GET /api/v1/metrics |
Prometheus metrics for the queue, transfers and errors |
GET /api/v1/diagnostics/logs |
The stored log records, filtered |
GET /api/v1/audit/records |
The audit log, filtered |
POST /api/v1/auth/password |
Change the administrator password; needs the current one and ends every other session |
GET, POST /api/v1/system/restart
|
Whether a restart is pending — pending, reasons, how (self, supervisor or manual), can_restart — and the restart itself (api:admin); running downloads refuse it with restart.transfers_active unless "allow_active": true
|
POST /api/v1/capture/server-update/restart |
The capture agent's Restart server (capture token with capture:server_update) |
GET, POST /api/v1/system/cleanup
|
What a clean-up of the data folder would remove — backups before updates and database upgrades, compiled plugin code, old skipped subscription entries, free database space — and the clean-up itself (api:admin); refused without "confirmed": true
|
GET /api/v1/system/data-reset |
How many records the log, the audit log, the statistics and the notification history hold |
POST /api/v1/diagnostics/logs/clear, /api/v1/audit/records/clear, /api/v1/stats/transfers/clear, /api/v1/notifications/deliveries/clear
|
Empty one store; refused without "confirmed": true
|
GET /api/v1/history |
The download history, newest first; q, outcome, kind, from, to filter it, limit/offset page it with X-Total-Count
|
POST /api/v1/history/{id}/readd |
Put an entry's sources back into the LinkGrabber |
POST /api/v1/history/clear |
Empty the download history; refused without "confirmed": true
|
POST /api/v1/automations/{id}/versions/reset |
Keep only the automation's version in force, renumbered to 1, and delete the older versions with their runs; refused with automation.version_reset_run_active while a run of an older version is queued, running or retrying |
GET /api/v1/notifications/placeholders |
The placeholders each event fills in a notification rule's title_template and body_template (api:config); a rule's save refuses a template with notification.template_syntax, notification.template_unknown_placeholder or notification.template_too_long
|
POST /api/v1/capture/file |
The browser extension hands over a file it received, or its address with that host's cookies (capture scope) — see Browser extension |
GET /api/v1/remote-jobs/providers |
Which providers have a remote-job plugin installed; ?container=nzb names only those that take NZB files |
POST, GET, DELETE /api/v1/accounts/{id}/browser-session
|
Ask the browser extension for the account's hoster session, read the request's state, withdraw it |
POST /api/v1/accounts/{id}/remote-jobs |
Hand a job to an account: exactly one of magnet, address or container (base64, at most 16 MiB) |
POST /api/v1/nzb/imports/{id}/remote-job, POST /api/v1/packages/{id}/remote-job
|
Hand the NZB of a LinkGrabber import or of a queued package to an account whose provider takes NZB files; only the account is sent |
GET, PUT /api/v1/plugins/updates/settings
|
Whether every installed plugin installs its updates by itself |
POST /api/v1/downloads |
Add a direct link; "paused": true creates it paused instead of queued |
The lists that grow with use — GET /api/v1/downloads, /api/v1/packages,
/api/v1/collector/packages, /api/v1/collector/batches, /api/v1/collector/candidates and
/api/v1/nzb/imports — take an optional limit (1–1000) and offset. Without either the whole
list comes back. With one, the answer is that window of the list in its own order, and
the X-Total-Count header names the length of the whole list; an offset past the end is an empty
page. A limit out of range or a value that is no number is refused with request.page_limit
rather than shortened, so a short page always means the end of the list.
The four import routes — /api/v1/containers/import, /api/v1/dlc/import,
/api/v1/torrents/import and POST /api/v1/nzb/imports — take application/json beside
multipart/form-data. The file goes as base64 in content, next to the same fields the upload
has (file_name, name, category_id, priority, and format for the container route). A
script gets exactly the result and the error codes a browser upload gets, at the same api:intake
price.
The request limit is 65 MiB and base64 grows a file by a third, so a JSON body carries a file of
at most 48 MiB. A larger file is refused with container.too_large before it is decoded, a body
over the service-wide limit with request.body_too_large, and invalid base64 with
container.base64_invalid — nothing is truncated. A larger NZB still goes in as an upload.
Two Server-Sent Events streams, both with keep-alive:
| Stream | Carries |
|---|---|
GET /api/v1/events |
The general service event stream |
GET /api/v1/capture/events |
Capture-agent intake events only |
The event stream applies the same permission areas as the routes. A monitoring token with
api:read receives queue and progress events and never account, proxy, credential or plugin
changes.
The capture stream carries only the intake event and the captcha signal stripped to a bare count, so a capture token given to a desktop agent does not expose download paths.
A client that names the last id it saw in Last-Event-ID is handed what the service buffered
after it, through the same permission filter as the live stream. When the buffer no longer reaches
back that far, the client is told with a marker. Every stream opens with the retry: interval
clients should reconnect at. The buffer is in memory, so a resume does not survive a restart of
the service.
Errors carry a stable code with parameters, not free text. The web interface translates
them into four languages; your own client can switch on the code.
Signed query parameters, Authorization, Cookie and secret-vault references are removed from
stored failures, REST error bodies and logs; parameter names are kept.
A SABnzbd-compatible API and a qBittorrent Web API v2 subset exist outside /api/v1 for
automation tools written against those clients. See
Automation tool compatibility.
/api/v1 changes only by addition: new routes, new optional request fields, new response
fields. A route stays until another replaces it. A release that has to break the API anyway — a
removed route or response field, a newly required request field or parameter, a narrowed set of
values, a changed type — names each break with its reason in its changelog. Clients should ignore
response fields they do not know.
If you build against a checkout: scripts/api-contract.sh regenerates both web/openapi.json and
the TypeScript types. Those files are generated — do not hand-edit them.
- MCP server — the same operations, for an AI assistant
- Command line client
- Accounts, sessions and permissions
- Automation tool compatibility