Complete API reference for qBitrr's WebUI REST API. All endpoints are accessible via the built-in WebUI server (default port 6969).
http://<host>:<port>
Default: http://localhost:6969
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).
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 intentionally includes only /api/* paths. /web/* routes remain available at runtime and are documented in this page, but they are excluded from Swagger/OpenAPI output.
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.
When WebUI authentication is enabled, /api/* endpoints require authentication via Bearer token (or a valid session cookie for browser access):
Header:
Authorization: Bearer <token>Query Parameter (alternative):
?token=<token>
Example:
curl -H "Authorization: Bearer abc123..." http://localhost:6969/api/processesThe 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.
Authentication supports multiple formats for flexibility:
Bearer Token (recommended):
Authorization: Bearer <token>Query Parameter (for URLs):
?token=<token>
Example:
curl -H "Authorization: Bearer abc123..." http://localhost:6969/api/processesToken Retrieval — GET /api/token (requires auth) returns the current token:
{"token": "<your-token>"}- 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
Check if WebUI is running.
Endpoint: GET /health
Authentication: None
Response:
{
"status": "ok"
}Use Case: Monitoring, reverse proxy health checks, container liveness probes.
Redirect to WebUI.
Endpoint: GET /
Authentication: None
Response: HTTP 302 redirect to /ui
Serve React SPA.
Endpoint: GET /ui
Authentication: None
Response: HTTP 302 redirect to /static/index.html
Headers:
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0Serve service worker for PWA support.
Endpoint: GET /sw.js
Authentication: None
Response: JavaScript file (text/javascript)
Headers:
Cache-Control: no-cache, no-store, must-revalidateUse Case: Progressive Web App functionality, offline support.
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
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.
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 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):
{
"status": "ok",
"restarted": ["search", "torrent"]
}Response (Error):
{
"error": "Unknown category radarr-4k"
}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 Arr instance processes (global restart).
Endpoints:
POST /api/processes/restart_all(requires auth)POST /web/processes/restart_all(public)
Request: No body required
Response:
{
"status": "ok"
}Behavior: Iterates through all managed Arr instances and restarts both search and torrent processes.
Dynamically change application log level without restart.
Endpoints:
POST /api/loglevel(requires auth)POST /web/loglevel(public)
Request Body:
{
"level": "DEBUG"
}Valid Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACE
Response (Success):
{
"status": "ok",
"level": "DEBUG"
}Response (Error):
{
"error": "Invalid log level"
}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
Trigger database rebuild for all Arr instances.
Endpoints:
POST /api/arr/rebuild(requires auth)POST /web/arr/rebuild(public)
Request: No body required
Response:
{
"status": "started"
}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 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):
{
"status": "ok",
"restarted": ["search", "torrent"]
}Response (Error)**:
{
"error": "Unknown section Radarr-4K"
}HTTP Status Codes:
200- Success404- Unknown section503- Arr manager not ready
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 nameinstance- qBittorrent instance namemanagedBy-"qbit"or"arr"torrentCount,seedingCount,totalSize,avgRatio,avgSeedingTimeseedingConfig- Per-category seeding limits (e.g.maxRatio,maxTime,removeMode,downloadLimit,uploadLimit)
Use Case: Category management UI, seeding stats display.
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
Get all available log files.
Endpoints:
GET /api/logs(requires auth)GET /web/logs(public)
Response:
{
"files": ["Main.log", "WebUI.log"]
}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.
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:
Cache-Control: no-cache
Content-Type: text/plain; charset=utf-8Example:
curl http://localhost:6969/web/logs/Main.logBehavior:
- 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 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:
Content-Disposition: attachment; filename="Main.log"Example:
curl -O http://localhost:6969/web/logs/Main.log/downloadBrowse 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).
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).
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
}
]
}
]
}Grouping: Tracks nested within albums.
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
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
}
}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:
{
"changes": {
"WebUI.Token": null
}
}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):
{
"error": "Cannot modify protected configuration key: Settings.ConfigVersion"
}HTTP Status Codes:
200- Success400- Invalid request body403- Protected key modification attempt500- Save failure
Protected Keys:
Settings.ConfigVersion- Managed automatically by migration system
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):
{
"arrType": "radarr",
"uri": "http://localhost:7878",
"apiKey": "abc123..."
}- By instance key (when API key is redacted in the UI; backend uses stored config):
{
"arrType": "radarr",
"instanceKey": "Radarr-Movies"
}When instanceKey is present, uri and apiKey are 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):
{
"success": false,
"message": "Connection refused"
}HTTP Status Codes:
200- Connection test completed (checksuccessfield)400- Missing required fields500- Unexpected error
Use Case: Validate Arr credentials before saving config changes.
Trigger qBitrr self-update (pip or binary).
Endpoints:
POST /api/update(requires auth)POST /web/update(public)
Request: No body required
Response (Success):
{
"status": "started"
}Response (Already Running):
{
"error": "An update is already in progress."
}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.
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):
{
"error": "Download only available for binary installations"
}Response (No Update):
{
"error": "No update available"
}Response (No Binary):
{
"error": "No binary available for your platform"
}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.
All errors return JSON with an error field:
{
"error": "Error message"
}| 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 |
No rate limiting is enforced by default. External reverse proxies (e.g., Nginx, Traefik) should implement rate limiting if exposed publicly.
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";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
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:
{
"total": 150,
"page": 0,
"page_size": 50,
"movies": [ ... ]
}Calculating Pages:
const totalPages = Math.ceil(response.total / response.page_size);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.
- 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)
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']}")curl -H "Authorization: Bearer abc123..." \
http://localhost:6969/api/processescurl -X POST \
http://localhost:6969/web/processes/radarr-4k/all/restartcurl "http://localhost:6969/web/radarr/radarr-4k/movies?has_file=false&monitored=true"curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer abc123..." \
-d '{"changes": {"Settings.LoopSleepTimer": 60}}' \
http://localhost:6969/api/configcurl -X POST \
http://localhost:6969/web/update- 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
- Configuration File Reference - Manual TOML editing
- First Run Guide - Initial setup
- Troubleshooting - Common issues