Skip to content
nexcrate
API

A fixed contract for other programs

Nexview, nexbeat and nexdeck talk to nexcrate through the same interface that is open to you: /api/v1. It stays as it is while the pages of the interface move on. This page explains how to get in, and lists every address below.

The reference describes nexcrate 0.2.0, contract v1, stage V5, 43 addresses. It is generated from nexcrate's code.

Two interfaces

nexcrate's own interface uses an API under /api, with the session cookie. It follows the pages of the interface and changes with them. Other programs use /api/v1 and nothing else.

  • /api/v1 opens only with a key, never with the interface's cookie.
  • Requests that change something need no X-Requested-With header there.
  • No key reaches indexers, download clients, profiles, the account or stored secrets. There is no address for them under /api/v1.

Every running nexcrate describes itself: /api/openapi.json is the OpenAPI document, /api/docs the page for it.

Keys and scopes

Under Settings › System › API keys press New key. Give every program its own key; then one can be revoked without disturbing the others. The key is shown exactly once; nexcrate keeps only its fingerprint.

The dialog New key with a name and the three scopes Read, Request and Operate
ScopeWhat it allows
ReadRead the library, versions, states, storage, health, the queue, problems, history, the calendar, ratings and events. Every key has it.
RequestRequest titles and more of them, watch or freeze them, have them searched, take them back and move files into the recycle bin.
OperateTry stuck downloads again, remove them or have them searched again, confirm a path and assign files by hand.

The key travels in a header, never in the address:

HTTP
Authorization: Bearer nxc_…

Pairing

A program can ask for a key instead of someone typing it in. That is how Nexview, nexbeat and nexdeck connect. The two addresses for it are the only ones under /api/v1 that need no key.

  1. AskPOST /api/v1/pairing with the program's name and the scopes it wants. The answer carries a pairing_id, a secret and a short code. The program shows the code.
  2. ConfirmIn nexcrate the request shows at the top of every page and under Settings › System › API keys in Programs that want to connect. The owner compares the code and presses Connect or Refuse; they may grant fewer scopes than asked for.
  3. CollectThe program asks GET /api/v1/pairing/{pairing_id} with the header X-Pairing-Secret, every poll_seconds. The first answer with confirmed carries the key, exactly once.
Terminal
curl -s -X POST http://nexcrate:8390/api/v1/pairing \
  -H 'Content-Type: application/json' \
  -d '{"app": "My script", "scopes": ["read", "request"]}'

The first request

Which versions there are and whether they are ready:

Terminal
curl -s http://nexcrate:8390/api/v1/versions \
  -H "Authorization: Bearer $NEXCRATE_KEY"

Request a movie in one version and have it searched right away. Asking twice gives the same, never a double; origin is your own reference and comes back on the title:

Terminal
curl -s -X POST http://nexcrate:8390/api/v1/requests \
  -H "Authorization: Bearer $NEXCRATE_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"kind": "movie", "ref": "tmdb:28503", "versions": ["<version id>"],
       "search_now": true, "origin": "my-script:1"}'

Rules of the answers

  • A title is kind plus ref. ref names its source: tmdb:, imdb:, for shows also tvdb:, for music mbid:. What concerns only one kind sits under a key with its name.
  • A field is never missing. An unknown value is null.
  • Errors are flat: {"code": "…", "message": "…", "params": {…}}. The code is stable, the message an English fallback. Every address lists its error codes in the reference.
  • Every answer carries X-Request-Id. A program may send its own; nexcrate writes it on every log line of that request.
  • Every key has a limit on requests per second. Going over it gets 429 with Retry-After.
  • Stable means: the contract changes its major number only with a break, and that number is in the address. GET /api/v1/system names the major number and the stage; a higher stage only adds.

Events and webhooks

What happens in nexcrate gets a running number. GET /api/v1/events?after=… returns what came since a number, and next_after for the next question. GET /api/v1/events/stream returns the same as Server-Sent Events. The library itself can be read the same way: GET /api/v1/titles?after=… only returns what changed.

