# NNTmux API v2 Specification Code-first reference for the JSON API under `/api/v2`. Primary sources: - `routes/api.php` - `app/Http/Controllers/Api/ApiV2Controller.php` - `app/Transformers/ApiTransformer.php` - `app/Transformers/DetailsTransformer.php` - `app/Transformers/CategoryTransformer.php` ## Base URL ```text https:///api/v2 ``` ## Authentication and Rate Limits - `GET /capabilities` is public. - All other v2 routes require `api_token`. - Route-level middleware uses token-aware throttling (`apiRateLimit`) and accepts `api_token` or the legacy `apikey` alias when that middleware is reused. - `POST /nzbadd` is intentionally exempt from route-level rate limiting and API request quotas, and uploads are not recorded as API usage. Authentication and posting privileges still apply. - Controller-level auth errors return a JSON error envelope: ```json { "error": "Missing parameter (api_token)" } ``` Common auth/rate-limit statuses: | HTTP | Error | |---:|---| | 400 | `Missing parameter (api_token)` | | 401 | `Incorrect user credentials` | | 403 | `Account suspended` | | 429 | `Request limit reached` | ## Common Query Parameters | Parameter | Type | Default | Notes | |---|---|---:|---| | `api_token` | string | - | Required except `capabilities`. | | `id` | string | `""` | Search text/fallback identifier on search endpoints. | | `limit` | int | `100` | Max rows in page. | | `offset` | int | `0` | Zero-based pagination offset. | | `cat` | csv string | `-1` | Category filter; `TV_WEBDL` auto-add can apply when `TV_HD` is requested. | | `group` | string | `-1` | Usenet group filter (where supported). | | `maxage` | int | `-1` | Max post age in days. Invalid values return JSON `400`. | | `minsize` | int | `0` | Min release size in bytes. | | `maxsize` | int | - | Accepted for compatibility; currently not enforced in query layer. | | `sort` | string | `posted_desc` | `cat|name|size|files|stats|posted` + `_asc|_desc`. | Sorting examples: - `/api/v2/search?api_token=&id=ubuntu&sort=posted_desc` - `/api/v2/search?api_token=&id=ubuntu&sort=name_asc` - `/api/v2/tv?api_token=&id=last+week+tonight&season=2025&ep=11/10&sort=posted_desc` - `/api/v2/movies?api_token=&imdbid=tt0816692&sort=size_desc` JSON sorting response snippet (`sort=size_desc`): ```http HTTP/1.1 200 OK Content-Type: application/json { "Total": 2, "apiCurrent": 0, "apiMax": 100, "grabCurrent": 0, "grabMax": 100, "apiOldestTime": "", "grabOldestTime": "", "results": [ { "title": "Ubuntu ISO x64", "size": 734003200 }, { "title": "Ubuntu ISO x86", "size": 367001600 } ] } ``` Releases are ordered largest-to-smallest because `sort=size_desc`. > The legacy `Results` (capital R) Fractal field has been replaced by the > lower-case `results` field. Search responses retain pagination and quota > metadata in the top-level JSON object. Movie/TV-only fields (`tvdbid`, > `imdbid`, `season`, …) are omitted when they do not apply to a release. ## Endpoints ## 1) Capabilities - `GET /capabilities` - Auth: none Returns: - `server` - `limits` - `searching` - `registration` - `categories` - `groups` - `genres` ## 2) Search - `GET /search` - Auth: required Behavior: - If `id` is present: text search. - If `id` is omitted: browse mode. - Includes API usage counters via response headers (`X-Api-Current`, `X-Api-Max`, `X-Grab-Current`, `X-Grab-Max`, `X-Api-Oldest-Time`, `X-Grab-Oldest-Time`). ## 3) TV Search - `GET /tv` - Auth: required Identifiers: - `vid`, `tvdbid`, `traktid`, `rid`, `tvmazeid`, `imdbid`, `tmdbid` Optional filters: - `season`, `ep`, `cat`, `maxage`, `minsize`, `sort`, `offset`, `limit` Daily parsing: - `season=YYYY` and `ep=MM/DD` infers an airdate query. ## 4) Movie Search - `GET /movies` - Auth: required Identifiers: - `imdbid`, `tmdbid`, `traktid` Optional filters: - `id`, `cat`, `maxage`, `minsize`, `sort`, `offset`, `limit` ## 5) Audio Search - `GET /audio` - Auth: required Required: - `id` (query string) ## 6) Book Search - `GET /books` - Auth: required Required: - `id` (query string) ## 7) Anime Search - `GET /anime` - Auth: required Selectors: - `id` and/or `anidbid` and/or `anilistid` ## 8) Get NZB - `GET /getnzb` - Auth: required - Valid GUID redirects to `/getnzb?r=&id=[&del=1]` - Not found returns HTTP `404` JSON. ## 9) Details - `GET /details` - Auth: required - Requires `id` (GUID) ## 10) Add NZB - `POST /nzbadd` - Auth: required; the user must have posting privileges (`can_post`) - Content type: `multipart/form-data` Fields: | Field | Required | Notes | |---|---|---| | `api_token` | yes | API token for a verified, enabled user. | | `nzb` | yes | Valid `.nzb` file staged for deferred import. | | `nfo` | no | `.nfo` file with any safe basename, maximum 65,535 bytes. | | `cat` | no | Echoed as response metadata; it does not affect import categorization. | When `nfo` is supplied, its basename does not need to match the NZB basename. The request is atomic: both files are validated before staging in an isolated upload directory, and a partial write is rolled back. NZB-only example: ```bash curl -X POST -F "api_token=" -F "nzb=@Release.nzb" https:///api/v2/nzbadd ``` Paired example: ```bash curl -X POST -F "api_token=" -F "cat=5040" -F "nzb=@Release.nzb" -F "nfo=@scene-info.nfo" https:///api/v2/nzbadd ``` Successful staging returns HTTP `201`: ```json { "success": true, "status": "staged", "name": "Release", "category": "5040", "files": { "nzb": { "filename": "Release.nzb", "type": "nzb" }, "nfo": { "filename": "scene-info.nfo", "type": "nfo" } } } ``` Staging does not synchronously create a release. Import the NZB files first, then import their paired NFO files using the manifest identity recorded by the NZB importer: ```bash php artisan nntmux:import-nzbs --folder=/path/to/NZB_UPLOAD_FOLDER php artisan nntmux:import-nfos --folder=/path/to/NZB_UPLOAD_FOLDER ``` The NFO importer replaces an existing NFO for the resolved release. Add `--delete` to remove successfully imported NFO payloads or `--delete-failed` to remove payloads that cannot be linked. NZB-only responses set `files.nfo` to `null`. ## Response Models ### Search Envelope (`/search`, `/tv`, `/movies`, `/audio`, `/books`, `/anime`) ```json { "Total": 123, "apiCurrent": 2, "apiMax": 1000, "grabCurrent": 1, "grabMax": 100, "apiOldestTime": "Wed, 20 Nov 2024 12:00:00 +0000", "grabOldestTime": "", "results": [] } ``` ### Details Object (`/details`) Returns a single release object (not envelope). Download field name is `link` (not `url`). ## Error Response Conventions - Missing token: JSON `400` - Invalid token: JSON `401` - Disabled account: JSON `403` - Invalid `maxage`: JSON `400` - Invalid `sort`: JSON `400` - Missing required endpoint parameter (`id`, etc.): JSON `400` - Missing GUID in `/getnzb`: JSON `404` ## Unsupported in v2 The following are intentionally not part of v2 JSON API: - `register` - `user` - `comments` - `commentadd` - `cartadd` - `cartdel` ## Postman Collection - `docs/postman/nntmux_api_v2.postman_collection.json`