Skip to content

API reference

Trackarr exposes three surfaces:

  1. Tracker endpointsapps/tracker (Go). Public, anonymous, BEP-spec'd. /announce over HTTP and UDP.
  2. REST APIapps/api (Nitro). Everything under /api/*. Session-cookie auth.
  3. Torznab + RSS — feed-style endpoints for *Arr clients and feed readers, keyed by passkey or Torznab API key.

The lists below are curated, not exhaustive: they cover the surfaces an integrator or an operator reaches for, with the notes a generator cannot write. The complete, always-current inventory is generated from the route tree itself and served by the running instance:

GET /api/docs/openapi.json      # every path, method, guard and shared schema

apps/api/scripts/generate-openapi.mjs derives it from the filenames, so a path cannot exist without appearing there, nor appear without existing. Whole subsystems (messaging, federation, themes, presentation templates, tickets, site statistics) are documented in their own guides and in that document rather than duplicated here.

Auth labels: public (anonymous), user (any authenticated user), mod (canModerate permission), admin (admin role), passkey (announce-style, passkey in URL).

Tracker endpoints

The Go tracker speaks two protocols off the same backend.

HTTP announce (BEP 3) — GET /announce

GET /announce?info_hash=...&peer_id=...&port=...
            &uploaded=...&downloaded=...&left=...
            &event=started|completed|stopped&numwant=...&compact=1

Passkey is read from the path (/announce/PASSKEY) or the ?passkey= query parameter. Response is the standard bencoded peer list.

UDP announce (BEP 15) — :6969/udp

Stateless connect-then-announce. Passkey extracted from the BEP 41 URL_DATA option. See UDP Tracker for the wire format, connection-id HMAC scheme, and reverse-proxy caveats.

No /scrape, no wss://

The tracker does not currently implement a multi-hash scrape endpoint or the WebTorrent (WebSocket) announce path. Don't advertise a wss:// URL or a scrape URL in .torrent files — clients will hang.

Public (no auth)

MethodPathPurpose
GET/api/healthReadiness probe, public and unauthenticated. Returns 200 {status, uptime, checks:{postgres,redis}} with per-component latency — it reports on dependencies, so treat it as a liveness signal you are choosing to publish.
GET/api/brandingSite title, tagline, logo URL, favicon URL, feature flags.
GET/api/runtime-configPublic Nuxt runtime config (announce URLs, etc.) for the static SPA.
GET/api/announcementOperator-set homepage banner (text + level).
GET/api/homepage-contentFeatured rails, custom blocks (operator-curated).
GET/api/categoriesHierarchical category tree.
GET/api/tagsTag master list (autocomplete source).
GET/api/upload-rulesPublic upload constraints (max size, allowed extensions, …).
GET/api/stats/publicSwarm counters (peers, seeders, torrents, users) + UDP toggle matrix.
GET/api/tracker-healthTracker availability matrix (HTTP up/down, UDP up/down).
GET/api/torrentsBrowse torrents (page, limit, category, tags, q, sort).
GET/api/torrents/:hashTorrent detail (metadata + swarm + comments).
GET/api/users/:idPublic profile (display name, role badges, redacts last-seen if opted out).
GET/api/users/:id/uploadsPaginated list of accepted torrents from this user.

Authentication

Zero-Knowledge: the password never leaves the browser. See the Zero-Knowledge Auth guide.

MethodPathAuthPurpose
GET/api/auth/powpublicIssue a Proof-of-Work challenge for the registration form.
POST/api/auth/registerpublicCreate a user — body: { username, salt, verifier, invite?, pow }.
GET/api/auth/challengepublicReturn { salt, challenge } keyed by username for the ZK login proof.
POST/api/auth/loginpublicVerify { username, proof, challenge } and open a sealed-cookie session.
POST/api/auth/logoutuserRevoke the current session.
GET/api/auth/statuspublicReturns current user + locale + theme (or null if anonymous).
PUT/api/auth/passworduserAtomic password rotation: prove current, ship new { salt, verifier }.
GET/api/auth/passkeypublicWebAuthn assertion options for first-factor passkey login.
POST/api/auth/passkeypublicVerify a WebAuthn assertion and open a session.

Two-factor (during sign-in)

MethodPathAuthPurpose
POST/api/auth/2fa/passkey-optionsuser (1FA)WebAuthn options for the second-factor step.
POST/api/auth/2fa/passkey-verifyuser (1FA)Verify the second-factor passkey, finalise the session.
POST/api/auth/2fa/verify-totpuser (1FA)Verify a 6-digit TOTP code (with optional trust_device).

Two-factor enrolment (logged in)

MethodPathAuthPurpose
GET/api/me/2fa/statususerTOTP enabled? Passkey list? Trust-device flag?
POST/api/me/2fa/totp/setupuserGenerate a TOTP secret + recovery codes.
POST/api/me/2fa/totp/enableuserConfirm with a 6-digit code → activates TOTP.
POST/api/me/2fa/totp/disableuserDisable TOTP (requires a fresh 6-digit code).
POST/api/me/2fa/recovery-codes/regenerateuserRotate the recovery code set.
POST/api/me/2fa/passkey/register-challengeuserWebAuthn attestation options to add a passkey.
POST/api/me/2fa/passkey/register-verifyuserVerify and store a new passkey.
DELETE/api/me/2fa/passkey/:iduserRemove a passkey by id.
GET/api/me/2fa/sessionsuserList trusted devices.
DELETE/api/me/2fa/sessions/:iduserRevoke a trusted device.
PUT/api/me/2fa/trust-devicesuserToggle the "trust this browser for 30 days" flag.

Profile (/me)

MethodPathAuthPurpose
GET/api/meuserFull profile snapshot (KPIs, role badges, bio, locale, theme).
PATCH/api/meuserUpdate display name, bio, locale, theme, last-seen privacy toggle.
GET/api/me/downloadsuser.torrent downloads + per-download HnR state.
GET/api/me/seedsuserActive seeds with live swarm stats.
GET/api/me/bonus-historyuserLedger of credited bonus points (paginated, oldest → newest).
GET/api/users/hnruserCurrent user's HnR violations (shortcut).

Notifications

The in-app inbox is always-on; external channels are opt-in per user.

MethodPathAuthPurpose
GET/api/me/notificationsuserPaginated inbox.
GET/api/me/notifications/streamuserServer-Sent Events stream — live push to open tabs (Redis pub/sub fanout).
POST/api/me/notifications/:id/readuserMark a single row as read.
POST/api/me/notifications/read-alluserMark every row as read.
GET/api/me/notification-channelsuserChannels configured for this user (and their state).
PUT/api/me/notification-channels/:typeuserConfigure / update a channel (encrypted at rest).
DELETE/api/me/notification-channels/:typeuserDisable a channel.
POST/api/me/notification-channels/:type/testuserSend a test notification through this channel.
PUT/api/me/notification-routinguserMap {notification_type → channel_type} for fan-out.

See Notifications for the channel types (SMTP, Telegram, Discord, ntfy, Gotify, Pushover, Slack, Mattermost, webhook, Apprise, Web Push).

Torrents

MethodPathAuthPurpose
GET/api/torrentsuserThe flat listing. Filters: search, categoryId, tag, tagGroups, imdbid/tmdbid/tvdbid, uploader, year, season, episode, minSeeders, freeleech, notTaken, hideSuperseded, favorites, `since=24h
GET/api/torrents/groupsuserThe same query, grouped by work — one row per film / series / album, with its scopes. `sources=local
GET/api/torrents/groupuserOne work's releases, bucketed by season / episode / integral.
GET/api/torrents/facetsuserCounts for the current query: categories, tags per family, years, member options, plus what each dropped criterion would return. Cached 20 s per member.
POST/api/torrentsuserUpload (multipart: .torrent + metadata + optional NFO).
POST/api/torrents/checkuserPre-flight infohash lookup before opening the upload form.
GET/api/torrents/:hashpublicDetail page payload.
PATCH/api/torrents/:hashuploader / modEdit title / description / tags / external IDs.
DELETE/api/torrents/:hashuploader / modSoft-delete.
GET/api/torrents/:hash/downloaduser.torrent with the user's passkey baked into the announce list.
PUT/api/torrents/:hash/tagsuploader / modReplace the tag set.
POST/api/torrents/:hash/commentsuserPost a comment.
DELETE/api/torrents/comments/:idauthor / modDelete a comment.
GET/api/torrents/:hash/moderation/messagesthread†Moderation chat — {status, messages}. 404 if not allowed.
POST/api/torrents/:hash/moderation/messagesthread†Reply without a status change.
GET/api/torrents/:hash/commentsuserOlder comments, by cursor (before + beforeId, limit ≤ 50). The detail payload carries the first twenty and the total.
GET/api/torrents/:hash/my-obligationuserThe caller's own seed obligation for this release, or null when Hit & Run is off.
GET/api/torrents/:hash/stats-historyuserSeven days of swarm counts, one point per day the collector ran.
GET/api/torrents/:hash/cross-seedsuserSibling torrents that share this content signature (up to 50).
GET/api/torrents/:hash/cross-seed-statsuserCross-seed KPI bundle (peer overlap + volume share). Memoised 30 s.
POST/api/torrents/:hash/favoriteuserStar (idempotent).
DELETE/api/torrents/:hash/favoriteuserUnstar (silent if not starred).
GET/api/debug/:hashmodRaw row + per-peer swarm dump.

thread = uploader of the row OR any staff member.

Favorites

MethodPathAuthPurpose
GET/api/me/favoritesuserPaginated catalogue, sort recent / name / seeders.

Follows (social graph)

MethodPathAuthPurpose
POST/api/users/:id/followuserSubscribe to a user (idempotent, refuses self-follow).
DELETE/api/users/:id/followuserUnsubscribe (silent if not following).
GET/api/me/followinguserPaginated cast list, sort recent / alpha.

The public /api/users/:id payload carries followersCount and viewerFollowing so the profile page renders the toggle state in one round-trip.

Reports (user-side)

MethodPathAuthPurpose
POST/api/reportsuserSubmit a report ({ target_type, target_id, reason, details? }). See Reports.
GET/api/me/reportsuserList the caller's filed reports + current status.
DELETE/api/me/reports/:iduserWithdraw a pending report (hard-delete; refused once staff has acted).

Upload requests (bounty board)

MethodPathAuthPurpose
GET/api/requestsuserPaginated list. Filters: status, mine, categoryId, search.
POST/api/requestsuserCreate. Reward (if any) debited atomically from bonus_points.
GET/api/requests/:iduserDetail + thread + fill-attempt history + viewer permissions.
PATCH/api/requests/:idrequesterEdit while requested. Reward bump-only.
DELETE/api/requests/:idrequesterCancel + refund. Only while requested.
POST/api/requests/:id/filluser (not requester)Propose a torrent ({ infoHash } or { torrentId }). Enforces uploader = caller, category match, per-user cap.
POST/api/requests/:id/validaterequesterAccept the fill + pay the filler.
POST/api/requests/:id/rejectrequesterBounce the fill; status → requested; filler attempts counter ticks.
POST/api/requests/:id/commentsuserAppend to the discussion (locked when validated/cancelled).
PATCH/api/requests/:id/comments/:cidauthor (15 min)Edit own comment within the grace window.
DELETE/api/requests/:id/comments/:cidauthor (15 min) / modSoft-delete (body hidden, row stays).

See Upload Requests for the full lifecycle, point ledger, and operator settings.

Anti-cheat (staff)

MethodPathAuthPurpose
GET/api/mod/anti-cheat/flagsmodPaginated queue. Filters: status, kind, userId.
PUT/api/mod/anti-cheat/flags/:idmodStamp a review verdict + optional note. See Anti-cheat.

Moderation

MethodPathAuthPurpose
GET/api/mod/dashboardmodCounters: pending uploads, pending reports, HnR violations, …
GET/api/mod/torrents/pendingmodPending upload queue.
POST/api/mod/torrents/:hash/approvemodMove row → accepted. Optional message.
POST/api/mod/torrents/:hash/request-changesmodMove row → changes_requested. Message required.
POST/api/mod/torrents/:hash/rejectmodMove row → rejected. Message required.
POST/api/mod/torrents/:hash/resetmodOnly path out of rejected. Message required.

Forum

MethodPathAuthPurpose
GET/api/forum/categoriespublicForum structure.
GET/api/forum/categories/:idpublicCategory + paginated topics.
POST/api/forum/categoriesadminCreate a category.
PUT/api/forum/categories/:idadminEdit a category.
DELETE/api/forum/categories/:idadminRemove a category.
POST/api/forum/topicsuserCreate a topic.
GET/api/forum/topics/:idpublicTopic + paginated posts.
DELETE/api/forum/topics/:idauthor / modDelete topic.
PUT/api/forum/topics/:id/pinmodToggle pin.
PUT/api/forum/topics/:id/lockmodToggle lock.
POST/api/forum/postsuserReply in a topic.
PATCH/api/forum/posts/:idauthor / modEdit a post.
DELETE/api/forum/posts/:idauthor / modDelete a post.
GET/api/forum/statspublicAggregate stats (topic count, post count, latest reply).

Bonus economy & shop

MethodPathAuthPurpose
GET/api/bonus/activepublicActive freeleech / silverleech / custom events.
GET/api/shop/itemsuserShop catalogue.
POST/api/shop/buyuserPurchase an item ({ item_id }). Transactional against users.bonus_points.

Invitations

MethodPathAuthPurpose
GET/api/invitesuserMy invite codes (remaining quota + outstanding codes).
POST/api/invitesuserGenerate a code (decrements users.invites_remaining).
DELETE/api/invites/:iduserRevoke an unused code.

Metadata lookup

Filename → external metadata (TMDb / IGDB / Open Library / Google Books). Locale-aware via the user's profile setting.

MethodPathAuthPurpose
GET/api/metadata/searchuserMulti-source search (auto-routes by category type).
GET/api/metadata/lookupuserResolve a known external id to a normalised payload.
POST/api/metadata/tintuserStore the dominant colour of a work's poster ({source, id, tint: "R G B"}), computed by the browser on the detail page and reused by the catalogue's cards. Accepted only for a work the metadata cache already holds; the answer never says whether it did.

A missing provider key returns 503 only on the routes that need that source — the others keep serving. See Metadata providers.

Torznab & RSS

Exposed for *Arr clients (Sonarr / Radarr / …) and feed readers.

MethodPathAuthPurpose
GET / HEAD/api/torznab/apitorznab keyNewznab/Torznab dispatcher (`?t=caps
GET/api/torznab/downloadtorznab keyDownload a .torrent keyed by r=<torznab-key>.
GET/api/rss/latestpublicRecent torrents (Atom feed).
GET/api/rss/category/:slugpublicCategory-scoped feed.

See Torznab integration for the *Arr-client wiring.

Admin

The admin surface is gated by the canAccessAdmin permission. Auth = admin below.

Users & roles

MethodPathPurpose
GET/api/admin/usersPaginated registry with KPI strip + filters.
POST/api/admin/users/:id/banBan (auto-banlist the last-known IP). Accepts an optional duration (days).
POST/api/admin/users/:id/unbanUnban (does not auto-remove the IP banlist row). Refuses a mod trying to lift an admin-issued ban.
PUT/api/admin/users/:id/roleDeprecated. Use the role-attach endpoints below.
GET/api/admin/users/:id/rolesList a user's roles.
POST/api/admin/users/:id/rolesAttach a role.
DELETE/api/admin/users/:id/roles/:roleIdDetach a role.
POST/api/admin/users/:id/bonus-pointsAdd or subtract bonus points with an audit-logged reason.
GET/api/admin/rolesRole catalogue.
POST/api/admin/rolesCreate a role.
PUT/api/admin/roles/:idEdit role (name, permissions, auto-assignment rules).
DELETE/api/admin/roles/:idRemove a role.
POST/api/admin/roles/recomputeRe-evaluate every user against auto-assignment rules.

See Roles & Permissions.

Settings, branding, system

MethodPathPurpose
GET/api/admin/settingsAll site settings (title, tagline, force-2FA scope, retention, request auto-validate timeout, request fill cap, …).
PUT/api/admin/settingsSave settings (any subset; missing keys are left untouched).
POST/api/admin/logoUpload the site logo (multipart).
POST/api/admin/faviconUpload the favicon (multipart).
GET/api/admin/statsLive aggregate stats (users, torrents, peers, DB size).
GET/api/admin/stats/historyTime-series for the admin charts.
GET/api/admin/system/versionCurrent version + commit + runtime label.
GET/api/admin/system/updateLatest release available on GitHub (queries TRACKARR_REPO).

See Branding.

Categories & tags

MethodPathPurpose
POST/api/admin/categoriesCreate a category.
PUT/api/admin/categories/:idEdit a category.
DELETE/api/admin/categories/:idRemove a category.
POST/api/admin/categories/seedSeed the default category tree (idempotent — skips existing rows).
POST/api/admin/categories/:id/mergeFold a category into another ({into}): torrents, sub-categories, saved searches, upload requests, upload-rule patterns and the federated map all follow, then the source is deleted. Refused when the two differ on the adult flag, when the target descends from the source, or when a category with children would land under a non-root.
POST/api/admin/tagsCreate a tag.
DELETE/api/admin/tags/:idDelete a tag (unbinds it from every torrent in the same transaction).

Search misses

What members looked for and the catalogue does not have. Recorded only for a text-only search, on the first page, that returned nothing — never who searched.

MethodPathPurpose
GET/api/admin/search-missesMost-asked first, with a count and when it was last asked.
DELETE/api/admin/search-missesClear one ({query}) or, with no body, the whole list.

Upload rules

MethodPathPurpose
GET/api/admin/upload-rulesRead rules (NFO requirement, description min length, regex patterns).
PUT/api/admin/upload-rulesUpdate rules (atomic).

See Upload Rules.

Bonus rules & events

MethodPathPurpose
GET/api/admin/bonus-rulesRead all rules + tier sets.
PATCH/api/admin/bonus-rules/rule/:kindConfigure a rule (seeding, first_seeder, daily_login, …).
POST/api/admin/bonus-rules/tiers/seed-countAdd a seeder-count tier.
PATCH/api/admin/bonus-rules/tiers/seed-count/:idEdit a seeder-count tier.
DELETE/api/admin/bonus-rules/tiers/seed-count/:idRemove a tier.
POST/api/admin/bonus-rules/tiers/ageAdd a torrent-age tier.
PATCH/api/admin/bonus-rules/tiers/age/:idEdit an age tier.
DELETE/api/admin/bonus-rules/tiers/age/:idRemove an age tier.
GET/api/admin/bonus-eventsList scheduled events.
POST/api/admin/bonus-eventsCreate a Freeleech / Silverleech / custom event.
PATCH/api/admin/bonus-events/:idEdit an event.
DELETE/api/admin/bonus-events/:idCancel an event.
POST/api/admin/bonus-events/:id/togglePause / resume a running event.
GET/api/admin/shop/itemsShop catalogue (admin view).
POST/api/admin/shop/itemsAdd a shop item.
PATCH/api/admin/shop/items/:idEdit a shop item.
DELETE/api/admin/shop/items/:idRemove a shop item.

HnR

MethodPathPurpose
GET/api/admin/hnrList violations.
PUT/api/admin/hnr/:idMark exempt / cleared / unresolved.

See Hit-and-Run.

Banned IPs

MethodPathPurpose
GET/api/admin/banned-ipsList banlist entries.
POST/api/admin/banned-ipsAdd an IP banlist row.
DELETE/api/admin/banned-ips/:ipRemove an IP banlist row.

Invitations

MethodPathPurpose
GET/api/admin/invitesEvery invite code + usage state.
POST/api/admin/invites/generateBatch-generate codes (operator share link).
POST/api/admin/invites/grantAward invite quota to a user.
DELETE/api/admin/invites/:idRevoke a code.

See Invitations.

Reports

MethodPathPurpose
GET/api/admin/reportsQueue (paginated, filterable by target_type and status).
PUT/api/admin/reports/:idResolve a report. For targetType='user' + status='resolved', accepts banDuration (none / 1d / 7d / 1m / 1y / permanent) and banReason to ban the offender in the same transaction. See Reports.

Notification channels (server-side config)

MethodPathPurpose
GET/api/admin/notification-channelsList configured channels + capability matrix.
PUT/api/admin/notification-channels/:typeConfigure a channel (SMTP host, Discord token, VAPID keys, …).
POST/api/admin/notification-channels/:type/testSend a test through this channel (uses the admin's own user as recipient).

Torznab admin

MethodPathPurpose
GET/api/admin/torznabTorznab config + per-user key inventory.
PUT/api/admin/torznabToggle Torznab + edit limits.
GET/api/admin/torznab/usersActive user keys.
POST/api/admin/torznab/users/:id/blockRevoke a user's Torznab key.
POST/api/admin/torznab/users/:id/resetRotate a user's Torznab key.
GET/api/admin/torznab/blacklistQuery patterns currently blacklisted.
POST/api/admin/torznab/unblockLift a blacklist entry.
GET/api/admin/torznab/logsRecent Torznab requests with status + handler.
GET/api/admin/torznab/statsAggregate request counters by handler.

Panic Mode

MethodPathPurpose
GET/api/admin/panicEncryption state + last-activation timestamp.
POST/api/admin/panic/encryptActivate. Body: { confirmation: "ENCRYPT_ALL_DATA" }.
POST/api/admin/panic/restoreRestore. Body: { password }.

See Panic Mode. Activation is irreversible without the original password — operators are warned in-app.

Debug

MethodPathPurpose
GET/api/admin/debug/tracker-statusRaw tracker health + peer counter snapshot.
GET/api/admin/debug/redis-peersPeer hash sample for one info_hash.
GET/api/admin/debug/redis-all-peersFull SCAN over peers:*. Heavy — use sparingly.

Rate limits

The API enforces sliding windows in Redis. Limits are tuned in apps/api/utils/rateLimit.ts and are not env-driven today.

BucketLimitWindow
Public read100 req1 min
Mutations (POST/PUT/PATCH/DELETE)10 req1 min
Auth (/api/auth/*)5 req5 min
Tracker announce200 req1 min

Exceeding a bucket returns 429 Too Many Requests. Repeated offences within a short window add a temporary IP ban — see Security.

Error shape

Every error response uses h3's default shape:

json
{
  "url": "/api/example",
  "statusCode": 429,
  "statusMessage": "Too Many Requests",
  "message": "Rate limit exceeded",
  "data": {}
}

Common status codes:

CodeMeaning
400Validation failed (Zod body / query schema).
401Not authenticated (no session cookie).
403Authenticated but lacking the required permission/role.
404Resource not found, OR (deliberate) hidden — see Moderation thread.
409Conflict (e.g. duplicate info_hash on upload, role-rule collision).
429Rate limited.
500Server error — surface to the operator via LOG_LEVEL=error.

Released under the MIT License.