nexcrate sends webhooks to addresses the owner enters under Settings › System › Webhooks; no key reaches there. Every delivery carries X-Nexcrate-Event, X-Nexcrate-Delivery and a signature:

HTTP
X-Nexcrate-Signature: t=<unix time>,v1=<HMAC-SHA256 of "<t>.<body>">

Verify it with the target's secret over the raw body, exactly as it arrived:

Python
import hashlib, hmac

def genuine(secret: str, header: str, body: bytes) -> bool:
    parts = dict(piece.split("=", 1) for piece in header.split(","))
    expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + body,
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

If a delivery fails, nexcrate tries again after 1, 5, 30 and 120 minutes. A new target starts at the newest event, not with everything that came before.

Reference

System and versions

Who answers, which versions there are, how much space is free behind them and what is wrong right now.

GET/api/v1/healthWhat is wrong with this nexcrateRead

Findings from what nexcrate already knows: asking sends no request anywhere and wakes no disk. Translate by code; params carry the names.

Response 200 HealthOut

FieldTypeMeaning
items *array of FindingOutThe worst first; empty when nothing is wrong.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 500 internal_error
GET/api/v1/statesThe states a version, a season or an episode can haveRead

The list is part of the contract. A later stage may add a state; a program shows one it does not know as unknown instead of failing.

Response 200 StatesOut

FieldTypeMeaning
items *array of StateOutIn the order of precedence: the first that applies wins.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 500 internal_error
GET/api/v1/storageFree space behind every versionRead

Free and total space of the disk each version's folder lies on. Never a path.

Response 200 StorageListOut

FieldTypeMeaning
items *array of StorageOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 500 internal_error
GET/api/v1/systemWhat this nexcrate is and can doRead

The version, the stage of the contract, what works per kind, and the fixed id of the installation. Ask this first: a program that needs more than this nexcrate carries can say so instead of failing.

Response 200 SystemOut

FieldTypeMeaning
app *string
version *stringThe running nexcrate.
contract *Contract
installation_id *stringFixed for this installation, whatever address it answers at. Tells a moved nexcrate from another.
scopes *array of stringWhat the key of this request may do.
capabilities *Capabilities
web_url *string | nullThe address the owner gave for reaching nexcrate, sub path included; null without one.
links *object of stringThe jumps into nexcrate's pages, to put behind web_url (or the address you reach nexcrate at): title, version, profile, download, problems, recycle_bin, calendar. They stay whatever the pages are called.
update *app__routers__v1__UpdateOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 500 internal_error
GET/api/v1/versionsList the versions and whether they are readyRead

A version is a named slot per kind, such as HD and 4K for movies. A version that is not ready does nothing by itself: offer only ready ones, and tell the owner the reasons of the others.

Parameters

FieldInTypeMeaning
kindquerystring | null

Response 200 VersionsOut

FieldTypeMeaning
items *array of VersionOutPer kind none, one or several: assume no number.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 kind_unsupported, invalid_input
  • 500 internal_error
Reference

Reading titles

The library as a whole or as the changes since a marker, one title with its seasons and episodes, its history and why something is still missing.

GET/api/v1/titlesThe library, whole or what changed since a numberRead

With after=0 the whole library in pages; afterwards ask with the next_after of the last answer and get only what changed since, and what went away. A title's number moves whenever anything of what this list says about it changes, some seconds after the change. A series carries its seasons; episodes are not in the list: fetch the season when its number moved. marker_too_old: the number is older than 90 days of removals, start over at 0.

Parameters

FieldInTypeMeaning
afterqueryintegerThe next_after of the last answer; 0 for everything.
kindquerystring | null
limitqueryinteger

Response 200 TitlesOut

FieldTypeMeaning
items *array of ChangedTitleOutTitles that are new or changed, the oldest change first.
removed *array of RemovedOutTitles that went away. Never part of a fetch from 0.
next_after *integerAsk with this next.
more *booleanTrue: ask again at once, there is more.
latest *integerThe highest number given so far.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 410 marker_too_old
  • 422 kind_unsupported, invalid_input
  • 500 internal_error
