rest api - degoya/rDownloader GitHub Wiki

REST API

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.

The OpenAPI document

GET /api/v1/openapi.json

Or from the command line, without a running service:

rdownloader openapi --output openapi.json

The 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.

Authentication

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.

Permission areas

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.

Route groups

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

Long lists in pages

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.

Handing in a file as JSON

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.

Events

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.

Both streams resume

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

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.

Compatibility surfaces

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.

Stability

/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.

After changing the API

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.

Related pages

⚠️ **GitHub.com Fallback** ⚠️