REST API Reference - Will-Luck/iplayer-arr GitHub Wiki
iplayer-arr exposes three API surfaces:
- REST API -- the primary JSON API used by the web dashboard.
- Newznab API -- an XML indexer interface compatible with Sonarr and other PVR clients.
- SABnzbd API -- a download-client interface that Sonarr uses to submit and track downloads.
All responses from the REST API are application/json unless noted otherwise.
Newznab responses are application/xml. SABnzbd responses are application/json.
The API key is passed in one of two ways:
| Method | Format |
|---|---|
| Query parameter | ?apikey=<key> |
| HTTP header | Authorization: Bearer <key> |
The API key is auto-generated on first run and stored in the BoltDB database. It is visible on the Config page and in the GET /api/config response.
Note: Authentication middleware exists but is not currently enforced. iplayer-arr is designed for LAN-only deployment behind a VPN or reverse proxy. The SABnzbd compatibility layer does enforce the API key for mutating operations (queue, history, addurl).
Lightweight status snapshot used by the dashboard header.
Response 200:
{
"ffmpeg": "7.1",
"geo_ok": true,
"active_workers": 1,
"queue_depth": 3,
"paused": false,
"disk_total": 107374182400,
"disk_free": 53687091200
}| Field | Type | Description |
|---|---|---|
ffmpeg |
string | Detected FFmpeg version (empty if not found) |
geo_ok |
bool | Whether the last BBC geo check passed (UK access) |
active_workers |
int | Downloads currently in progress |
queue_depth |
int | Downloads waiting in the queue |
paused |
bool | Whether the download queue is paused |
disk_total |
int64 | Total bytes on the download volume |
disk_free |
int64 | Available bytes on the download volume |
Detailed system information for the System page.
Response 200:
{
"version": "0.1.0",
"go_version": "go1.24.1",
"uptime_seconds": 86400,
"build_date": "2026-04-01T00:00:00Z",
"geo_ok": true,
"geo_checked_at": "2026-04-01T12:00:00Z",
"ffmpeg_version": "7.1",
"ffmpeg_path": "/usr/bin/ffmpeg",
"disk_total": 107374182400,
"disk_free": 53687091200,
"disk_path": "/downloads",
"downloads_completed": 42,
"downloads_failed": 3,
"downloads_total_bytes": 21474836480,
"last_indexer_request": "2026-04-01T11:30:00Z"
}| Field | Type | Description |
|---|---|---|
version |
string | Application version |
go_version |
string | Go runtime version |
uptime_seconds |
int64 | Seconds since process start |
build_date |
string | Build timestamp |
geo_ok |
bool | BBC geo check result |
geo_checked_at |
string | When the geo check last ran (omitted if never) |
ffmpeg_version |
string | FFmpeg version string |
ffmpeg_path |
string | Absolute path to FFmpeg binary |
disk_total |
int64 | Total bytes on download volume |
disk_free |
int64 | Free bytes on download volume |
disk_path |
string | Mount path of the download directory |
downloads_completed |
int | Lifetime completed download count |
downloads_failed |
int | Lifetime failed download count |
downloads_total_bytes |
int64 | Total bytes downloaded (completed only) |
last_indexer_request |
string | Last time a Newznab client queried the indexer (omitted if never) |
Re-run the BBC iPlayer geo check. Returns the updated result.
Response 200:
{
"geo_ok": true,
"geo_checked_at": "2026-04-01T12:05:00Z"
}Server-Sent Events (SSE) stream. See the SSE Events section below.
Headers:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
List all active downloads (pending, downloading, resolving, converting).
Response 200:
[
{
"id": "abc123",
"pid": "p0abcdef",
"vpid": "m001xyz",
"title": "Show Name - S01E03",
"show_name": "Show Name",
"season": 1,
"episode": 3,
"air_date": "2026-03-15",
"identity_tier": "default",
"quality": "720p",
"status": "downloading",
"category": "sonarr",
"stream_url": "https://...",
"output_dir": "/downloads/Show Name - S01E03",
"output_file": "Show Name - S01E03.mp4",
"progress": 45.2,
"size": 524288000,
"downloaded": 237006438,
"duration": 3600,
"error": "",
"failure_code": "",
"retryable": false,
"retry_count": 0,
"created_at": "2026-04-01T10:00:00Z",
"started_at": "2026-04-01T10:00:05Z",
"completed_at": "0001-01-01T00:00:00Z"
}
]Returns an empty array [] when no downloads are active.
See the Download object section for field descriptions.
Start a manual download.
Request body:
{
"pid": "p0abcdef",
"quality": "720p",
"title": "Show Name - Episode Title",
"category": "manual"
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
pid |
string | Yes | -- | BBC programme ID |
quality |
string | No | "720p" |
Quality tag (e.g. "1080p", "720p", "480p") |
title |
string | No | -- | Display title for the download |
category |
string | No | "manual" |
Download category |
Response 200:
{
"id": "abc123"
}Error 400:
{
"error": "pid is required"
}Paginated download history with optional filtering and sorting.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
status |
string | all | Filter: "completed" or "failed"
|
since |
string | all time | ISO date or RFC 3339 timestamp |
page |
int | 1 |
1-based page number |
per_page |
int | 20 |
Entries per page |
sort |
string | "completed_at" |
Sort field: "completed_at" or "title"
|
order |
string | "desc" |
Sort direction: "asc" or "desc"
|
Response 200:
{
"items": [
{ "...": "Download object" }
],
"total": 142
}| Field | Type | Description |
|---|---|---|
items |
Download[] | Array of download objects (never null) |
total |
int | Total matching entries (before pagination) |
Aggregate statistics across download history.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
since |
string | all time | ISO date or RFC 3339 timestamp |
Response 200:
{
"completed": 42,
"failed": 3,
"total_bytes": 21474836480
}Delete a single history entry.
Path parameters:
| Parameter | Description |
|---|---|
id |
Download ID |
Response 200:
{
"status": "ok"
}Return all configuration key-value pairs.
Response 200:
{
"api_key": "abc123...",
"quality": "720p",
"max_workers": "4",
"download_dir": "/downloads",
"auto_cleanup": "false"
}| Key | Default | Read-only | Description |
|---|---|---|---|
api_key |
-- | Yes (env var) | API authentication key |
quality |
"720p" |
No | Default download quality |
max_workers |
"4" |
No | Maximum concurrent download workers (reduced from 10 in v1.1.3 to avoid BBC rate limits) |
download_dir |
"/downloads" |
Yes (env var) | Output directory for downloads |
auto_cleanup |
"false" |
No | Automatically remove completed download folders |
Update a single configuration value.
Request body:
{
"key": "quality",
"value": "1080p"
}Response 200:
{
"status": "ok"
}Error 400 (read-only key):
{
"error": "api_key is read-only (set via environment variable)"
}Error 400 (invalid max_workers):
{
"error": "max_workers must be a positive integer"
}List all show episode-numbering overrides.
Response 200:
{
"Show Name": {
"show_name": "Show Name",
"force_date_based": false,
"force_series_num": 0,
"force_position": false,
"series_offset": 0,
"episode_offset": 0,
"custom_name": ""
}
}The response is a map keyed by show name.
Create or update an override. The {name} path segment is the URL-encoded show name.
Request body:
{
"show_name": "Show Name",
"force_date_based": true,
"force_series_num": 0,
"force_position": false,
"series_offset": 0,
"episode_offset": 0,
"custom_name": "Custom Title"
}| Field | Type | Description |
|---|---|---|
show_name |
string | Show name (must match the key) |
force_date_based |
bool | Force date-based episode numbering |
force_series_num |
int | Override the series number (0 = auto) |
force_position |
bool | Use position-based numbering |
series_offset |
int | Add/subtract from detected series number |
episode_offset |
int | Add/subtract from detected episode number |
custom_name |
string | Override the show name in output filenames |
Response 200:
{
"status": "ok"
}Delete an override by URL-encoded show name.
Response 200:
{
"status": "ok"
}Search BBC iPlayer for programmes.
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
string | Yes | Search query |
Response 200:
Returns an array of Programme objects from the BBC iPlayer API. Returns [] when the query is empty or the search fails.
[
{
"pid": "p0abcdef",
"vpid": "m001xyz",
"name": "Show Name",
"episode": "Episode Title",
"series": 1,
"episode_num": 3,
"air_date": "2026-03-15",
"channel": "BBC One",
"duration": 3600,
"synopsis": "Episode synopsis text.",
"thumbnail": "https://ichef.bbci.co.uk/...",
"available": "2026-03-15T00:00:00Z",
"expires": "2026-04-15T00:00:00Z",
"qualities": [
{
"tag": "720p",
"height": 720,
"bitrate": 3500,
"fps": 25,
"stream_url": "https://...",
"supplier": "mf_akamai",
"has_subs": true,
"synthetic": false
}
],
"position": 3,
"identity_tier": "default",
"is_date_based": false,
"cached_at": "2026-04-01T12:00:00Z"
}
]List subdirectories in the download directory, with file details.
Response 200:
[
{
"name": "Show Name - S01E03",
"path": "/downloads/Show Name - S01E03",
"files": [
{ "name": "Show Name - S01E03.mp4", "size": 524288000 }
],
"total_size": 524288000,
"owned": true
}
]| Field | Type | Description |
|---|---|---|
name |
string | Directory name |
path |
string | Absolute path |
files |
object[] | Files in the directory (name + size in bytes) |
total_size |
int64 | Sum of all file sizes |
owned |
bool | Whether iplayer-arr created this directory |
Delete a download directory. Only directories created by iplayer-arr (owned) can be deleted.
Path parameters:
| Parameter | Description |
|---|---|
folder |
Directory name (not a path -- must not contain slashes) |
Response 200:
{
"deleted": "Show Name - S01E03"
}Error 403:
{
"error": "folder not owned by iplayer-arr"
}Pause the download queue.
Response 200:
{
"paused": true
}Resume the download queue.
Response 200:
{
"paused": false
}Recent log entries from the in-memory ring buffer.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
level |
string | all | Filter by level (case-insensitive): "info", "warn", "error"
|
q |
string | all | Search term (case-insensitive substring match on message) |
Response 200:
[
{
"timestamp": "2026-04-01T12:00:00Z",
"level": "info",
"message": "download p0abcdef started"
}
]Simple health check endpoint.
Response 200:
ok
Content-Type is text/plain.
The Newznab-compatible indexer is mounted at /newznab/api. Sonarr (or any Newznab client) uses this to search for available programmes and fetch NZB files that encode the programme ID and quality.
All Newznab responses are application/xml.
Returns the indexer capabilities XML. Used by Sonarr during indexer setup to discover supported search modes.
General search.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
q |
string | Search query (defaults to "BBC" if empty) |
limit |
int | Maximum results |
offset |
int | Pagination offset |
Returns an RSS feed with <item> elements containing programme metadata and NZB download links.
TV-specific search. Supports TVDB lookups via the Skyhook service.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
q |
string | Search query |
tvdbid |
string | TVDB series ID (resolves to show name via stored mapping or Skyhook lookup) |
season |
string | Season number filter |
ep |
string | Episode number filter |
Tvdbid resolution:
- When
qis empty buttvdbidis provided, the handler resolves the show name from:- A stored series mapping (local cache), or
- Sonarr's Skyhook TVDB lookup service.
- When
qis provided buttvdbidis empty, the handler does a reverse lookup of the stored series mapping by name to recover a previously-resolvedtvdbid(and the series year, if known). The request's owntvdbidparameter always wins when supplied; rehydration only runs whentvdbidis absent. This keeps the<newznab:attr name="tvdbid">echo firing on Sonarr's episode-level follow-up queries, which arrive withq=ShowNameand notvdbid.
Returns an RSS feed matching the Newznab specification. Each <item> includes a tvdbid attribute in the Newznab namespace, allowing Sonarr to match results without a separate lookup.
Download an NZB file by GUID.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
id |
string | NZB GUID (encodes the programme ID and quality) |
Returns an application/x-nzb response containing an XML document that encodes the programme PID and quality for the SABnzbd handler to parse.
The SABnzbd-compatible download client is mounted at /sabnzbd/api. Sonarr uses this interface to submit downloads and track their progress.
The mode query parameter selects the operation. Responses are JSON.
Returns the emulated SABnzbd version.
Response:
{
"version": "4.0.0"
}Returns available download categories.
Response:
{
"categories": ["sonarr", "tv", "manual"]
}Returns SABnzbd-compatible configuration including the download directory.
Response:
{
"config": {
"misc": {
"complete_dir": "/downloads"
},
"categories": [
{ "name": "sonarr", "dir": "" },
{ "name": "tv", "dir": "" },
{ "name": "manual", "dir": "" }
]
}
}Returns full status including the completion directory.
Response:
{
"status": {
"completedir": "/downloads"
}
}Submit a download by NZB URL. Requires a valid API key.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
apikey |
string | API key |
name |
string | NZB URL to download |
nzbname |
string | Display name for the download |
cat |
string | Category (default: "sonarr") |
Also accepts mode=addfile with a multipart NZB file upload.
Response (success):
{
"status": true,
"nzo_ids": ["abc123"]
}Response (error):
{
"status": false,
"error": "error message"
}Current download queue in SABnzbd format. Requires a valid API key.
Supports name=delete&value={nzo_id} to cancel a download.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
apikey |
string | API key |
name |
string | Action: "delete" to cancel a download |
value |
string | Download ID (for delete action) |
Response (list):
{
"queue": {
"status": "Downloading",
"paused": false,
"noofslots": 2,
"speed": "0",
"timeleft": "0:00:00",
"slots": [
{
"nzo_id": "abc123",
"filename": "Show Name - S01E03",
"status": "Downloading",
"percentage": "45",
"mb": "500.00",
"mbleft": "275.00",
"timeleft": "0:00:00",
"cat": "sonarr",
"size": "500.00 MB",
"sizeleft": "275.00 MB"
}
]
}
}Download history in SABnzbd format. Requires a valid API key.
Supports name=delete&value={nzo_id} to remove a history entry.
Response:
{
"history": {
"slots": [
{
"nzo_id": "abc123",
"name": "Show Name - S01E03",
"nzb_name": "Show Name - S01E03.nzb",
"status": "Completed",
"storage": "/downloads/Show Name - S01E03",
"path": "/downloads/Show Name - S01E03",
"bytes": 524288000,
"downloaded": 524288000,
"completed": 1711929600,
"download_time": 120,
"category": "sonarr",
"fail_message": "",
"action_line": "",
"script": "None"
}
]
}
}| Field | Type | Description |
|---|---|---|
status |
string |
"Completed" or "Failed"
|
storage / path
|
string | Output directory path |
completed |
int64 | Unix timestamp of completion |
download_time |
int | Download duration in seconds |
fail_message |
string | Error message (empty on success) |
Connect to GET /api/events to receive real-time Server-Sent Events. Each event has a type field and a JSON data payload.
| Event | Payload | Description |
|---|---|---|
download:progress |
Download object | Periodic progress update during download (throttled) |
download:status |
Download object | Status transition (pending, resolving, downloading, converting) |
download:complete |
Download object | Download finished successfully |
download:failed |
Download object | Download failed |
log:line |
LogEntry | New log entry added to the ring buffer |
pause:changed |
{ "paused": bool } |
Download queue paused or resumed |
cleanup:completed |
{ "removed": int } |
Auto-cleanup removed completed download folders |
event: download:progress
data: {"id":"abc123","pid":"p0abcdef","progress":45.2,...}
event: log:line
data: {"timestamp":"2026-04-01T12:00:00Z","level":"info","message":"download started"}
The SSE endpoint holds the connection open indefinitely. The client should reconnect on disconnection. The standard EventSource browser API handles this automatically.
The Download object appears in REST responses and SSE event payloads.
| Field | Type | Description |
|---|---|---|
id |
string | Unique download identifier |
pid |
string | BBC programme ID |
vpid |
string | BBC version PID |
title |
string | Display title |
show_name |
string | Series/show name |
season |
int | Season number |
episode |
int | Episode number |
air_date |
string | Original air date (ISO date) |
identity_tier |
string | BBC identity tier |
quality |
string | Quality tag (e.g. "720p") |
status |
string | Current status (see below) |
category |
string | Download category |
stream_url |
string | Source stream URL |
output_dir |
string | Output directory path |
output_file |
string | Output filename |
progress |
float64 | Download progress (0--100) |
size |
int64 | Total size in bytes |
downloaded |
int64 | Bytes downloaded so far |
duration |
int | Programme duration in seconds |
error |
string | Error message (empty on success) |
failure_code |
string | Machine-readable failure code |
retryable |
bool | Whether the download can be retried |
retry_count |
int | Number of retry attempts |
created_at |
string | When the download was enqueued (RFC 3339) |
started_at |
string | When downloading began (RFC 3339) |
completed_at |
string | When the download finished (RFC 3339) |
Status values: pending, resolving, downloading, converting, completed, failed
All error responses follow the same shape:
{
"error": "description of the problem"
}HTTP status codes used:
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request (missing/invalid parameters) |
| 403 | Forbidden (e.g. deleting an unowned directory) |
| 404 | Not found (unknown route) |
| 500 | Internal server error |