POST/api/v1/titles/lookupLook up many titles at onceRead

Up to 100 titles in one call, for a page of tiles that each need a badge. Answers in the order asked, straight from the library and without the delay of the change numbers. An entry nexcrate cannot answer carries error; the batch never fails for one entry.

Request LookupIn

FieldTypeMeaning
items *array of LookupItemIn

Response 200 LookupOut

FieldTypeMeaning
items *array of LookupItemOutIn the order asked.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 invalid_input
  • 500 internal_error
GET/api/v1/titles/series/{ref}/seasons/{season}The episodes of one seasonRead

Every episode TMDB lists for the season, in TMDB's numbers, with every version's state, size and quality. Season 0 holds the specials.

Parameters

FieldInTypeMeaning
ref *pathstring
season *pathinteger

Response 200 SeasonDetailOut

FieldTypeMeaning
season *integer
name *string | null
episodes *array of app__routers__v1__EpisodeOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found, season_not_found
  • 409 ref_ambiguous
  • 422 ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
POST/api/v1/titles/whyWhy many titles are not there yetRead

The same for up to 50 titles, in the order asked; an entry nexcrate cannot answer carries error and breaks nothing else.

Request WhyManyIn

FieldTypeMeaning
items *array of WhyItemIn

Response 200 WhyManyOut

FieldTypeMeaning
items *array of WhyManyItemOutIn the order asked.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 invalid_input
  • 500 internal_error
GET/api/v1/titles/{kind}/{ref}One title, a series with its seasonsRead

The title as the list shows it; a series also with every season and, per version, its state, numbers and size on disk. ref takes every source nexcrate knows for the kind: tmdb and imdb, for a series also tvdb.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found
  • 409 ref_ambiguous
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
GET/api/v1/titles/{kind}/{ref}/historyThe history of a titleRead

