REST API Reference - Will-Luck/iplayer-arr GitHub Wiki

REST API Reference

iplayer-arr exposes three API surfaces:

  1. REST API -- the primary JSON API used by the web dashboard.
  2. Newznab API -- an XML indexer interface compatible with Sonarr and other PVR clients.
  3. 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.


Authentication

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


REST API

GET /api/status

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

GET /api/system

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)

POST /api/system/geo-check

Re-run the BBC iPlayer geo check. Returns the updated result.

Response 200:

{
  "geo_ok": true,
  "geo_checked_at": "2026-04-01T12:05:00Z"
}

GET /api/events

Server-Sent Events (SSE) stream. See the SSE Events section below.

Headers:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

GET /api/downloads

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.


POST /api/download

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"
}

GET /api/history

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)

GET /api/history/stats

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 /api/history/{id}

Delete a single history entry.

Path parameters:

Parameter Description
id Download ID

Response 200:

{
  "status": "ok"
}

GET /api/config

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

PUT /api/config

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"
}

GET /api/overrides

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.


PUT /api/overrides/{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 /api/overrides/{name}

Delete an override by URL-encoded show name.

Response 200:

{
  "status": "ok"
}

GET /api/search

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"
  }
]

GET /api/downloads/directory

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 /api/downloads/directory/{folder}

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"
}

POST /api/pause

Pause the download queue.

Response 200:

{
  "paused": true
}

POST /api/resume

Resume the download queue.

Response 200:

{
  "paused": false
}

GET /api/logs

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"
  }
]

GET /health

Simple health check endpoint.

Response 200:

ok

Content-Type is text/plain.


Newznab API

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.

GET /newznab/api?t=caps

Returns the indexer capabilities XML. Used by Sonarr during indexer setup to discover supported search modes.

GET /newznab/api?t=search

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.

GET /newznab/api?t=tvsearch

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 q is empty but tvdbid is provided, the handler resolves the show name from:
    1. A stored series mapping (local cache), or
    2. Sonarr's Skyhook TVDB lookup service.
  • When q is provided but tvdbid is empty, the handler does a reverse lookup of the stored series mapping by name to recover a previously-resolved tvdbid (and the series year, if known). The request's own tvdbid parameter always wins when supplied; rehydration only runs when tvdbid is absent. This keeps the <newznab:attr name="tvdbid"> echo firing on Sonarr's episode-level follow-up queries, which arrive with q=ShowName and no tvdbid.

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.

GET /newznab/api?t=get

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.


SABnzbd API

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.

GET /sabnzbd/api?mode=version

Returns the emulated SABnzbd version.

Response:

{
  "version": "4.0.0"
}

GET /sabnzbd/api?mode=get_cats

Returns available download categories.

Response:

{
  "categories": ["sonarr", "tv", "manual"]
}

GET /sabnzbd/api?mode=get_config

Returns SABnzbd-compatible configuration including the download directory.

Response:

{
  "config": {
    "misc": {
      "complete_dir": "/downloads"
    },
    "categories": [
      { "name": "sonarr", "dir": "" },
      { "name": "tv",     "dir": "" },
      { "name": "manual", "dir": "" }
    ]
  }
}

GET /sabnzbd/api?mode=fullstatus

Returns full status including the completion directory.

Response:

{
  "status": {
    "completedir": "/downloads"
  }
}

POST /sabnzbd/api?mode=addurl

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"
}

GET /sabnzbd/api?mode=queue

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"
      }
    ]
  }
}

GET /sabnzbd/api?mode=history

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)

SSE Events

Connect to GET /api/events to receive real-time Server-Sent Events. Each event has a type field and a JSON data payload.

Event Types

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

Wire Format

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"}

Connection

The SSE endpoint holds the connection open indefinitely. The client should reconnect on disconnection. The standard EventSource browser API handles this automatically.


Common Types

Download Object

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

Error Response

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
⚠️ **GitHub.com Fallback** ⚠️