WebUI API Reference¶
Complete API reference for qBitrr's WebUI REST API. All endpoints are accessible via the built-in WebUI server (default port 6969).
Overview¶
Base URL¶
Default: http://localhost:6969
Endpoint Patterns¶
qBitrr provides dual endpoint patterns for flexibility:
| Pattern | Purpose | When authentication is required | Typical use |
|---|---|---|---|
/api/* | API-first endpoints | Same rules as /web/* for mirrored routes: when WebUI auth is enabled, use Bearer WebUI.Token or a browser session after login | External clients, scripts, automation |
/web/* | First-party endpoints | Same as /api/* for mirrored data/control routes (see Public endpoints); not a separate "anonymous API" | React WebUI, same-origin tools |
Mirrored pairs (for example GET /api/processes and GET /web/processes) return the same JSON. Prefer /api/* for scripts (Bearer header) and /web/* for the bundled WebUI (session cookie).
OpenAPI and Swagger UI¶
The running WebUI serves a bundled OpenAPI 3 document and Swagger UI:
| Resource | URL | Description |
|---|---|---|
| Interactive docs | GET /web/docs or GET /api/docs | Swagger UI (both views load the same filtered spec on the same origin) |
| OpenAPI JSON | GET /web/openapi.json or GET /api/openapi.json | Machine-readable spec for codegen or import into API clients |
Authentication: When WebUI.AuthDisabled is true, these URLs work without credentials. When WebUI authentication is enabled, they use the same rules as other protected routes: open /web/docs in a browser after logging into the WebUI (session cookie), or pass Authorization: Bearer <WebUI.Token> (or use Authorize in Swagger for "Try it out"). The main WebUI header includes an OpenAPI link to /web/docs.
The served OpenAPI document is /api/*-first; it also includes mirrored Arr poster thumbnail routes under /web/* (see Arr poster thumbnails). Other /web/*-only routes remain runtime-available and are documented in prose here.
The base spec is maintained in the repository at qBitrr/openapi.json (also shipped inside the Python package). A prose reference for every endpoint continues in this document.
Keeping the spec aligned with the runtime¶
Every Flask route registered in qBitrr/webui.py should appear under paths in qBitrr/openapi.json (and vice versa). A static drift check is provided so the two files cannot diverge silently:
The check is also wired into pre-commit (local hook openapi-check). It parses the @app.<method>("/path") decorators in qBitrr/webui.py, walks paths in qBitrr/openapi.json, and fails on any route that exists in one file but not the other. Path shape is what matters; cosmetic parameter renames (e.g. {id} ↔ {entry_id}) are tolerated.
If you add or remove an endpoint, update qBitrr/openapi.json in the same commit.
Authentication¶
Bearer Token¶
When WebUI authentication is enabled, /api/* endpoints require authentication via Bearer token (or a valid session cookie for browser access):
Header:
Query Parameter (alternative):
Example:
Public Endpoints¶
The following endpoints are always public (no Bearer token or login session):
GET /health— Health checkGET /— Root redirectGET /ui— WebUI entry point (redirect)GET /login— Redirects into the WebUI login flowGET /sw.js— Service workerGET /static/*— Static assets for the WebUIPOST /web/login,POST /web/logout,POST /web/auth/set-password— Local auth flowsGET /web/auth/oidc/challenge— Starts OIDC loginGET <WebUI.OIDC.CallbackPath>— OIDC callback (default/signin-oidc; must match your IdP redirect URI)GET /web/meta— Version and auth flags (used by the WebUI before login)
Special case: GET /web/token returns the API token when auth is disabled, or when the caller is already authorized; when auth is enabled and the caller is not authorized, it responds with 401 and {"token":""} (so the SPA can detect auth without treating the response as a fatal error).
All other GET/POST /api/* and GET/POST /web/* routes (including library views, config, logs, processes, OpenAPI/Swagger URLs, and GET /api/meta) require authentication when WebUI auth is enabled.
Token Authentication¶
Authentication supports multiple formats for flexibility:
Bearer Token (recommended):
Query Parameter (for URLs):
Example:
Token Retrieval — GET /api/token (requires auth) returns the current token:
Endpoint Categories¶
- OpenAPI / Swagger UI — Interactive docs on the running server
- System - Health, status, version info
- Processes - Process monitoring and control
- Logs - Log file access
- Arr Views - Radarr/Sonarr/Lidarr library browsing
- Configuration - Config management
- Updates - Auto-update and version management
System Endpoints¶
Health Check¶
Check if WebUI is running.
Endpoint: GET /health
Authentication: None
Response:
Use Case: Monitoring, reverse proxy health checks, container liveness probes.
Root Redirect¶
Redirect to WebUI.
Endpoint: GET /
Authentication: None
Response: HTTP 302 redirect to /ui
WebUI Entry Point¶
Serve React SPA.
Endpoint: GET /ui
Authentication: None
Response: HTTP 302 redirect to /static/index.html
Headers:
Service Worker¶
Serve service worker for PWA support.
Endpoint: GET /sw.js
Authentication: None
Response: JavaScript file (text/javascript)
Headers:
Use Case: Progressive Web App functionality, offline support.
System Status¶
Get qBittorrent and Arr instance statuses.
Endpoints: - GET /api/status (requires auth) - GET /web/status (public)
Response:
{
"qbit": {
"alive": true,
"host": "localhost",
"port": 8080,
"version": "4.6.0"
},
"qbitInstances": {
"default": {
"alive": true,
"host": "localhost",
"port": 8080,
"version": "4.6.0"
}
},
"arrs": [
{
"category": "radarr-4k",
"name": "Radarr-4K",
"type": "radarr",
"alive": true
},
{
"category": "sonarr-tv",
"name": "Sonarr-TV",
"type": "sonarr",
"alive": true
}
],
"webui": {
"LiveArr": true,
"GroupSonarr": true,
"GroupLidarr": true,
"Theme": "Dark",
"ViewDensity": "Comfortable"
},
"ready": true
}
Fields:
qbit- Legacy single-instance qBittorrent info (for backward compatibility)qbitInstances- Multi-instance qBittorrent info keyed by instance namearrs[].alive- Arr instance process health (checks both search and torrent processes)webui- WebUI configuration settingsready- Overall system ready state
Version Metadata¶
Get current version, latest version, update availability, and changelog.
Endpoints: - GET /api/meta (requires auth) - GET /web/meta (public)
Query Parameters: - force (boolean, optional) - Force refresh from GitHub (bypasses 1-hour cache)
Response:
{
"current_version": "5.2.0",
"latest_version": "5.3.0",
"update_available": true,
"changelog": "## What's Changed\n- Added feature X\n- Fixed bug Y",
"current_version_changelog": "## Previous Release\n- Added feature A",
"changelog_url": "https://github.com/Feramance/qBitrr/releases/tag/v5.3.0",
"repository_url": "https://github.com/Feramance/qBitrr",
"homepage_url": "https://github.com/Feramance/qBitrr",
"last_checked": "2025-11-27T12:00:00Z",
"installation_type": "pip",
"error": null,
"update_state": {
"in_progress": false,
"last_result": null,
"last_error": null,
"completed_at": null
}
}
Installation Types:
pip- Installed via pip/PyPIdocker- Running in Docker containerbinary- Standalone binarysource- Running from sourceunknown- Cannot determine
Update State:
in_progress- Manual update in progresslast_result-"success"or"error"(last update result)last_error- Error message (if last update failed)completed_at- ISO 8601 timestamp (when last update completed)
Caching: Results cached for 1 hour. Use ?force=true to bypass cache.
Process Endpoints¶
List Processes¶
Get all Arr instance processes (search and torrent loops).
Endpoints: - GET /api/processes (requires auth) - GET /web/processes (public)
Response:
{
"processes": [
{
"category": "radarr-4k",
"name": "Radarr-4K",
"kind": "search",
"pid": 12345,
"alive": true,
"rebuilding": false,
"searchSummary": "Found 12 missing movies, searched 5",
"searchTimestamp": "2025-11-27T12:00:00Z"
},
{
"category": "radarr-4k",
"name": "Radarr-4K",
"kind": "torrent",
"pid": 12346,
"alive": true,
"rebuilding": false,
"queueCount": 3,
"categoryCount": 8
}
]
}
Fields:
kind- Process type ("search"or"torrent")pid- Process ID (null if not started)alive- Process running staterebuilding- Global rebuild state (all processes restarting)
Search Process Fields:
searchSummary- Human-readable search status (e.g., "Searched 5 movies, found 2 missing")searchTimestamp- Last search activity timestamp (ISO 8601)
Torrent Process Fields:
queueCount- Active downloads in Arr queuecategoryCount- Torrents in qBittorrent with matching categoryfreeSpacePaused- Torrents tagged as paused due to free-space guard (Torrent Policy Manager only)metricType- Special metric type ("torrent-policy"for Torrent Policy Manager,"category"for PlaceHolderArr)
Refresh Interval: Poll this endpoint every 5-10 seconds for real-time updates.
Restart Process¶
Restart specific Arr instance process(es).
Endpoints: - POST /api/processes/<category>/<kind>/restart (requires auth) - POST /web/processes/<category>/<kind>/restart (public)
Path Parameters:
category(string, required) - Arr instance category (e.g.,radarr-4k)kind(string, required) - Process type:search,torrent, orall
Request: No body required
Response (Success):
Response (Error):
HTTP Status Codes:
200- Success400- Invalid kind parameter404- Unknown category503- Arr manager not ready
Behavior:
- Kills existing process(es) via
SIGTERM - Removes from child process list
- Spawns new process(es) via multiprocessing
- Returns immediately (restart is asynchronous)
Restart All Processes¶
Restart all Arr instance processes (global restart).
Endpoints: - POST /api/processes/restart_all (requires auth) - POST /web/processes/restart_all (public)
Request: No body required
Response:
Behavior: Iterates through all managed Arr instances and restarts both search and torrent processes.
Change Log Level¶
Dynamically change application log level without restart.
Endpoints: - POST /api/loglevel (requires auth) - POST /web/loglevel (public)
Request Body:
Valid Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACE
Response (Success):
Response (Error):
Behavior:
- Updates root logger level
- Updates all child loggers (qBitrr, qBitrr.arr, qBitrr.webui, etc.)
- Changes take effect immediately (no restart required)
- Does not persist to config file
Rebuild Arr Databases¶
Trigger database rebuild for all Arr instances.
Endpoints: - POST /api/arr/rebuild (requires auth) - POST /web/arr/rebuild (public)
Request: No body required
Response:
Behavior:
- Spawns background thread
- Restarts all Arr instances sequentially
- Triggers
db_update()for each instance - Refreshes cached library data from Arr APIs
- Returns immediately (rebuild is asynchronous)
Use Case: Force refresh after bulk library changes in Radarr/Sonarr/Lidarr.
Restart Arr Instance¶
Restart specific Arr instance (both search and torrent processes).
Endpoints: - POST /api/arr/<section>/restart (requires auth) - POST /web/arr/<section>/restart (public)
Path Parameters:
section(string, required) - Arr instance key (e.g.,Radarr-4K,Sonarr-TV)
Request: No body required
Response (Success):
Response (Error)**:
HTTP Status Codes:
200- Success404- Unknown section503- Arr manager not ready
Open Arr Item in Arr UI¶
Open a specific Radarr/Sonarr/Lidarr item in its native Arr web interface.
Endpoints: - GET /api/arr/<category>/open/<kind>/<entry_id> (requires auth) - GET /web/arr/<category>/open/<kind>/<entry_id> (public)
Path Parameters:
category(string, required) - Arr instance category key (for exampleradarr-4k)kind(string, required) - Target item type:movie,series, orartistentry_id(integer, required) - Arr item id in that instance
Response (Success): HTTP 302 redirect to the Arr UI page.
Route Resolution Behavior:
- qBitrr first resolves the managed Arr instance by
categoryand validateskind. - qBitrr fetches the item from that Arr API using
entry_id. - qBitrr then builds an app-native URL token:
- Radarr: prefers
titleSlug, falls back to numericentry_id - Sonarr: prefers
titleSlug, falls back to numericentry_id - Lidarr: prefers
foreignArtistId(thentitleSlug), falls back to numericentry_id
Item Targets:
movie->.../movie/<route-token>(Radarr)series->.../series/<route-token>(Sonarr)artist->.../artist/<route-token>(Lidarr)
Response (Error):
HTTP Status Codes:
302- Redirect to Arr UI item page400- Unknown item kind or missing Arr URI401- Unauthorized404- Unknown Arr section/type mismatch or item lookup failure503- Arr manager not ready
Get qBit Categories¶
Get qBittorrent categories managed by qBitrr (qBit-managed and Arr-managed) with seeding statistics.
Endpoint: - GET /web/qbit/categories (public only; no /api/ variant)
Authentication: None (public endpoint).
Response: Array of category objects, each including: - category - Category name - instance - qBittorrent instance name - managedBy - "qbit" or "arr" - torrentCount, seedingCount, totalSize, avgRatio, avgSeedingTime - seedingConfig - Per-category seeding limits (e.g. maxRatio, maxTime, removeMode, downloadLimit, uploadLimit)
Use Case: Category management UI, seeding stats display.
Get Torrent Distribution¶
Get torrent distribution statistics across all categories.
Endpoints: - GET /api/torrents/distribution (requires auth) - GET /web/torrents/distribution (public)
Response:
{
"distribution": {
"radarr-4k": {
"default": 50
},
"sonarr-tv": {
"default": 30
},
"uncategorized": {
"default": 5
}
}
}
Fields:
distribution- Object keyed by category name, containing objects keyed by qBit instance name with torrent counts- Each inner object maps qBit instance names to torrent counts for that category
Log Endpoints¶
List Log Files¶
Get all available log files.
Endpoints: - GET /api/logs (requires auth) - GET /web/logs (public)
Response:
Fields:
files- Array of log filenames (use with/api/logs/<filename>to get content)
Log Rotation: Log files rotate at 10 MB, keeping 5 backups. Older backups appear as Main.log.1, Main.log.2, etc.
Get Log Content¶
Stream log file content as plain text.
Endpoints: - GET /api/logs/<name> (requires auth) - GET /web/logs/<name> (public)
Path Parameters:
name(string, required) - Log filename (e.g.,Main.log)
Response: Plain text (MIME type: text/plain; charset=utf-8)
Headers:
Example:
Behavior:
- Reads entire log file into memory
- Returns full content (supports dynamic loading in LazyLog component)
- Invalid UTF-8 sequences ignored (errors="ignore")
Download Log File¶
Download log file as attachment.
Endpoints: - GET /api/logs/<name>/download (requires auth) - GET /web/logs/<name>/download (public)
Path Parameters:
name(string, required) - Log filename
Response: File attachment (MIME type: application/octet-stream)
Headers:
Example:
Arr View Endpoints¶
Arr poster thumbnails (cached)¶
Read-only image bytes for browse Icon tiles and detail modals. The server asks each Arr instance for the entity (movie, series, or Lidarr artist) and only serves images from that same Arr host (for example MediaCover paths under the Arr base URL). Metadata that points at external CDNs is ignored; if no same-host image exists, the route returns 404 and the WebUI shows a built-in placeholder. qBitrr downloads bytes from the Arr URL using the instance API key, caches them under the qBitrr data directory, and returns them with long-cache headers when possible. For Lidarr artists, when the JSON payload has no usable same-host URL, qBitrr may probe MediaCover paths on that Arr host before giving up. Cache files written before this policy may still contain older bytes until cleared.
Endpoints (each has a /api and /web mirror; behavior is identical):
| Method | Path pattern |
|---|---|
GET | /api/radarr/<category>/movie/<id>/thumbnail · /web/radarr/<category>/movie/<id>/thumbnail |
GET | /api/sonarr/<category>/series/<id>/thumbnail · /web/sonarr/<category>/series/<id>/thumbnail |
GET | /api/lidarr/<category>/artist/<id>/thumbnail · /web/lidarr/<category>/artist/<id>/thumbnail |
Parameters: category is the qBitrr Arr instance category; id is the Arr database id for that entity (movie, series, or Lidarr artist). Optional ?token=<WebUI.Token> works for <img src> when not using a session cookie.
Responses: 200 with an image body (or 304 when If-None-Match matches), 401 if unauthorized, 404 if the entity or image cannot be resolved.
These routes are listed in qBitrr/openapi.json (both /api and /web variants).
Radarr Movies¶
Browse Radarr movie library from cached database.
Endpoints: - GET /api/radarr/<category>/movies (requires auth) - GET /web/radarr/<category>/movies (public)
Path Parameters:
category(string, required) - Radarr instance category (e.g.,radarr-4k)
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | null | Search query (title substring match) |
page | integer | 0 | Page number (0-indexed) |
page_size | integer | 50 | Results per page (1-100) |
year_min | integer | null | Minimum release year filter |
year_max | integer | null | Maximum release year filter |
monitored | boolean | null | Filter by monitored status |
has_file | boolean | null | Filter by file availability |
quality_met | boolean | null | Filter by quality profile met |
is_request | boolean | null | Filter by request status (Ombi/Overseerr) |
Response:
{
"category": "radarr-4k",
"counts": {
"available": 120,
"monitored": 150,
"missing": 30,
"quality_met": 100,
"requests": 5
},
"total": 150,
"page": 0,
"page_size": 50,
"movies": [
{
"id": 1,
"title": "Inception",
"year": 2010,
"monitored": true,
"hasFile": true,
"movieFileId": 12345,
"tmdbId": 27205,
"imdbId": "tt1375666",
"qualityMet": true,
"isRequest": false,
"upgrade": false,
"customFormatScore": 0,
"minCustomFormatScore": 0,
"customFormatMet": true,
"reason": null,
"qualityProfileId": 1,
"qualityProfileName": "Ultra-HD"
}
]
}
Fields:
counts- Aggregate totals (unaffected by pagination)total- Total movies matching filterspage/page_size- Pagination echomovies[].reason- Why entry appears in search results (e.g., "Missing", "Quality Unmet")
Performance: Uses cached database (SQLite). For real-time data, enable WebUI.LiveArr = true (increases Arr API load).
Sonarr Series¶
Browse Sonarr series library from cached database.
Endpoints: - GET /api/sonarr/<category>/series (requires auth) - GET /web/sonarr/<category>/series (public)
Path Parameters:
category(string, required) - Sonarr instance category
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | null | Search query (series title) |
page | integer | 0 | Page number |
page_size | integer | 25 | Results per page |
missing / only_missing | boolean | false | Show only missing episodes |
Response:
{
"category": "sonarr-tv",
"counts": {
"available": 500,
"monitored": 600,
"missing": 100
},
"total": 50,
"page": 0,
"page_size": 25,
"series": [
{
"series": {
"id": 1,
"title": "Breaking Bad",
"tvdbId": 81189,
"seriesType": "standard",
"monitored": true,
"qualityProfileId": 1,
"qualityProfileName": "HD-1080p"
},
"totals": {
"available": 62,
"monitored": 62,
"missing": 0
},
"seasons": [
{
"seasonNumber": 1,
"available": 7,
"monitored": 7,
"missing": 0,
"episodes": [
{
"id": 101,
"episodeNumber": 1,
"title": "Pilot",
"airDateUtc": "2008-01-20T00:00:00Z",
"hasFile": true,
"episodeFileId": 1001,
"monitored": true
}
]
}
]
}
]
}
Grouping: Episodes grouped by series → season (hierarchical structure).
Lidarr Artists¶
Browse Lidarr artist library from the cached database. Mirrors Radarr/Sonarr filtering: a Status (missing only) and Search Reason filter, scoped to the artists' albums.
Endpoints: - GET /api/lidarr/<category>/artists (requires auth) - GET /web/lidarr/<category>/artists (public)
Path Parameters:
category(string, required) – Lidarr instance category. The literal stringlidarrresolves to the single configured Lidarr instance when only one exists.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | null | Search query (artist name) |
page | integer | 0 | Page number |
page_size | integer | 50 | Results per page (alias size accepted) |
monitored | boolean | null | Filter by monitored artist |
missing | boolean | false | Restrict to artists with at least one monitored album whose file is missing |
reason | string | all | Restrict to artists with at least one album whose Reason matches. Accepted values: Missing, Quality, CustomFormat, Upgrade, Not being searched (also matches NULL). Use all (or omit) for no reason filter. |
The missing and reason filters are applied at the album level via an EXISTS-style subquery on AlbumFilesModel; total and pagination reflect the filtered artist set, so the UI shows accurate counts.
Lidarr Albums¶
Browse Lidarr album library from cached database.
Endpoints: - GET /web/lidarr/<category>/albums (public)
Note: There is no /api/lidarr/<category>/albums endpoint. Use /web/lidarr/<category>/albums instead.
Path Parameters:
category(string, required) - Lidarr instance category
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | null | Search query (artist or album name) |
page | integer | 0 | Page number |
page_size | integer | 25 | Results per page |
monitored | boolean | null | Filter by monitored status |
has_file | boolean | null | Filter by file availability |
Response:
{
"category": "lidarr",
"counts": {
"available": 200,
"monitored": 250,
"missing": 50,
"quality_met": 180,
"requests": 2
},
"total": 250,
"page": 0,
"page_size": 25,
"albums": [
{
"album": {
"id": 1,
"title": "Dark Side of the Moon",
"artistId": 1,
"artistName": "Pink Floyd",
"monitored": true,
"hasFile": true,
"foreignAlbumId": "12345",
"releaseDate": "1973-03-01",
"qualityMet": true,
"isRequest": false,
"upgrade": false,
"customFormatScore": 0,
"minCustomFormatScore": 0,
"customFormatMet": true,
"reason": null,
"qualityProfileId": 1,
"qualityProfileName": "Lossless"
},
"totals": {
"available": 10,
"monitored": 10,
"missing": 0
},
"tracks": [
{
"id": 101,
"trackNumber": 1,
"title": "Speak to Me",
"duration": 70,
"hasFile": true,
"trackFileId": 1001,
"monitored": true,
"reason": "Not being searched"
}
]
}
]
}
Grouping: Tracks nested within albums.
Track reason: Each object in tracks includes a string reason derived for that row (for example Missing, Unmonitored, Not being searched, or album-level states such as Quality when applicable). Older responses may omit it; clients can fall back to the parent album reason.
List Arr Instances¶
Get all configured Arr instances.
Endpoints: - GET /api/arr (requires auth) - GET /web/arr (public)
Response:
{
"arr": [
{
"category": "radarr-4k",
"name": "Radarr-4K",
"type": "radarr"
},
{
"category": "sonarr-tv",
"name": "Sonarr-TV",
"type": "sonarr"
},
{
"category": "lidarr",
"name": "Lidarr",
"type": "lidarr"
}
],
"ready": true
}
Fields:
arr- Array of Arr instancesarr[].category- qBittorrent category (used in API paths)arr[].name- Friendly display namearr[].type- Arr type (radarr,sonarr,lidarr)ready- Overall system ready state
Configuration Endpoints¶
Get Configuration¶
Fetch current configuration from disk.
Endpoints: - GET /api/config (requires auth) - GET /web/config (public)
Response:
{
"Settings": {
"ConsoleLevel": "INFO",
"Logging": true,
"CompletedDownloadFolder": "/mnt/downloads",
"FreeSpace": "100G",
"AutoUpdateEnabled": true
},
"WebUI": {
"Host": "0.0.0.0",
"Port": 6969,
"Token": "abc123...",
"LiveArr": false,
"GroupSonarr": true,
"Theme": "Dark"
},
"qBit": {
"Host": "localhost",
"Port": 8080,
"UserName": "admin",
"Password": "***"
},
"Radarr-4K": {
"Managed": true,
"URI": "http://localhost:7878",
"APIKey": "***",
"Category": "radarr-4k"
}
}
Behavior:
- Reloads config from disk (always fresh)
- Returns entire config tree as JSON
- Converts TOML to JSON-compatible structure
Public Endpoint: /web/config includes config version mismatch warnings:
{
"config": { ... },
"warning": {
"type": "config_version_mismatch",
"message": "Config version 1 is outdated (current: 2)",
"currentVersion": 1
}
}
Update Configuration¶
Apply changes to configuration and trigger reload.
Endpoints: - POST /api/config (requires auth) - POST /web/config (public)
Request Body:
{
"changes": {
"Settings.LoopSleepTimer": 60,
"Radarr-4K.EntrySearch.SearchLimit": 10,
"WebUI.Theme": "Dark"
}
}
Dotted Key Format: Use dot notation for nested keys (e.g., Radarr-4K.Torrent.AutoDelete).
Deletion: Set value to null to delete key:
Response (Success):
{
"status": "ok",
"configReloaded": true,
"reloadType": "single_arr",
"affectedInstances": ["Radarr-4K"]
}
Reload Types:
| Type | Description | Behavior |
|---|---|---|
frontend | Frontend-only changes | No reload (e.g., WebUI.Theme) |
webui | WebUI server settings | Restart WebUI server |
single_arr | One Arr instance | Reload that instance only |
multi_arr | Multiple Arr instances | Reload each instance sequentially |
full | Global settings | Reload all components |
Response (Validation Error):
{
"error": "Please resolve the following issues:\nWebUI.Port: WebUI Port must be between 1 and 65535."
}
Response (Protected Key Error):
HTTP Status Codes:
200- Success400- Invalid request body403- Protected key modification attempt500- Save failure
Protected Keys:
Settings.ConfigVersion- Managed automatically by migration system
Test Arr Connection¶
Test connection to Arr instance without saving configuration.
Endpoints: - POST /api/arr/test-connection (requires auth) - POST /web/arr/test-connection (public)
Request Body: Either credentials or an instance key:
-
By credentials (e.g. new instance or form has real values):
-
By instance key (when API key is redacted in the UI; backend uses stored config):
WheninstanceKeyis present,uriandapiKeyare not required.
Valid Arr Types: radarr, sonarr, lidarr
Response (Success):
{
"success": true,
"version": "4.3.2.6857",
"qualityProfiles": [
{
"id": 1,
"name": "HD-1080p"
},
{
"id": 4,
"name": "Ultra-HD"
}
]
}
Response (Failure):
HTTP Status Codes:
200- Connection test completed (checksuccessfield)400- Missing required fields500- Unexpected error
Use Case: Validate Arr credentials before saving config changes.
Update Endpoints¶
Trigger Manual Update¶
Trigger qBitrr self-update (pip or binary).
Endpoints: - POST /api/update (requires auth) - POST /web/update (public)
Request: No body required
Response (Success):
Response (Already Running):
HTTP Status Codes:
200- Update started409- Update already in progress
Behavior:
- Spawns background thread
- Runs
pip install --upgrade qBitrr2(pip) or downloads binary (binary) - Restarts qBitrr on success
- Returns immediately (update is asynchronous)
Monitoring: Poll GET /api/meta to check update_state.in_progress and update_state.last_result.
Download Binary Update¶
Redirect to GitHub binary download URL for current platform.
Endpoints: - GET /api/download-update (requires auth) - GET /web/download-update (public)
Response (Success): HTTP 302 redirect to GitHub asset URL
Response (Not Binary):
Response (No Update):
Response (No Binary):
HTTP Status Codes:
302- Redirect to download400- Not a binary installation404- No update or no binary available
Use Case: Manual binary download for offline update.
Error Responses¶
Standard Error Format¶
All errors return JSON with an error field:
Common HTTP Status Codes¶
| Code | Meaning | Common Causes |
|---|---|---|
200 | Success | Request completed successfully |
302 | Redirect | Root endpoint, binary download |
400 | Bad Request | Invalid parameters, malformed JSON |
401 | Unauthorized | Missing or invalid token |
403 | Forbidden | Protected key modification |
404 | Not Found | Unknown category, missing log file |
409 | Conflict | Update already in progress |
500 | Server Error | Config save failure, unexpected exception |
503 | Service Unavailable | Arr manager not ready |
Rate Limiting¶
No rate limiting is enforced by default. External reverse proxies (e.g., Nginx, Traefik) should implement rate limiting if exposed publicly.
CORS¶
Cross-Origin Requests: Not explicitly configured. CORS headers are not set by default. Configure reverse proxy to add CORS headers if needed:
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
WebSocket Support¶
Not supported. Use HTTP polling for real-time updates:
GET /api/processes- Poll every 5-10 secondsGET /api/meta- Poll every 60 seconds (cached for 1 hour)GET /api/status- Poll every 10-30 seconds
Pagination¶
Arr view endpoints support pagination via page and page_size parameters:
Example:
# Page 1 (first 50 results)
curl "http://localhost:6969/web/radarr/radarr-4k/movies?page=0&page_size=50"
# Page 2 (next 50 results)
curl "http://localhost:6969/web/radarr/radarr-4k/movies?page=1&page_size=50"
Response:
Calculating Pages:
Filtering¶
Arr view endpoints support multiple filters:
Example (Radarr):
# Missing 4K movies from 2020-2023
curl "http://localhost:6969/web/radarr/radarr-4k/movies?has_file=false&year_min=2020&year_max=2023&monitored=true"
Filters are cumulative (AND logic). All specified filters must match.
Best Practices¶
- Use
/web/*endpoints for WebUI to avoid token management - Use
/api/*endpoints for external clients with Bearer token - Cache
/api/metaresponses for 1 hour to reduce GitHub API load - Poll
/api/processesevery 5-10 seconds (not faster to avoid overhead) - Enable
WebUI.LiveArronly when real-time Arr data is required (increases API load) - Set
WebUI.Tokenwhen exposing WebUI publicly - Use reverse proxy for HTTPS, rate limiting, and authentication
- Monitor update state via
/api/metaafter triggering/api/update - Test Arr connections via
/api/arr/test-connectionbefore saving config - Page Arr views to avoid memory issues with large libraries (use
page_size=50)
Example: Python Client¶
import requests
class QbitrrClient:
def __init__(self, base_url, token=None):
self.base_url = base_url
self.token = token
def _headers(self):
if self.token:
return {"Authorization": f"Bearer {self.token}"}
return {}
def get_processes(self):
url = f"{self.base_url}/api/processes"
response = requests.get(url, headers=self._headers())
response.raise_for_status()
return response.json()
def restart_process(self, category, kind):
url = f"{self.base_url}/api/processes/{category}/{kind}/restart"
response = requests.post(url, headers=self._headers())
response.raise_for_status()
return response.json()
def get_radarr_movies(self, category, page=0, page_size=50, has_file=None):
url = f"{self.base_url}/api/radarr/{category}/movies"
params = {"page": page, "page_size": page_size}
if has_file is not None:
params["has_file"] = str(has_file).lower()
response = requests.get(url, params=params, headers=self._headers())
response.raise_for_status()
return response.json()
# Usage
client = QbitrrClient("http://localhost:6969", token="abc123...")
processes = client.get_processes()
print(f"Found {len(processes['processes'])} processes")
movies = client.get_radarr_movies("radarr-4k", has_file=False)
print(f"Missing movies: {movies['counts']['missing']}")
Example: cURL Commands¶
Get Processes¶
Restart Arr Instance¶
Get Radarr Movies (Missing Only)¶
Update Configuration¶
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer abc123..." \
-d '{"changes": {"Settings.LoopSleepTimer": 60}}' \
http://localhost:6969/api/config
Trigger Update¶
Related Pages¶
- Configuration Editor - WebUI config management
- Processes Page - Process monitoring interface
- Logs Page - Log viewing interface
- Arr Views - Library browsing interface
- WebUI Overview - Introduction to WebUI
See Also¶
- Configuration File Reference - Manual TOML editing
- First Run Guide - Initial setup
- Troubleshooting - Common issues