Loaded, filed (with the replaced file's quality and size), failed, deleted, restored, requested, taken back, and what programs did, newest first in pages.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring
beforequeryinteger | nullThe next_before of the last page.
limitqueryinteger

Response 200 HistoryOut

FieldTypeMeaning
items *array of HistoryItemOutNewest first. Entries from before V3 carry only kind and time.
next_before *integer | nullAsk with before set to this for older entries; null at the end.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found
  • 409 ref_ambiguous
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
GET/api/v1/titles/{kind}/{ref}/whyWhy a title is not there yetRead

Per version one reason as a code with params, the weightiest first: loading, stuck, not released yet, the last search found only releases the profile refuses, the next search, waiting out a delay. Build the sentence from the code. Asks no indexer.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found
  • 409 ref_ambiguous
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
Reference

Requesting and taking back

Request a title or more of it, ask first what there would be, freeze, have it searched, take it back and remove it.

POST/api/v1/previewAsk before a requestRequest

Whether the title is there, when it comes out, whether each version would search and load by itself, and with search a search of the indexers without loading (follow it with GET /api/v1/preview/{preview_id}). A title nexcrate does not have comes from TMDB; a series nexcrate does not have cannot be searched yet.

Request app__routers__v1_round__PreviewIn

FieldTypeMeaning
kind *string
ref *stringA title nexcrate does not have only by tmdb:.
versionsarray of string | nullFixed version ids; left out, every version of the kind.
searchbooleanAlso search the indexers, without loading, within their budget.
seriesPreviewSeriesIn | null

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 409 ref_ambiguous, search_busy, tmdb_not_configured, tmdb_token_unreadable
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, ref_not_addable, version_unknown, scope_not_for_kind, invalid_input
  • 500 internal_error
  • 502 tmdb_token_rejected, tmdb_rate_limited, tmdb_unavailable, tmdb_unreachable, tmdb_http_error
  • 504 tmdb_timeout
GET/api/v1/preview/{preview_id}What a preview's search foundRequest

Per version the release nexcrate would take, or why nothing fits. Kept 30 minutes.

Parameters

FieldInTypeMeaning
preview_id *pathstring

Response 200 PreviewResultOut

FieldTypeMeaning
preview_id *string
state *stringrunning or done.
releases *integerReleases of the title found.
indexers *array of PreviewIndexerOut
versions *array of PreviewResultVersionOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 preview_not_found
  • 422 invalid_input
  • 500 internal_error
POST/api/v1/requestsRequest a title, or request more of itRequest

One call for both: a title nexcrate lacks is added from TMDB, versions it lacks are added, and the seasons and episodes under series are switched on. Asking twice gives the same, never a double. Requesting more never switches anything off, and what the owner switched by hand stays. A version a Radarr or Sonarr connection feeds answers 409. search_now puts the title into the order of the automatic, even with its switch off, within the indexers' budget. 200 when the title was there, 201 when it came with this call.

Request RequestIn

FieldTypeMeaning
kind *string
ref *stringA new title only by tmdb:, a new album or artist by mbid: (its release group or artist).
versionsarray of string | nullFixed version ids. Needed for a movie or series; an album takes the one music version without it, an artist has none.
seriesSeriesScopeIn | nullOnly for a series; a movie answers 422.
artistArtistScopeIn | nullOnly for an artist: which albums to watch.
search_nowbooleanSearch as soon as the order of the automatic allows.
originstring | nullThe program's own reference, at most 100 printable characters; listed back with the title and its versions.

Response 200 RequestOut

FieldTypeMeaning
created *booleanTrue when the title came in with this request.
versions *array of RequestedVersionOut
albums_watchedinteger | nullAn artist: how many of its albums this request watched; null otherwise.
search *stringqueued, not_asked, nothing_wanted, or not_possible (an anime series while capabilities.anime is false; the note anime_not_supported says it).
notes *array of app__routers__v1_write__NoteOut
title *TitleOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found, version_not_found
  • 409 ref_ambiguous, version_fed_by_source, tmdb_not_configured, tmdb_token_unreadable
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, version_unknown, version_kind_mismatch, scope_not_for_kind, season_not_found, episode_not_found, track_not_found, ref_not_addable, invalid_input
  • 500 internal_error
  • 502 tmdb_token_rejected, tmdb_rate_limited, tmdb_unavailable, tmdb_unreachable, tmdb_http_error
  • 504 tmdb_timeout
DELETE/api/v1/titles/{kind}/{ref}Remove a title from the libraryRequest

As the button in nexcrate: nexcrate's own versions go, and the title when none is left. Its running downloads are stopped at their client first. With delete_files the files go into the recycle bin before; ⚠️ a title that is gone cannot take them back, so add it again first. Versions a Radarr or Sonarr connection feeds stay, and the answer is 409 title_has_source_versions.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring
delete_filesquerybooleanMove the files into the recycle bin first.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found
  • 409 ref_ambiguous, download_finished, title_has_source_versions
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
POST/api/v1/titles/{kind}/{ref}/delete-filesMove files into the recycle binRequest

The files of the scope go into the recycle bin with their subtitles; they come back with POST /api/v1/recycle-bin/{entry_id}/restore until the bin's time is up. A file of a series that holds several episodes goes whole when one of them is named.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Request app__routers__v1_write__DeleteFilesIn

FieldTypeMeaning
versionsarray of string | nullFixed version ids (v_…); without it every version of nexcrate's own.
seriesSeriesScopeIn | null
albumAlbumScopeIn | nullOnly for an album: single tracks.

Response 200 DeleteFilesOut

FieldTypeMeaning
versions *array of DeletedVersionOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found, version_not_found
  • 409 ref_ambiguous, version_fed_by_source, download_importing
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, version_unknown, version_kind_mismatch, scope_not_for_kind, season_not_found, episode_not_found, track_not_found, invalid_input
  • 500 internal_error
PUT/api/v1/titles/{kind}/{ref}/monitoringFreeze or thawRequest

Only the switches: no download stops, no file moves. Freezing a whole series also keeps seasons TMDB names later out; thawing it takes them in again.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Request MonitoringIn

FieldTypeMeaning
monitored *booleanFalse freezes: nothing is searched or upgraded, files stay. True thaws. An artist frozen is Lidarr's unmonitored artist: none of its albums is searched on its own, their switches stay.
versionsarray of string | nullFixed version ids (v_…); without it every version of nexcrate's own.
seriesSeriesScopeIn | null
artistArtistMonitoringIn | null

Response 200 MonitoringOut

FieldTypeMeaning
versions *array of MonitoredVersionOut
title *TitleOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found, version_not_found
  • 409 ref_ambiguous, version_fed_by_source
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, version_unknown, version_kind_mismatch, scope_not_for_kind, season_not_found, episode_not_found, track_not_found, invalid_input
  • 500 internal_error
POST/api/v1/titles/{kind}/{ref}/searchAsk for a searchRequest

The title goes into the order of the automatic, right after the replacements, even with its switch off, and within the indexers' budget: a hundred asks at once are searched one after the other.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Response 202 app__routers__v1_write__SearchOut

FieldTypeMeaning
search *stringqueued, running (a search of it runs already), nothing_wanted, or not_possible (an anime series while capabilities.anime is false).

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found
  • 409 ref_ambiguous
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
POST/api/v1/titles/{kind}/{ref}/withdrawTake a request backRequest

Watching off for the scope, running downloads that lie wholly in it stopped at their client (never blocklisted), and with delete_files the files moved into the recycle bin. A download being filed away cannot be stopped: then 409 download_finished and nothing changed. A title or version a program brought and that is empty afterwards goes.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Request WithdrawIn

FieldTypeMeaning
versionsarray of string | nullFixed version ids (v_…); without it every version of nexcrate's own.
seriesSeriesScopeIn | null
delete_filesbooleanAlso move the files of the scope into the recycle bin.

Response 200 WithdrawOut

FieldTypeMeaning
title_removed *booleanA title a program added goes when nothing of it is watched and no file is left. An album stays in its artist's catalogue; an artist a program added goes when none of its albums is left.
versions *array of WithdrawnVersionOut
albumsarray of WithdrawnAlbumOut | nullAn artist: what happened per album.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 title_not_found, version_not_found
  • 409 ref_ambiguous, version_fed_by_source, download_finished, download_importing
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, version_unknown, version_kind_mismatch, scope_not_for_kind, season_not_found, episode_not_found, track_not_found, invalid_input
  • 500 internal_error
Reference

Downloads

What is loading, what is stuck, what failed, and what a program may do about it.

POST/api/v1/downloads/{download_id}/assignAssign the files of a download by handOperate

A series: each file with its episodes in TMDB's numbers, filed in the background; a file with no episodes is not filed, a file left out keeps its decision. A movie with several videos: exactly one file, the one to file. Replacing a file that is not worse needs confirm: ["not_better"].

Parameters

FieldInTypeMeaning
download_id *pathinteger

Request app__routers__v1_back__AssignIn

FieldTypeMeaning
files *array of AssignFileIn
albumAssignAlbumIn | nullOnly for an album download.
confirmarray of "not_better"Replace a file that is not worse all the same, as the owner may.

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed, download_not_assignable, download_busy, assignment_not_better, assignment_covers_more, download_files_changed, download_not_choosable
  • 422 episode_not_found, episode_twice, scope_not_for_kind, invalid_input, track_not_found, release_not_of_album, track_not_in_release, track_twice, release_fixed
  • 500 internal_error
POST/api/v1/downloads/{download_id}/clearTake a failed download off the problemsOperate

It stays in the history, and its release stays on the blocklist.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed, download_not_failed
  • 422 invalid_input
  • 500 internal_error
POST/api/v1/downloads/{download_id}/confirm-mappingConfirm the proposed path of a downloadOperate

Stores the proposal of path_not_found with the download client and files the download away again.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed, mapping_not_proposed
  • 422 invalid_input
  • 500 internal_error
GET/api/v1/downloads/{download_id}/filesThe files of a stuck downloadRead

Every video with what nexcrate read from its name and decided; for a series every episode in TMDB's numbers with the file the version holds for it. name is relative to the download, never a path of the disk.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 422 invalid_input
  • 500 internal_error
POST/api/v1/downloads/{download_id}/finishFile nothing more of a downloadOperate

The download counts as filed with what is filed; the rest is not filed.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed, download_not_assignable, download_busy
  • 422 invalid_input
  • 500 internal_error
POST/api/v1/downloads/{download_id}/removeRemove a downloadOperate

The download client drops the job (SABnzbd with its files, a torrent without). With search_again the release goes on the title's blocklist and the title into the order of the automatic, even with its switch off. Filed files are never touched.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed, download_finished
  • 422 invalid_input
  • 500 internal_error
POST/api/v1/downloads/{download_id}/retryTry a download againOperate

A problem the client reported goes back to the client, any other to filing away.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed, download_not_retryable
  • 422 invalid_input
  • 500 internal_error
POST/api/v1/downloads/{download_id}/searchSearch the title of a download againOperate

The title goes into the order of the automatic, even with its switch off, within the indexers' budget. A failed download is taken off the problems with it.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Response 200 ActOut

FieldTypeMeaning
download *QueueItemOutThe download as it stands after the action.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 409 action_not_allowed
  • 422 invalid_input
  • 500 internal_error
GET/api/v1/foreign-jobsJobs in nexcrate's category that no download followsRead

Put there by hand, by another program, or handed over without an answer and never matched; read from the clients once a minute. nexcrate never imports them by itself. The owner assigns one to a movie in the interface; a program can remove one. Since 22.09.2026.

Response 200 ForeignJobsV1Out

FieldTypeMeaning
items *array of ForeignJobV1Out

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 500 internal_error
POST/api/v1/foreign-jobs/{job_id}/importAssign a foreign job to a title of the library and import itOperate

As in the interface: the job becomes a download of the version and is filed away when finished. Only titles of the library; a series job is for the episodes its name names. Needs the scope operate. Since 22.09.2026.

Parameters

FieldInTypeMeaning
job_id *pathinteger

Request ForeignAdoptV1In

FieldTypeMeaning
kind *"movie" | "series" | "album"
ref *stringThe title as everywhere in /api/v1, for example tmdb:603.
version_id *stringThe public id of the version definition.

Response 201 ForeignAdoptV1Out

FieldTypeMeaning
download_id *integerThe download the job became; follow it in the queue.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 not_found, title_not_found
  • 409 foreign_job_failed, foreign_same_release, foreign_no_version, foreign_no_episodes, version_owned_by_source
  • 422 invalid_input, version_unknown, version_kind_mismatch
  • 500 internal_error
POST/api/v1/foreign-jobs/{job_id}/removeRemove a foreign job with its filesOperate

The job leaves the download client with its files. Needs the scope operate.

Parameters

FieldInTypeMeaning
job_id *pathinteger

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 not_found
  • 422 invalid_input
  • 500 internal_error
  • 502 client_unreachable
GET/api/v1/problemsDownloads with a problem, and what may be doneRead

The downloads of the queue with a problem or a hint, those that need somebody first. Each problem names its allowed actions and which of them an automatic may take without a human.

Parameters

FieldInTypeMeaning
kindquerystring | null

Response 200 QueueOut

FieldTypeMeaning
items *array of QueueItemOutNewest first.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 kind_unsupported, invalid_input
  • 500 internal_error
GET/api/v1/queueWhat is loading, stuck or failedRead

Every download that is not finished, one entry per download with its episodes and one state: running, stuck, and failed ones that wait for the owner. Progress and remaining time are only here, never events.

Parameters

FieldInTypeMeaning
kindquerystring | null

Response 200 QueueOut

FieldTypeMeaning
items *array of QueueItemOutNewest first.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 kind_unsupported, invalid_input
  • 500 internal_error
GET/api/v1/queue/{download_id}One downloadRead

A download as the queue lists it, also after it finished.

Parameters

FieldInTypeMeaning
download_id *pathinteger

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 download_not_found
  • 422 invalid_input
  • 500 internal_error
Reference

Events

What happened, as a list with a running number or as a stream. Webhooks send the same.

GET/api/v1/eventsWhat happened since a numberRead

Events after after, oldest first; ask again with next_after. Start with the latest of a first call. Events stay 90 days; a number older than that answers 410 marker_too_old: read the library again. Types: title.added, title.changed, title.removed, version.added, version.removed, download.started, download.state, download.imported, download.failed, download.removed, problem.opened, problem.closed, file.deleted, file.restored, request.made, request.withdrawn, series.season_complete, version_definition.added, version_definition.changed, version_definition.removed, health.changed, source.taken_over, source.takeover_undone.

Parameters

FieldInTypeMeaning
afterqueryintegerThe next_after of the last answer.
limitqueryinteger

Response 200 EventsOut

FieldTypeMeaning
items *array of EventOutOldest first.
next_after *integerAsk with this next.
more *booleanTrue: ask again at once.
latest *integerThe highest number given so far.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 410 marker_too_old
  • 422 invalid_input
  • 500 internal_error
GET/api/v1/events/streamThe events as a streamRead

The same events as Server-Sent Events: id is the number, event the type, data the event as JSON; a comment every 15 seconds keeps proxies from closing. One way only: nexcrate never needs to reach the program. After a break reconnect with the header Last-Event-ID or after. A stream ends after an hour. At most 5 open streams per key (too_many_streams).

Parameters

FieldInTypeMeaning
afterqueryinteger | nullThe number to follow; the latest when left out.
Last-Event-IDheaderstring | null

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 410 marker_too_old
  • 422 invalid_input
  • 429 too_many_streams
  • 500 internal_error
Reference

Calendar and ratings

What comes out when, and the ratings from IMDb, Rotten Tomatoes and Metacritic.

GET/api/v1/calendarWhat comes out in a span of daysRead

Movies with their cinema, digital and disc dates, episodes on the day they air, at most 100 days at once. A movie's date is the one of the region (the one set in nexcrate, or region); without a date there, the earliest anywhere, and country names it.

Parameters

FieldInTypeMeaning
from *querystringYYYY-MM-DD.
to *querystringYYYY-MM-DD.
kindquerystring | null
regionquerystring | nullISO 3166-1; left out, the one set.
monitoredquerybooleanOnly what a version watches.
missingquerybooleanOnly what has no file yet.

Response 200 CalendarOut

FieldTypeMeaning
from *string
to *string
region *stringThe region the movie dates come from; empty is the earliest date anywhere.
items *array of CalendarEntryOutBy day.
truncated *booleanThe span held more entries than one answer carries: ask a shorter one.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 kind_unsupported, invalid_input
  • 500 internal_error
POST/api/v1/ratingsIMDb ratings of many titles at onceRead

Up to 100 titles, also titles that are in no library, from nexcrate's own copy of IMDb's daily file; Rotten Tomatoes and Metacritic from the 30-day store of OMDb answers. A tmdb: title nexcrate does not have is looked up at TMDB once for its IMDb number (kept 30 days); nothing else leaves. Information courtesy of IMDb (https://www.imdb.com). Used with permission.

Request RatingsIn

FieldTypeMeaning
items *array of RatingItemIn

Response 200 RatingsOut

FieldTypeMeaning
items *array of RatingItemOutIn the order asked.
imdb *stringloaded, off (the owner switched it off) or not_loaded (not loaded yet).
attribution *stringShow it where the ratings show.
omdb_attribution *string | nullShow it too when any Rotten Tomatoes or Metacritic value shows; null when none does.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 invalid_input
  • 500 internal_error
GET/api/v1/ratings/{kind}/{ref}The ratings of one titleRead

IMDb from nexcrate's copy; with an OMDb key of the owner also Rotten Tomatoes and Metacritic (movies only), kept 30 days and within OMDb's daily limit. Information courtesy of IMDb (https://www.imdb.com). Used with permission.

Parameters

FieldInTypeMeaning
kind *pathstring
ref *pathstring

Response 200 RatingOut

FieldTypeMeaning
kind *string
ref *string
imdb_ref *string | null
imdb *ImdbRatingOut | null
rotten_tomatoes *integer | nullThe Tomatometer in percent.
metacritic *integer | nullThe Metascore, 0 to 100.
sources *object of stringimdb: loaded, off or not_loaded. omdb: ok, not_found, no_key, key_invalid, limit (the day's requests are used up), failed, or not_for_kind (OMDb has no such values for series).
attribution *array of string

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 imdb_unknown
  • 409 ref_ambiguous
  • 422 kind_unsupported, ref_invalid, ref_source_unknown, invalid_input
  • 500 internal_error
Reference

Recycle bin

What was replaced or deleted and can still be brought back.

GET/api/v1/recycle-binRead the recycle binRead

What was deleted, by whom, and whether it can come back: movies, series and albums, one entry per file.

Parameters

FieldInTypeMeaning
kindquerystring | null

Response 200 RecycleBinOut

FieldTypeMeaning
items *array of RecycleEntryOutNewest first.

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 422 kind_unsupported, invalid_input
  • 500 internal_error
POST/api/v1/recycle-bin/{entry_id}/restoreTake a file back out of the recycle binRequest

The file goes back where it was and counts again. Refused when something lies there (recycle_target_taken), when the version or episode has another file by now (recycle_slot_taken), when the title or version left the library (recycle_title_gone) or when the file is gone (recycle_file_gone); the file then stays in the bin.

Parameters

FieldInTypeMeaning
entry_id *pathinteger

Response 200 RestoredOut

FieldTypeMeaning
title *TitleOut

Error codes

  • 401 api_key_missing, api_key_invalid
  • 403 scope_missing
  • 404 not_found
  • 409 recycle_target_taken, recycle_slot_taken, recycle_title_gone, recycle_file_gone, version_fed_by_source
  • 422 invalid_input
  • 500 internal_error
Reference

Pairing

A program asks for a key, the owner confirms in nexcrate. The only addresses without a key.

POST/api/v1/pairingAsk to be connectedno key

Needs no key. nexcrate shows the request with its code to the owner, who confirms it with the scopes of the owner's choice or denies it. Ask GET /api/v1/pairing/{pairing_id} with the secret until it is decided; a request lives 10 minutes, at most 5 wait at once.

Request PairingIn

FieldTypeMeaning
app *stringThe program's name, shown to the owner.
scopes *array of stringread, request, operate; the owner may choose fewer.

Response 201 PairingOut

FieldTypeMeaning
pairing_id *string
secret *stringSend it as X-Pairing-Secret when asking how the request stands. Shown once.
code *stringShow it to the owner: nexcrate shows the same.
expires_at *string
poll_seconds *integerAsk again after this many seconds.

Error codes

  • 403 csrf_header_missing
  • 422 invalid_input
  • 429 pairing_limit
  • 500 internal_error
GET/api/v1/pairing/{pairing_id}How a request to be connected standsno key

With the header X-Pairing-Secret. Confirmed, the answer carries the key once; from then on the request is delivered. An unknown request and a wrong secret both answer 404.

Parameters

FieldInTypeMeaning
pairing_id *pathstring
X-Pairing-Secretheaderstring | null

Response 200 PairingStateOut

FieldTypeMeaning
state *stringpending, confirmed (this answer carries the key), delivered, denied or expired.
key *string | nullThe key, exactly once: in the first answer after the owner confirmed.
scopes *array of string | nullWhat the owner granted, with the key.
expires_at *string

Error codes

  • 404 pairing_not_found
  • 422 invalid_input
  • 500 internal_error