Repository navigation
REST API
Applies to Hydranos 4.4.
For scripting Hydranos: every native API route (/api/*) in 4.4, which work, which are stubs, which were removed, curl examples. The qBittorrent-compatible API (/api/v2/*) is documented on qBittorrent Shim and Automation, and the MCP endpoint on MCP Server.
This page is the map. Full request and answer bodies are in the repository's docs/API.md; the pages linked from each section explain what the settings do.
One port, 8199 by default ([daemon] api_port), serves the web UI, /api, the qBittorrent shim and /mcp. Examples use:
export HY=http://hydranos.example.org:8199
export KEY=YOUR_API_KEYBodies and answers are JSON unless a row says otherwise. Responses over 32 bytes are gzip-compressed when the client accepts it (curl --compressed).
There is one credential, the API key ([daemon] api_key, 48 hex characters, generated at first start). Where to find it, how to rotate it and how to expose Hydranos safely: Security and Access.
| How the key travels | Example | Notes |
|---|---|---|
X-Api-Key header |
-H "X-Api-Key: $KEY" |
The normal way. |
apikey query parameter |
"$HY/api/status?apikey=$KEY" |
For clients that cannot set headers. Lands in proxy logs. |
SID session cookie |
from POST /api/v2/auth/login
|
Form username + password (admin password or API key). Unlocks the whole native API. Expires after 3600 s idle; lost on restart. |
Authorization: Bearer |
-H "Authorization: Bearer $KEY" |
Accepted by /mcp only. |
POST /api/login (JSON {"username","password"}) does not set a cookie: it returns {"api_key": "..."}, which the web UI then sends as X-Api-Key. An empty api_key in the config refuses every caller.
Refusal shapes (test the status code, not the text):
| Where | Status | Body |
|---|---|---|
/api/* (almost every route) |
401 |
{"error":"Invalid or missing API key"} |
/api/selection/*, /api/network/egress
|
401 |
{"error":"unauthorized"} |
/api/v2/* (qBittorrent shim) |
403 |
plain text Forbidden. (Sonarr and Radarr expect 403 before login) |
/mcp |
401 |
JSON as above + header WWW-Authenticate: Bearer
|
POST /api/nodes/register |
401 |
{"error":"enrolment token unknown, already used, or expired"} |
POST /api/setup |
409 once an admin exists; 403 if the socket peer is not on loopback or a private network, or if a private peer (a reverse proxy) names a public client in X-Forwarded-For
|
JSON |
Public routes (no key): /health, /metrics, /, /static/*, /changelog.md, /install.sh, GET|POST /api/setup, POST /api/login, GET /api/startup, POST /api/v2/auth/login, POST /api/v2/auth/logout, POST /api/nodes/register (enrolment token instead of the key), and POST /api/auth/api-key/confirm (the pending key of a rotation instead of the current key, see Nodes).
The port opens before the catalogue is loaded. Until loading ends, only /, /static/*, /health ("status":"starting", 200), /metrics, GET /api/startup and GET /api/setup answer. Every other path, /api/v2/* and /mcp included, answers 503 with Retry-After: 5 and {"error":"Hydranos is starting (<phase>): N of M torrents restored","startup":{…}}; another method on one of the paths above (for example POST /api/setup) is not routed and is refused. Poll GET /api/startup until "ready":true, or retry after Retry-After. Phases: Operating Hydranos.
Errors are JSON {"error": "<message>"}, sometimes with more fields:
-
400: bad or missing field (strict bodies name the unknown field). -
404:{"error":"torrent not found"}, or no such engine/node/job. -
405: the path exists, not with this method (for examplePOST /api/race/settingssince 4.4). -
409: refused because of state. Data moves add"reason"(hardlinks,space,shared,destination_taken, …) and aplan; a filter selection that grew answers"reason":"grew"with the newcount; adding a torrent that is already there answers 409. -
410: a 3.x route removed in 4.4; the body names the replacement (Routes removed in 4.4). -
503+Retry-After: 5: still starting (above).
POST /api/torrents answers {"error","targets"} with a status that says why:
| Status | When |
|---|---|
400 |
Body not JSON; not exactly one of torrent_path, torrent_url, magnet_uri; unknown engine or mode; .torrent unparsable or not fetched (the URL shows as <url>: it may carry a passkey); no save path (none sent, no category with one); bad magnet (a malformed, missing or v2-only info hash included). |
404 |
torrent_path does not exist on the Hydranos host. |
409 |
Already added (torrent already added), and the other refusals: race disk full: … and, with [dedup] path_collision = "refuse", N file(s) of this torrent are already another torrent's with other bytes …. |
500 |
A store failure only (store: …). |
POST /api/torrents/upload answers the same refusals with 400, and 409 for already added. Test the status class, and read error for the reason.
-
:info_hashis the 40-character hex v1 hash. Some routes resolve a shorter prefix and act on the first match: always send the full hash. - Engines are addressed by id under
/api/engines/:id/...;/api/race/...and/api/hoard/...are aliases for the two engines that always exist. Exception:GET /api/race/torrentslists every engine with the race role. - The same torrent can be in several engines (one copy per engine). Torrent routes pick a copy with
?engine=<id>. The legacy spelling?agent=(a 3.x name) is also accepted:localor empty means the default,local-<id>means engine<id>. - List rows carry a label in the field
agent(legacy name):local-<engine>for a local copy,<node>-<engine>for a row merged from a declared node. Per-torrent routes answer 404 for a node row;/api/selection/*relays the action to the node that holds it.
Status (fields: Monitoring):
curl -s -H "X-Api-Key: $KEY" "$HY/api/status"First 100 hoard torrents of category tv, newest first (server-side filter and paging):
curl -s --compressed -H "X-Api-Key: $KEY" \
"$HY/api/hoard/page?category=tv&limit=100&sort=added_time"Create a category (there is no default save path, so adds need one):
curl -s -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
-d '{"name":"tv","save_path":"/data/tv","mode":"hoard"}' "$HY/api/categories"Add a .torrent file with a category (multipart):
curl -s -H "X-Api-Key: $KEY" \
-F "torrents=@show.s01e01.torrent" -F "category=tv" "$HY/api/torrents/upload"Add -F "seed_mode=true" to seed data already on disk without hashing it (seed mode; risks: Adding Torrents and Existing Data).
Stop every hoard torrent of category old (a bulk action by filter; count first, send the count as expect):
N=$(curl -s -H "X-Api-Key: $KEY" "$HY/api/hoard/page?category=old&limit=1" | jq .filtered)
curl -s -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"selection\":{\"filter\":\"category=old\",\"view\":\"hoard\",\"expect\":$N},\"params\":{}}" \
"$HY/api/selection/stop"
# 202 {"job":"...","total":N} -> poll GET /api/selection/jobs/<job>Status: ✓ works · ◐ works with a caveat. Stub routes appear only in Stubs and removed routes in 4.4.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/torrents/upload |
POST multipart | Add a .torrent. File part torrents, torrent or file; fields category, savepath/save_path, tags, engine or mode (race/hoard), paused/stopped, skip_checking/seed_mode/skip_recheck, create_subfolder. Body limit 2 MB. |
✓ |
/api/torrents |
POST JSON | Add from exactly one of torrent_path (a file on the Hydranos host), torrent_url (fetched by Hydranos, through the daemon's way out) or magnet_uri; plus category, save_path, tags, engine or mode, stopped, seed_mode (or skip_recheck), create_subfolder (default [daemon] create_torrent_folder). Magnet → 202 {"info_hash","status":"resolving"}, with seed_mode and stopped applied once resolved. Status codes: Errors above. |
◐ some refusals are 500 |
/api/torrents/:info_hash |
DELETE | Remove. ?delete_files=true deletes data; ?engine= removes one copy, otherwise all. Files another torrent still reads are kept. Also removes a torrent an engine runs with no store row. |
✓ |
/api/torrents/:info_hash/{files,torrent} |
GET | File list (index, path, size, done, progress, priority, plus sequential, first_last_piece, complete); the .torrent bytes from the store. |
✓ |
/api/torrents/:info_hash/download-order |
GET, POST | New in 4.4. File priorities (1, 6, 7; 0 → 409), sequential, first_last_piece (Download order). |
✓ |
/api/torrents/:info_hash/limits |
GET, POST | New in 4.4. Own speed caps {"up_kib","down_kib"} (0 = none), live and saved. |
✓ |
/api/torrents/:info_hash/share-limits |
GET, POST | New in 4.4. Own ratio_limit, seeding_time_limit, inactive_seeding_time_limit (-2 = engine's, -1 = none). |
✓ |
/api/torrents/:info_hash/trackers |
GET, POST | Read / edit. POST {"op":"add"|"remove","urls":[...]}, {"op":"replace","from","to"} or {"op":"set","tiers":[ ["u1"], ["u2"] ]}. .../add-tracker takes {"url"}. |
✓ |
/api/torrents/:info_hash/reannounce |
POST | Announce now, one copy only: the ?engine= one, else the first engine holding it. 429 in the 60 s cooldown. |
✓ |
/api/torrents/:info_hash/peers |
POST |
{"peers":["203.0.113.5:16172"]}: dial these peers. |
✓ |
/api/torrents/:info_hash/copy |
POST |
{"engine":"vpn1"}: add a second copy in another engine, same files. 409 while the source is not complete or is being checked. |
✓ |
/api/torrents/:info_hash/engine |
POST |
{"engine":"vpn1"}: move the torrent to another engine, files stay where they are. ?engine= names the source copy. The target takes the source's state (pieces, trackers, counters, stopped) before the source lets go; if either refuses, nothing changes. |
✓ |
/api/torrents/:info_hash/graduate |
POST |
{"engine","category","save_path","allow_breaking_hardlinks":false}: queue a graduate job (data move + engine change). save_path defaults to the category's. Same checks as a category move (409 with reason). |
✓ |
/api/torrents/add-defaults |
GET |
{"create_subfolder","skip_recheck"}: what the Add tab starts from (create_subfolder = [daemon] create_torrent_folder; skip_recheck always false). |
✓ |
/api/torrents/export |
POST form |
format=zip|txt|csv, selection (JSON) or hashes, strip_trackers=1. Streams a download. |
✓ |
/api/{hoard,race}/torrents/:info_hash |
GET | One row with live detail. | ✓ |
/api/{hoard,race}/torrents/:info_hash/category |
POST |
{"category","move_files":false,"allow_breaking_hardlinks":false}. With move_files, 202 + job (or a graduation if the mode differs). |
✓ |
/api/{hoard,race}/torrents/:info_hash/location |
POST |
Set location: {"location":"/abs/path","allow_breaking_hardlinks":false}. 202 + job when bytes move, 200 "moved":false if already there. |
✓ |
/api/{hoard,race}/torrents/:info_hash/move-preview |
GET | What a move would do, without doing it. | ✓ |
/api/{hoard,race}/torrents/:info_hash/tags |
POST |
{"tags":[...]} replaces the whole set. |
✓ |
/api/{hoard,race}/torrents/:info_hash/{pause,resume} |
POST | Stop / start one torrent. | ✓ |
/api/hoard/torrents/:info_hash/{pin,unpin} |
POST | Force download (started, runs past the download queue) / stop forcing. | ✓ |
/api/hoard/torrents/:info_hash/verify |
POST | Recheck (?engine= picks the copy), guarded (shared files). |
✓ |
/api/race/torrents/:info_hash/purge |
POST | Remove the copy in engine race with its files (files another torrent reads are kept); other copies untouched. 404 torrent not in race. Extra race engines: DELETE /api/torrents/:info_hash?engine=<id>. |
✓ |
/api/race/timeline/:info_hash, /api/trackers
|
GET | Race timeline; Trackers tab table. | ✓ |
How an add picks its engine and save path: Categories and Routing. Without engine or mode, the category decides. create_subfolder gives a single-file torrent a folder of its own, named after the torrent; it is not applied to magnets on this route.
Each /api/engines/:id/... row also answers as /api/hoard/... and /api/race/..., except pinned, pause-all and resume-all (hoard alias only) and rate-limits, share-limits (no alias: use /api/engines/race/… or /api/engines/hoard/…).
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/engines |
GET | Local engines (id, role, listen_port, torrents…), each with live stats (states, rates, peers, announces, caps, listening, held…). |
✓ |
/api/engines |
POST |
{"id","role":"race"|"hoard","listen_port","bind_interface"}: add an extra engine to the config. Answers restart_required: true. A non-empty bind_interface outside Linux → 400. |
✓ (after restart) |
/api/engines/:id |
DELETE | Remove an extra engine from the config. 409 with the count while it still holds torrents; race/hoard → 400. An empty engine keeps its listener until restart. |
✓ (after restart) |
/api/engines/:id/page |
GET | Paged list (see below). | ✓ |
/api/engines/:id/torrents |
GET | The engine as one array. Without a filter, offset/limit are ignored; with a filter, they are not (see below). |
✓ |
/api/engines/:id/pinned |
GET | Forced-download hashes. | ✓ |
/api/engines/:id/pause |
POST |
{"hashes":[...],"paused":true}: stop/start exact hashes. |
✓ |
/api/engines/:id/torrents/bulk |
POST |
{"action":"stop"|"start","hashes":[...],"exclude":[...],"all":false}. Unknown fields → 400; an empty hashes is not "all". |
✓ |
/api/engines/:id/{pause-all,resume-all} |
POST | Stop / start every torrent of the engine. | ✓ |
/api/engines/:id/listen-port |
POST |
{"port":16172}: moves TCP and uTP live, announces follow, written to the config. Port that cannot be bound, taken, or set by gluetun/WireGuard → 409; engine off the network → 503. |
✓ |
/api/engines/:id/dial-limits |
POST |
{"max_dials_per_sec","max_connections"} (0 = unlimited): live and written to the config. |
✓ |
/api/engines/:id/rate-limits |
GET, POST | New in 4.4. {"upload_kib","download_kib"} (0 = unlimited), live and saved. |
✓ |
/api/engines/:id/share-limits |
GET, POST | New in 4.4. max_ratio, max_seeding_time, max_inactive_seeding_time (-1 = off, minutes), share_limit_action. |
✓ |
/api/hoard/stats |
GET | Totals. | ✓ |
/api/hoard/download-slots |
GET | Configured active_downloads; the other counters are 0. |
◐ |
/api/race/settings |
GET | Race settings echo (upload_rate_limit/download_rate_limit = configured caps, bytes/s). GET only since 4.4. |
◐ |
/api/startup-pause, /api/startup-pause/release
|
GET, POST |
{"held":[engines],"holding"}; POST releases the hold (released). With start_paused, a held engine dials and announces nothing until released. |
✓ |
What the queue, share limits and speed limits do: Queue, Share and Speed Limits.
Paging parameters of .../page: offset (default 0), limit (default 500, clamped to 1–5000), sort (a row field, default added_time), order=asc (default descending), facets=1 (adds category/tag/tracker counts), fields=hash (hashes only). Filters: search (words ANDed, or a hex hash prefix of 6+ characters), category, tag, tracker, error_class and their _not variants (comma-separated lists, __none__ = without one), state (a row state such as seeding, or __active__, __error__, __tracker_err__, __pinned__). The answer is {"total","filtered","offset","limit","rows","facets"}. A page also merges the other local engines of the same role and the rows of declared nodes. Since 4.4 the same filters also apply to the whole-list routes GET /api/{hoard,race}/torrents and /api/engines/:id/torrents; without a filter they return the whole engine (/api/race/torrents: every race-role engine) and ignore offset/limit. With a filter, do not send offset/limit: a limit under 5000 cuts the answer to one page.
Known bug in 4.4. On a whole-list route, a filter plus an
offset(withlimitabsent or 5000 and up) loops forever once 5000 rows match from that offset: the route pages internally by appending its ownoffset, and the caller's comes first and wins, so the window never moves.
POST /api/selection/:action runs one action on many torrents as an in-memory bulk task (the web UI's context menu uses it). It answers 202 {"job","total"}; poll GET /api/selection/jobs/:id (kept 1 h after it ends, lost on restart). POST /api/selection/jobs/:id/cancel stops between two torrents. These tasks are not the persistent jobs of the Jobs tab: see Jobs, Bulk Actions and Moving Data.
Body: {"selection": ..., "params": {...}}, strict (unknown keys → 400).
- By rows:
"selection":{"items":[{"hash":"<40 hex>","agent":"local-hoard"}]}. Each row acts on its own copy (the engine in itsagentlabel). - By filter:
"selection":{"filter":"category=tv&state=seeding","view":"hoard","expect":<count>}, optional"exclude":[items].filtertakes only the filter keys listed under Lists and engines;viewis an engine id (defaulthoard);expectis required.
| Action | params |
|---|---|
stop, start, pin, unpin, reannounce, recheck
|
{} (recheck covers race copies too) |
tags |
{"tags":["a"],"op":"add"|"remove"} |
category |
{"category","move_files":false,"allow_breaking_hardlinks":false} |
location |
{"location":"/abs/path","allow_breaking_hardlinks":false} |
remove |
{"delete_files":false} |
copy, move-engine
|
{"engine":"vpn1"} |
handoff |
{"node","engine","then":"keep"|"remove"} |
node-fetch |
{"node","from_engine","engine"} |
node-move |
{"node","engine"} |
limits (4.4) |
{"up_kib","down_kib"}: each torrent's own speed caps. |
share_limits (4.4) |
{"ratio_limit","seeding_time_limit","inactive_seeding_time_limit"}. |
download_order (4.4) |
{"sequential","first_last_piece"} (file priorities are per torrent, on download-order). |
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/categories |
GET, POST | List / create {"name","save_path","mode":"race"|"hoard","graduate_to","transit"} → 201. mode case-insensitive, default race, other value → 400; existing name → 409. |
✓ |
/api/categories/:name |
PUT, DELETE | Update: the fields sent replace the stored ones, the others (mode included) are kept; a different name in the body renames it, relabels its torrents (relabelled; 409 if taken) and renames every reference to it (followed: counts of rss_rules, watch_folders, workflows, magnets, jobs) / delete: the label is cleared from its torrents (cleared). |
✓ |
/api/categories/orphans |
GET | Labels worn by torrents that match no category: [{"name","torrents","mode","save_path"}]. |
✓ |
/api/tags |
GET | Tags. | ✓ |
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/announce/{health,policy}, /api/announce/errors?host=
|
GET | Tracker health, live policy, error details. | ✓ |
/api/announce/ip-modes |
GET, POST |
{"host","mode"} IP family per tracker. |
✓ |
/api/announce/passkeys |
GET, POST |
{"host","passkey"} (empty passkey clears). |
✓ |
/api/announce/min-seed |
POST |
{"host","hours"} or {"host","clear":true}. |
✓ |
/api/announce/{mute,hidden} |
POST |
{"host","muted"} / {"host","hidden"} or {"hosts":[...],"hidden"}. |
✓ |
Meaning of each setting: Trackers and Announces.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/workflows |
GET, POST | List / create or replace a workflow. | ✓ |
/api/workflows/:id, /api/workflows/:id/run
|
DELETE, POST | Delete; run now (?dry=1: dry run). |
✓ |
/api/workflows/{fields,activity,links}, .../preview
|
GET, POST | Editor fields, activity log, link status; POST preview: torrents a draft would touch. | ✓ |
/api/jobs, /api/jobs/:id
|
GET | Persistent jobs (?limit=, default 100); one job. |
✓ |
/api/jobs/:id |
DELETE | Cancel: cancelled (queued, or a waiting move to a node) or cancelling (a running graduation, rolled back); another running move → 409 (it finishes); unknown id → 404. |
✓ |
Bodies: Workflows, Jobs, Bulk Actions and Moving Data.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/drain/{status,history,graduations} |
GET | Volumes, past drains, graduations in progress. | ✓ |
/api/drain/now?volume=, /api/drain/policy
|
POST | Drain now; {"volume","enabled","high_watermark","low_watermark"} or {"volume","inherit":true}, plus optional quota_gb (declared capacity, 10^9 bytes, 0 removes it) and trim_after_drain (bool), each on its own or with either form. |
✓ |
See Race Drain, Graduation and Seed Obligations.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/nodes |
GET, POST | List (probed live) / declare {"name","url","api_key"}. |
✓ |
/api/nodes/test |
POST | Probe without saving. | ✓ |
/api/nodes/:name |
DELETE, PATCH | Forget a node (the remote is untouched) / edit it (4.4): any of {"name","url","api_key"}; a new URL or key is probed first (400 if it does not answer), a name taken → 409, waiting moves follow a rename. |
✓ |
/api/nodes/:name/rotate-key |
POST | New in 4.4 (node on 4.4). The node gets a new key, stored here and returned once; the old one is refused. 502 with the reason if the node refused (it keeps its old key). | ✓ |
/api/auth/api-key/rotate, .../confirm
|
POST | Node side of a rotation: a pending key (10 min) / adopt it, authenticated by that key (written to [daemon] api_key). |
✓ |
/api/nodes/enrol, /api/nodes/register
|
POST | One-time token + install command; called by the new machine with the token. | ✓ |
/api/nodes/:name/{handoff,fetch,move-engine} |
POST | Push / pull / move between that node's engines (bodies: Nodes and Multiple Machines). | ✓ |
/node/:name/open |
GET | Redirect to the node's UI with its key. | ✓ |
/install.sh |
GET | Enrolment script (public). | ✓ |
The legacy 3.x routes /api/agents* were removed in 4.4: any path and method under /api/agents answers 410 Gone with "see":["/api/engines","/api/nodes"]. Use /api/engines for this machine's engines and /api/nodes for other machines (Upgrading from 3.x).
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/magnets, /api/magnets/:hash, /api/magnets/:hash/retry
|
GET, DELETE, POST | Magnets resolving or failed; drop; retry. | ✓ |
/api/watch |
GET, PUT | Watched folders. PUT takes the whole list [{"path","category","engine","paused","enabled"}]; path absolute and existing, category must exist. |
✓ |
/api/import/qbit/{preview,start}, .../{status,events}
|
POST, GET | Import from a qBittorrent instance; progress (events is SSE). |
✓ |
/api/import/transmission/{upload,preview,start} |
POST | Import from Transmission (upload multipart, up to 4 GiB). |
✓ |
/api/import/retry |
POST | New in 4.4. No body; retries what the last import failed with the same choices and answers {"job_id","torrents"} (the new job, the torrents retried); 404 no import, 409 one running, 400 nothing to retry or the qBittorrent login failed (the retry stays available). |
✓ |
/api/import/check-paths |
POST |
{"paths":[...]}: which paths this host can see. |
✓ |
/api/provenance |
GET | Where the library was imported from; written by every qBittorrent or Transmission import that added at least one torrent. | ✓ |
/api/dedup/config, /api/dedup/stats
|
POST, GET |
{"enabled":true} (restart to apply); duplicate-data counters. |
✓ |
Import wizard: Migrating from qBittorrent. Magnets, watched folders, dedup: Adding Torrents and Existing Data.
New in 4.4. Feeds, folders and download rules; a rule is qBittorrent's rule document (rss/setRule). The qBittorrent clients' /api/v2/rss/* routes reach the same feeds and rules.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/rss |
GET | Everything the RSS tab shows: settings, feeds, folders, rules. |
✓ |
/api/rss/feeds, /api/rss/feeds/:id
|
GET, POST / PATCH, DELETE | List, add {"url","path","refresh_secs"} (409 taken) / edit; remove. |
✓ |
/api/rss/feeds/:id/{refresh,articles,read,download}, /api/rss/refresh
|
POST, GET | Fetch now; articles; mark read; add one article by hand; fetch all (202). | ✓ |
/api/rss/folders |
POST |
{"path"}. |
✓ |
/api/rss/rules, /api/rss/rules/:name, .../rename
|
GET, PUT, DELETE, POST | List; create or replace (400 bad regex or episode filter); remove; rename. | ✓ |
/api/rss/rules/:name/matching, /api/rss/preview
|
GET, POST | What a saved rule, or a draft, would take from the articles already fetched. Adds nothing. | ✓ |
Rules, filters and what happens to an article: RSS and Automatic Downloads.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/network/{interfaces,engines}, /api/public-ip
|
GET | Interfaces that are up; exit IP per engine (?refresh=1); process exit IP. |
✓ |
/api/network/mode |
GET, POST | Network tab. POST writes the chosen mode's keys and removes the other modes'; restart_required. bind_interface outside Linux → 400. |
✓ |
/api/network/egress |
GET, POST | New in 4.4. The kill switch (armed, why, blocked engines) and the way out of the daemon's own requests (auto, direct, proxy, engine:<id>). |
✓ |
/api/network/wireguard, .../configs, .../configs/:name, .../engines
|
GET, POST, DELETE, POST | Managed WireGuard: state and live tunnels; upload a provider .conf; remove one; assign engines (tunnel, direct, none). |
✓ (Linux) |
/api/network/check |
POST | Leak test per engine: fail with LEAK: when an engine meant to leave by an interface or proxy shows the default route's address. |
✓ |
/api/port-forward |
GET | Per engine: port held, inbound_peers, reachable (yes/no/unproven), who forwards the port. |
✓ |
/api/ipfilter |
GET, PUT | Status / {"enabled","sources":[...],"refresh_hours"}. |
✓ |
/api/ipfilter/bans, /api/ipfilter/reload
|
POST, DELETE | Ban {"ip","reason"} / unban {"ip"}; POST reload reloads block lists. |
✓ |
Bodies: docs/API.md. Modes and the Network tab: Networking; kill switch and the daemon's way out: VPN and Egress; tunnels: Managed WireGuard.
| Route | Method | Purpose | Status |
|---|---|---|---|
/api/setup |
GET, POST | First run: {"needs_setup",...} / create the admin {"username","password"} (8+ chars) → API key. Public. |
✓ |
/api/login |
POST |
{"username","password"} → {"api_key"}. Public. |
✓ |
/api/auth/password |
POST |
{"password"} (8+ chars): stored hashed in [auth] password_hash. |
✓ |
/api/settings |
GET, POST | Whole config as JSON, the API key included. SOCKS5 passwords and the gluetun API key read ******** (posting the mask back keeps the stored value); an announce_proxy URL (with any user:pass in it) is still returned in clear / {"changes":[{"section","key","value"}]} edits existing keys only, except the live engine keys of [race]/[hoard] (rates, choker, queue, share limits; enable_lsd at next start), created when absent. Answers applied_live, restart_required. |
✓ |
/api/settings/reset |
POST | Reset to defaults (keeps login, key, data dir). | ✓ |
/api/settings/restart, /api/restart
|
POST | Answer, then stop cleanly (as on SIGTERM: stopped to trackers, resume data flushed) and exit with code 75 for the supervisor to restart (Operating Hydranos). |
✓ |
/api/fs/browse?path= |
GET | List folders on the Hydranos host. | ✓ |
/api/update-check |
GET | New release available. | ✓ |
A forgotten password: hydranos reset-password on the host (Security and Access).
| Route | Method | Purpose | Status |
|---|---|---|---|
/health |
GET | Public. status healthy (200), degraded (200: an engine without a listener, held by start_paused or blocked by the kill switch), unhealthy (503: the store does not answer, or no online engine listens), starting (200); plus checks and problems. |
✓ |
/metrics |
GET | Public, Prometheus, per engine (series: Prometheus Metrics). | ✓ |
/api/startup |
GET | Public. {"ready","phase","total","restored","engines","uptime"} (While Hydranos starts). |
✓ |
/api/status |
GET | Header and Overview figures. | ✓ |
/api/events |
GET | SSE stream of status snapshots. Ends itself when the daemon stops. | ✓ |
/api/logs, /api/logs/stream
|
GET | The in-memory ring (last 2000 lines), filtered server side: level (minimum), module, q, since (5m, 1h, 2d or Unix s), after (a seq), limit; an unreadable filter → 400. SSE tail with the same filters. |
✓ |
/api/stats/baseline |
GET, POST | Lifetime counter baseline {"total_uploaded","total_downloaded"}. |
✓ |
/api/health/anomalies |
GET | Anomaly report: torrent checks from a background pass every 5 min (generated_at), live tracker, announce, engine and disk counts. |
✓ |
/api/benchmark/{records,range,current,race-events}, .../trackers/{current,range}
|
GET | Benchmark data, tracker stats. | ✓ |
/api/benchmark/{engines,compare}, .../race-snapshots/:info_hash
|
GET | Extra engines' samples; A/B comparison; race timeline snapshots. | ✓ |
/api/vpn-speedtest/run, .../{latest,history}
|
POST, GET | iperf3 test (400 unconfigured, 409 one running, 502 iperf3 error); stored results. | ✓ |
Fields of /api/status, /api/benchmark/current, .../trackers/current and /api/health/anomalies: docs/API.md › Monitoring. /api/status covers race and hoard only (extra engines: /api/engines stats); in /api/benchmark/current, *_announce_rate, *_announce_fail_rate, race_avg_share and hoard_session_uploaded are always 0.
What each monitoring route reports: Monitoring and Logs.
These routes answer but do not do what their name says:
| Route | What it answers | Use instead |
|---|---|---|
GET /api/hoard/download-slots |
The configured active_downloads and zeros. |
active_downloads in the config, read live (Queue, Share and Speed Limits). |
GET /api/arr-cleanup/scan |
An empty scan ("candidates":null,"count":0). |
— |
Routes removed in 4.4. They were stubs up to 4.3 (a success-shaped answer that did nothing) and no screen called them. A removed path answers 404; a removed method on a path that keeps its GET answers 405; the 3.x routes answer 410 with a body naming the replacement.
| Route | Since 4.4 | Use instead |
|---|---|---|
POST|DELETE /api/hoard/download-slots |
405 |
active_downloads in the config, read live. |
POST /api/race/settings |
405 |
POST /api/settings or POST /api/engines/race/rate-limits. |
GET|POST /api/opt/flags, GET /api/race/choking
|
404 | — |
POST /api/hoard/verify-downloading |
404 |
POST /api/selection/recheck. |
POST /api/hoard/restart-stuck |
404 |
stop then start through /api/selection/*. |
POST /api/arr-cleanup/execute |
404 | — |
/api/agents, /api/agents/* (every method) |
410, "see":["/api/engines","/api/nodes"]
|
/api/engines, /api/nodes. |
/api/jobs/move-remote (every method) |
410, "see":["/api/selection/handoff",…]
|
POST /api/selection/handoff. |
The other routes listed as stubs or constants on the 4.3 page work in 4.4 (rows above).
Hydranos wiki — documents v4.4.1. · 🇬🇧 Home · 🇫🇷 Accueil · Changelog · Report an issue
Hydranos 4.4 · 🇬🇧
Start
Install
- Installation & First Run
- Docker
- Linux (bare metal)
- Windows
- Migrating from qBittorrent
- Upgrading from 3.x
Use
- Categories & Routing
- Adding Torrents & Existing Data
- RSS & Automatic Downloads
- Queue, Share & Speed Limits
- Engines: Race & Hoard
- Race Drain & Graduation
- Trackers & Announces
- Jobs, Bulk Actions & Moving Data
- Workflows
Integrate
Network & machines
- Deployment Topologies
- Networking: Ports & Port Forwarding
- VPN & Egress
- Managed WireGuard
- Nodes & Multiple Machines
Operate
- Operating Hydranos
- Monitoring & Logs
- Prometheus Metrics
- Security & Access
- Troubleshooting
- Known Limitations in 4.4
Reference
- Architecture
- Configuration overview
- Config: daemon, auth & global
- Config: engines & race drain
- Config: environment, CLI & signals
Hydranos 4.4 · 🇫🇷
Démarrer
Installer
- Installation & premier démarrage
- Docker
- Linux (bare metal)
- Windows
- Migrer depuis qBittorrent
- Mettre à jour depuis la 3.x
Utiliser
- Catégories & routage
- Ajouter des torrents & données existantes
- Flux RSS & téléchargements automatiques
- File, limites de partage & de débit
- Moteurs : race & hoard
- Drain race & graduation
- Trackers & annonces
- Jobs, actions groupées & déplacement
- Workflows
Intégrer
Réseau & machines
- Topologies de déploiement
- Réseau : ports & redirection
- VPN & sortie réseau
- WireGuard géré
- Nœuds & plusieurs machines
Exploiter
- Exploiter Hydranos
- Supervision & journaux
- Métriques Prometheus
- Sécurité & accès
- Dépannage
- Limites connues de la 4.4
Référence
- Architecture
- Configuration : vue d'ensemble
- Config : démon, auth & global
- Config : moteurs & drain race
- Config : environnement, CLI & signaux