Gextto HTTP API¶
This is the table of the public routes registered by the daemon. It is a surface reference, not a stable integration contract for every payload: before automating an action, check the request and the response on the running build.
The document covers two servers: the Gextto daemon (/api/…, the routes
below, plus a qBittorrent-compatible subset under /api/v2) and the
gx-torrent daemon, which exposes its own native /api/v1 and a
qBittorrent-compatible /api/v2 (see gx-torrent daemon routes at the end).
Before using the API¶
- By default the API is open, as on a trusted LAN. With
Configuration → Access and services → Require login enabled, a client
outside the local network must send the API key in the
X-Api-Keyheader (or?apikey=in the address) or the session cookie obtained from/login; without credentials the/api/…routes answer401. For access from the Internet use HTTPS anyway (a reverse proxy with a certificate, or a VPN). /loginand/logoutexist only when protected access is enabled.- Routes that change configuration, files, torrents or databases normally use
POST/DELETE; treat them as administrative actions and keep backups. A browser request that declares itself cross-site (anOrigin/Refererfrom another site, orSec-Fetch-Site: cross-site) is refused with403: this stops a page on another origin from driving the API through the browser of a user on the LAN. Same-origin calls and non-browser clients (curl, the TUI, scripts) are unaffected. - Placeholders in curly braces, for example
{hash}, are part of the path and must be encoded correctly by the client. - Some routes are historical aliases (
run_now/run-now, for example). New integrations should prefer the more readable form when available. - Long operations (
/api/scan-all-archives,/api/maintenance/backfill-media-info,/api/rename-all) answer202with ajob_id: follow state, progress and outcome withGET /api/jobs/{id}and cancel withPOST /api/jobs/{id}/cancel. The full list is atGET /api/jobs.
The official server-rendered UI is at /; /v2 is a technical alias. The
internal HTMX routes under /v2 are not part of this integration table and may
change. /ui is not served. For operational checks use
GET /api/health and GET /api/status first.
Gextto routes¶
| Method | Path | Description |
|---|---|---|
| GET | / |
web interface (the v2 page at the root) |
| POST | /api/access/generate-key |
generate a new API access key |
| GET | /api/acquisitions |
acquisition history |
| GET | /api/archive |
archive entries (search and pages) |
| GET | /api/ed2k/status |
aMule integration state: enabled, reachable, download, last_error and, when aMule answers, status (version, eD2k server and High/Low ID, Kad state, speeds and limits, network size, session_sent/session_recv in bytes since amuled started, total_sent/total_recv over all its runs, shared_files) |
| GET | /api/ed2k/downloads |
aMule's download queue: hash, name, size, done, progress, speed, state (queued, downloading, paused, checking, finished, error), sources and active sources, priority, ed2k link, last data received (Unix time); enabled is false while the integration is off |
| POST | /api/ed2k/downloads/{hash}/priority |
set a download's priority: JSON |
| POST | /api/ed2k/downloads/{hash}/{action} |
pause, resume or remove an aMule download (remove cancels a download in progress and deletes its partial data; a completed file only leaves aMule's list) |
| GET | /api/ed2k/limits |
aMule's global bandwidth limits: download_kib, upload_kib (0 = unlimited) |
| POST | /api/ed2k/limits |
set aMule's global bandwidth limits: JSON {"download_kib": N, "upload_kib": N}, 0 = unlimited |
| GET | /api/ed2k/log |
aMule's own log lines |
| GET | /api/ed2k/shared |
the files aMule shares with what it uploaded for each (session and total bytes, requests and accepted requests session/total, queued clients, live upload rate and last upload time, priority, ed2k link), the upload queue (client, software, file, speed, upload position) and the shared folders; enabled is false while the integration is off |
| POST | /api/ed2k/shared/reload |
ask aMule to rescan its shared folders, so files added or removed outside aMule show up (the reload button of the aMule GUI) |
| POST | /api/archive/add |
add an entry to the archive |
| POST | /api/archive/batch-download |
download several archive entries (magnet or ed2k_link) |
| POST | /api/archive/delete |
delete archive entries |
| GET | /api/backup |
list backups |
| POST | /api/backup |
create a backup |
| GET | /api/backup/list |
list backups |
| POST | /api/backup/verify |
verify a backup archive |
| POST | /api/backup/send-telegram |
send a backup through Telegram |
| GET | /api/backup/settings |
backup settings |
| POST | /api/backup/settings |
save the backup settings |
| POST | /api/backup/test-ftp |
test the FTP backup destination |
| GET | /api/blocklist |
blocked releases, each with the expiry of a temporary block (empty = permanent) |
| POST | /api/blocklist/{hash}/remove |
remove a blocklist entry |
| GET | /api/browse_dir |
list a directory for the folder picker (?path=; files=video also lists its video files) |
| GET | /api/browser-handlers/download |
download a browser integration script or .desktop file |
| GET | /api/calendar |
upcoming episodes (calendar) |
| GET | /api/comics |
list comics |
| POST | /api/comics |
add a comic |
| POST | /api/comics/check-links |
check the comic source links |
| POST | /api/comics/cycle |
run a comics cycle now |
| POST | /api/comics/download |
download comic issues |
| GET | /api/comics/downloads |
comic downloads in progress |
| POST | /api/comics/downloads/tag |
tag a comic download |
| POST | /api/comics/downloads/{id}/pause |
pause a comic download |
| POST | /api/comics/downloads/{id}/remove |
remove a comic download |
| POST | /api/comics/downloads/{id}/resume |
resume a comic download |
| POST | /api/comics/explore |
search comics on the source |
| GET | /api/comics/history |
comic download history |
| POST | /api/comics/history/delete |
delete a comic history entry |
| POST | /api/comics/history/clear |
clear the comic history |
| POST | /api/comics/weekly/history/clear |
clear the weekly-pack history |
| POST | /api/comics/links |
save the comic source links |
| GET | /api/comics/weekly |
weekly comic packs |
| POST | /api/comics/weekly/links |
save the weekly-pack links |
| POST | /api/comics/weekly/settings |
weekly-pack settings |
| DELETE | /api/comics/{id} |
remove a comic |
| POST | /api/comics/{id}/enabled |
enable or disable a comic |
| GET | /api/quality-profiles |
named quality profiles (the list behind the v3 qualityprofile endpoint) |
| POST | /api/quality-profiles |
save the named quality profiles |
| GET | /api/v3/system/status |
Sonarr/Radarr v3 system status for a request manager |
| GET | /api/v3/qualityprofile |
v3 quality profiles: the named profiles, with stable ids |
| GET | /api/v3/qualityProfile |
same as /api/v3/qualityprofile (the spelling Jellyseerr uses) |
| GET | /api/v3/languageprofile |
v3 language profile (the default language) |
| GET | /api/v3/rootfolder |
v3 root folders (archive root and each series archive path) |
| GET | /api/v3/tag |
v3 tags |
| GET | /api/v3/series/lookup |
v3 series lookup (?term=, also tvdb:ID: a configured series comes back with its id and monitored seasons) |
| POST | /api/v3/series |
v3 add series (only the seasons marked monitored are followed) |
| PUT | /api/v3/series |
v3 update series: adds the newly monitored seasons to an existing series |
| GET | /api/v3/movie/lookup |
v3 movie lookup (?term=, also tmdb:ID: a configured movie comes back with its id, monitored and hasFile) |
| POST | /api/v3/movie |
v3 add movie (the response carries the library id) |
| PUT | /api/v3/movie |
v3 update movie: monitors a movie already in the library |
| GET | /api/v3/queue |
v3 queue (the torrent session) |
| GET | /api/import/preview |
manual import: the video files of a path with their match and destination (?path=) |
| POST | /api/import |
manual import: rename, move and register one file |
| POST | /series/gaps/search |
Series → Missing tab: start the background search of every missing episode of one series (form field series) |
| GET | /series/gaps/search/status |
Series → Missing tab: progress or result of that search (form field job) |
| POST | /series/gaps/search/cancel |
Series → Missing tab: cancel that search (form field job) |
| GET | /library/bulk/profile |
Series/Movies "Edit selected" panel: the value fields filled from a quality profile (?profile=ID&view=) |
| POST | /library/bulk |
Series/Movies "Edit selected" panel: apply the ticked values to the ticked rows |
| GET | /import/preview |
manual import page: preview fragment |
| POST | /import/apply |
manual import page: confirm one file |
| GET | /api/config |
read the configuration |
| POST | /api/config |
save the configuration |
| POST | /api/config/add-all-from-archive |
add the series found in the archive folder to the configuration |
| POST | /api/config/add_all_from_archive |
alias of add-all-from-archive |
| GET | /api/config/check-ports |
check the configured transfer port range |
| GET | /api/config/library |
library folders; each movie also carries downloaded, downloaded_path, downloaded_title and downloaded_at when a copy is already in the library |
| POST | /api/config/library |
save the library folders |
| POST | /api/config/movies |
save the movie defaults |
| POST | /api/config/series |
save the series defaults |
| POST | /api/config/settings |
save one setting |
| DELETE | /api/config/settings/{key} |
reset one setting |
| GET | /api/config/source-filters |
per-source filters |
| POST | /api/config/source-filters |
save the per-source filters |
| GET | /api/cycle-history |
acquisition cycle history |
| GET | /api/cycles |
acquisition cycle history |
| POST | /api/database/cleanup |
clean the database |
| POST | /api/database/rescore |
rescore the archived releases |
| POST | /api/db/action |
vacuum, analyze or integrity-check the databases |
| GET | /api/db/info |
database size and table counts |
| POST | /api/db/prune |
prune the database |
| POST | /api/db/prune-by-ids |
prune the selected rows |
| POST | /api/db/prune-keyword |
prune the rows matching a keyword |
| POST | /api/db/prune/preview |
preview a prune |
| GET | /api/debug/torrent_match |
explain a torrent match (debug) |
| GET | /api/download-tags |
download tags |
| POST | /api/download-tags |
add a download tag |
| DELETE | /api/episodes/{series}/{season}/{episode} |
delete an episode file |
| POST | /api/episodes/{series}/{season}/{episode}/force |
force the search of an episode |
| POST | /api/episodes/{series}/{season}/{episode}/ignore |
ignore an episode |
| POST | /api/episodes/{series}/{season}/{episode}/redownload |
redownload an episode |
| POST | /api/episodes/{series}/{season}/{episode}/search |
search one episode |
| GET | /api/episodes/{series}/{season}/{episode}/sources |
candidate sources for an episode |
| GET | /api/event-hooks |
event hooks (webhooks and commands) |
| POST | /api/event-hooks |
save the event hooks |
| GET | /api/feed.xml |
magnet feed of the recent releases |
| GET | /api/feed/status |
feed status |
| POST | /api/flaresolverr/test |
test FlareSolverr |
| GET | /api/gaps |
missing episodes |
| GET | /api/health |
health check |
| GET | /api/i18n/active |
active language and catalogue |
| POST | /api/i18n/active |
set the active language |
| POST | /api/i18n/language |
set the interface language |
| GET | /api/i18n/languages |
available languages |
| POST | /api/indexer/test |
test an indexer |
| POST | /api/jellyfin/refresh |
refresh the Jellyfin library |
| POST | /api/jellyfin/test |
test Jellyfin |
| GET | /api/jobs |
background jobs |
| GET | /api/jobs/{id} |
state and progress of one job |
| POST | /api/jobs/{id}/cancel |
cancel a job |
| GET | /api/last_cycle |
last cycle summary |
| GET | /api/last_cycles |
recent cycles |
| GET | /api/license |
license text |
| POST | /api/log-level |
set the log level |
| GET | /api/log_level |
log level |
| POST | /api/log_level |
set the log level (alias) |
| GET | /api/logs |
recent log lines |
| GET | /api/logs/stream |
live log (Server-Sent Events) |
| POST | /api/maintenance/backfill-media-info |
probe the missing media info |
| POST | /api/maintenance/media-integrity/start |
start or resume the file integrity check; restart=true forgets the previous outcomes and rechecks everything, batch=0 checks every pending file |
| POST | /api/maintenance/media-integrity/stop |
stop the file integrity check after the current file |
| GET | /api/maintenance/night |
night maintenance: the window, whether it is open, why it waits (a download transferring, an import, a high load), the last database maintenance and integrity check, the audio cleanup of the library, the last library check and the library files gone from the disk |
| POST | /api/maintenance/night/run |
one night maintenance pass now, outside the window; it still waits until Gextto is idle (202) |
| POST | /api/maintenance/night/forget |
body with series, season and episode: clear the archive row of an episode whose file is confirmed gone, so the search downloads it again; 409 when the file is back or its folder cannot be read |
| GET | /api/maintenance/media-integrity |
progress and damaged files of the integrity check; files already checked and unchanged are skipped, a file that timed out is retried after a day, and the answer keeps damaged and unreadable counts apart |
| POST | /api/maintenance/clean-duplicates |
remove the duplicate library files |
| POST | /api/maintenance/clean-trash |
empty the recycle bin |
| POST | /api/maintenance/housekeeping |
run the housekeeping now |
| POST | /api/maintenance/restore-source |
restore files from the recycle bin |
| POST | /api/maintenance/rename-folder/scan |
scan the folders to rename |
| POST | /api/maintenance/rename-folder/apply |
apply a folder rename |
| GET | /api/manual-search |
manual search state and results |
| GET | /api/media-info |
media info of the library files |
| POST | /api/media-info/probe |
probe a file with ffprobe |
| POST | /api/missing/search |
search the missing episodes |
| POST | /api/mkdir |
create a directory |
| GET | /api/movies |
list movies |
| GET | /api/movies/history |
movie download history |
| GET | /api/movies/seen |
seen movies |
| GET | /api/movies/seen/grouped |
seen movies, grouped |
| DELETE | /api/movies/{id} |
remove a movie |
| GET | /api/movies/{id} |
movie details |
| POST | /api/movies/{id} |
update a movie |
| POST | /api/movies/{id}/metadata |
apply metadata to a movie |
| POST | /api/movies/{id}/metadata/search |
search movie metadata |
| POST | /api/movies/{id}/redownload |
redownload a movie |
| POST | /api/movies/{id}/search |
search a movie |
| GET | /api/network/interfaces |
network interfaces |
| GET | /api/notifications/stream |
live notifications (Server-Sent Events) |
| POST | /api/plex/refresh |
refresh the Plex library |
| POST | /api/plex/test |
test Plex |
| GET | /api/process-metrics |
CPU and memory of the Gextto process |
| GET | /api/providers/status |
status of the providers |
| POST | /api/providers/status |
clear the provider status |
| GET | /api/ramdisk |
RAM disk state |
| POST | /api/ramdisk/create |
create the RAM disk |
| POST | /api/ramdisk/select |
select the RAM disk |
| GET | /api/ramdisk_check |
check whether a path is a RAM disk |
| GET | /api/recent-downloads |
recent downloads |
| POST | /api/rename-all |
rename the whole library |
| GET | /api/rename-progress |
rename progress |
| POST | /api/run-now |
start an acquisition cycle now |
| GET | /api/run_now |
start a cycle now (alias) |
| POST | /api/run_now |
start a cycle (alias) |
| POST | /api/scan-all-archives |
scan every series archive |
| POST | /api/score/preview |
score a release with the current weights |
| POST | /api/search |
manual search |
| POST | /api/search/dashboard |
manual search for the dashboard (with the rejected ones) |
| POST | /api/search/add |
add a search result |
| POST | /api/search/archive |
search the archive |
| POST | /api/search/explain |
explain a release decision |
With the aMule integration enabled (ed2k_enabled), manual search results can
carry network: "ed2k", ed2k_link and sources next to the torrent fields.
eD2k is searched only when the request asks for it: send
{"query":"…","kad":true} to POST /api/search or /api/search/dashboard (the
UI has a Kad checkbox); the network is the one set in ed2k_search_network.
Such a result has no magnet: POST /api/search/add hands its link to aMule,
answers 409 when the integration is off or aMule already has the file, and
502 when aMule cannot be reached. Use GET /api/ed2k/status for the live
state.
| GET | /api/seed-rules | seed rules: per-source/category seeding limits |
| POST | /api/seed-rules | save the seed rules (list of name, source, category, ratio, seed_days, idle_minutes; null = global, 0 = no limit) |
| POST | /api/send-magnet | send a magnet to the engine |
| GET | /api/series | list series |
| GET | /api/series/all-missing | missing episodes (alias of gaps) |
| GET | /api/series/calendar | upcoming episodes |
| GET | /api/series/seen | seen episodes |
| GET | /api/series/seen/grouped | seen episodes, grouped |
| GET | /api/series/{name} | series details |
| POST | /api/series/{name} | update a series |
| DELETE | /api/series/{name} | remove a series |
| GET | /api/series/{name}/episodes | episodes of a series |
| GET | /api/series/{name}/info | metadata from the provider |
| POST | /api/series/{name}/metadata | refresh the metadata |
| POST | /api/series/{name}/path | set the series folder |
| POST | /api/series/{name}/rename-execute | rename the series files |
| POST | /api/series/{name}/rename-preview | preview a series rename |
| POST | /api/series/{name}/scan-archive | scan the series archive |
| POST | /api/series/{name}/search-missing | search the missing episodes of a series; the call stops at 90 s and answers with the episodes searched so far (the field "complete" says whether it reached the end), and the UI runs the full search through /series/gaps/search |
| POST | /api/series/{name}/toggle-season | enable or disable a season |
| POST | /api/service/restart | restart the Gextto service |
| GET | /api/services | status of the external services |
| POST | /api/set-speed-limits | set the global speed limits |
| GET | /api/setup | first-run setup status |
| POST | /api/setup/complete | complete the setup |
| POST | /api/simkl/auth/poll | poll the SimKL login |
| POST | /api/simkl/auth/revoke | revoke the SimKL token |
| POST | /api/simkl/auth/start | start the SimKL login |
| GET | /api/simkl/calendar | SimKL calendar |
| POST | /api/simkl/mark-watched | mark watched on SimKL |
| POST | /api/simkl/scrobble | scrobble to SimKL |
| GET | /api/simkl/settings | SimKL settings |
| POST | /api/simkl/settings | save the SimKL settings |
| GET | /api/simkl/status | SimKL status |
| GET | /api/simkl/watchlist | SimKL watchlist |
| POST | /api/simkl/watchlist/import | import the SimKL watchlist |
| GET | /api/sources/health | health of the sources |
| GET | /api/stats | library statistics |
| GET | /api/status | daemon status, version and build |
| GET | /api/system/lt_mem_suggest | suggested engine memory limits |
| GET | /api/system/stats | host statistics |
| GET | /api/tag-dir-rules | folder tag rules |
| POST | /api/tag-dir-rules | save the folder tag rules |
| POST | /api/test-notification | send a test notification |
| POST | /api/tmdb/add | add a title from TMDb |
| POST | /api/tmdb/discover | discover titles on TMDb |
| POST | /api/tmdb/search | search TMDb |
| GET | /api/torrent-backend | torrent backend status |
| GET | /api/torrent-backend/qbittorrent/update | qBittorrent update status |
| POST | /api/torrent-backend | activate a torrent backend |
| POST | /api/torrent-backend/preflight | preflight checks before switching backend |
| POST | /api/torrent-backend/qbittorrent/update | update qBittorrent |
| POST | /api/torrent-backend/test | test a torrent backend |
| POST | /api/torrent-backend/portcheck | check the backend peer port |
| GET | /api/torrent-migrations | torrent migration status |
| POST | /api/torrent-migrations/cancel | cancel a migration |
| POST | /api/torrent-migrations/plan | plan a migration |
| GET | /api/torrent-events | recent torrent events |
| GET | /api/torrent-no-rename | torrents excluded from renaming |
| GET | /api/torrent-tags | torrent tags |
| POST | /api/torrent-tags | set a torrent tag |
| GET | /api/torrents | list torrents |
| POST | /api/torrents | add a torrent |
| POST | /api/torrents/add | add a torrent |
| GET | /api/torrents/history | torrent history |
| GET | /api/torrents/ipfilter_status | IP filter status |
| POST | /api/torrents/ipfilter_update | update the IP filter |
| POST | /api/torrents/optimize_settings | apply the suggested engine settings |
| POST | /api/torrents/peers | peers of a torrent (legacy body form) |
| POST | /api/torrents/pin | pin a torrent |
| POST | /api/torrents/remove | remove a torrent (legacy form) |
| POST | /api/torrents/remove_completed | remove the completed torrents |
| POST | /api/torrents/sequential | sequential download on or off |
| GET | /api/torrents/session-stats | engine session statistics |
| POST | /api/torrents/set_limits | set the torrent limits (legacy form) |
| GET | /api/torrents/stats | torrent statistics |
| GET | /api/torrents/temp-limits | temporary speed limit and latency guard |
| POST | /api/torrents/temp-limits | set the temporary speed limit |
| POST | /api/torrents/unpin | unpin a torrent |
| DELETE | /api/torrents/{hash} | remove a torrent |
| GET | /api/torrents/{hash} | torrent details |
| GET | /api/torrents/{hash}/export.torrent | download the .torrent file |
| GET | /api/torrents/{hash}/files | files of a torrent |
| POST | /api/torrents/{hash}/files/priority | set the file priorities |
| POST | /api/torrents/{hash}/limits | per-torrent speed limits |
| GET | /api/torrents/{hash}/magnet | export the magnet link as JSON; with raw=1 it answers with the magnet itself as text/plain |
| POST | /api/torrents/{hash}/mark_failed | mark a torrent as failed |
| POST | /api/torrents/{hash}/no_rename | exclude a torrent from renaming |
| POST | /api/torrents/{hash}/pause | pause a torrent |
| GET | /api/torrents/{hash}/peers | peers of a torrent |
| GET | /api/torrents/{hash}/pieces | piece map |
| GET | /api/torrents/{hash}/pieces/runs | pieces being downloaded |
| POST | /api/torrents/{hash}/selective | selective download profile |
| POST | /api/torrents/{hash}/reannounce | reannounce to the trackers |
| POST | /api/torrents/{hash}/recheck | recheck the data |
| POST | /api/torrents/{hash}/remove | remove a torrent with options |
| POST | /api/torrents/{hash}/restart | restart a torrent |
| POST | /api/torrents/{hash}/resume | resume a torrent |
| POST | /api/torrents/{hash}/storage | move a torrent to another folder |
| GET | /api/torrents/{hash}/stream | stream a file from a torrent |
| POST | /api/torrents/{hash}/super-seeding | super-seeding on or off |
| GET | /api/torrents/{hash}/trackers | trackers of a torrent |
| POST | /api/torrents/{hash}/trackers | replace the tracker list (an empty list removes every tracker); the UI composes it from the add form and the per-row remove |
| POST | /api/torrents/{hash}/web-seeds | set the web seeds |
| GET | /api/torrents/{hash}/why | diagnose a torrent: reason and hint when it is not transferring; answers 503 when no torrent engine is active |
| GET | /api/trash | recycle bin entries |
| POST | /api/trash/delete | delete recycle bin entries |
| POST | /api/tvdb/search | search TheTVDB |
| GET | /api/tvdb/series/{id} | TheTVDB series details |
| GET | /api/update | update status |
| POST | /api/update/apply | apply the update |
| POST | /api/update/check | check for updates |
| POST | /api/upload-torrent | upload a .torrent file |
| POST | /api/v2/auth/login | qBittorrent-compatible login: the credentials of the access control, answered with Ok. or Fails. and the SID cookie |
| POST | /api/v2/auth/logout | qBittorrent-compatible logout: drops the SID cookie |
| GET | /api/v2/app/defaultSavePath | download folder of the configuration |
| GET | /api/v2/app/version | Gextto's version in the qBittorrent format (a v prefix) |
| GET | /api/v2/app/webapiVersion | version of the Web API subset |
| POST | /api/v2/torrents/add | add torrents the way the browser extensions do: the urls field (magnets and http(s) .torrent addresses, including an address that answers with a magnet, as a redirect or as the body) or the uploaded .torrent files, with stopped/paused, savepath, sequentialDownload, firstLastPiecePrio and skip_checking |
| POST | /api/v2/torrents/delete | remove torrents (hashes, deleteFiles) |
| GET | /api/v2/torrents/info | torrent list in the qBittorrent shape (hashes and filter honoured) |
| POST | /api/v2/torrents/pause | pause torrents (hashes, empty = all) |
| POST | /api/v2/torrents/recheck | recheck the data (hashes) |
| POST | /api/v2/torrents/resume | resume torrents (hashes, empty = all) |
| POST | /api/v2/torrents/start | resume torrents (qBittorrent 5.x alias of resume) |
| POST | /api/v2/torrents/stop | pause torrents (qBittorrent 5.x alias of pause) |
| GET | /api/v2/transfer/info | global download and upload speeds |
| GET | /api/watched-folders | watched folders |
| POST | /api/watched-folders | save the watched folders |
| GET | /favicon.ico | favicon |
| GET | /feed.xml | magnet feed (RSS) |
| GET | /feed/calendar.ics | calendar feed (iCal) |
| GET | /magnet | add from a magnet link |
Total routes: 281
The temporary speed limit (POST /api/torrents/temp-limits) is the speed
exception: it wins over the rule chosen in the settings (fixed limits, Auto or the
schedule) until it expires or is removed, whether minutes is set or not (0 =
until removed). It also accepts auto: true|false: with auto the bandwidth is
handed to the guided tuning with no cap, the numeric download_kib /
upload_kib are ignored, minutes sets how long it lasts and the two modes are
exclusive. The rule itself is the setting torrent_speed_mode (auto, fixed
or schedule), read and written through /api/config/settings like any other;
it is stored as torrent_speed_auto and torrent_sched_enabled.
GET /api/torrents/temp-limits also returns latency_guard, the state of the
Auto speed's latency guard: active, measuring, mode (free, trial or
control), latency_ms, baseline_ms, the caps it applies
(download_cap_kib, upload_cap_kib, 0 = none), router_counters (the
router's WAN counters answer), others_on_line and the others' traffic
(others_download_kib, others_upload_kib).
The controls behind it are settings: torrent_auto_latency_guard (on/off),
torrent_auto_latency_source (both = the Google/Cloudflare ping decides and
the router is diagnostic, ping = the router is not read, router = legacy
yield-to-others), torrent_auto_latency_target (empty = Google 8.8.8.8/8.8.4.4
and Cloudflare 1.1.1.1/1.0.0.1 on port 443) and torrent_line_max_down_kib /
torrent_line_max_up_kib (the line's real maximum in KiB/s; 0 = unknown, the
controller discovers it). The caps are driven by the latency, not by the
router; the controller ramps while the ping is good and cuts when it rises.
Per-torrent limits¶
POST /api/torrents/{hash}/limits changes only the fields it carries:
download_limit and upload_limit (bytes/s), seed_ratio, seed_days,
max_connections and max_uploads. A field that is left out keeps its current
value, so a client that edits one field at a time never resets the others
(setting the seed days to 0 must not move the ratio to the global value).
-1 means «use the global value», 0 «no limit». The historical
POST /api/torrents/set_limits (dl_kbps, ul_kbps, seed_ratio,
seed_days) always carries the two rates and only the seed limits it names.
Audio preferences¶
POST /api/tmdb/add, POST /api/series/{name} and POST /api/movies/{id}
accept an optional audio preset:
""orany: no constraint;lossless-5.1/lossless-7.1: TrueHD, DTS-HD, FLAC or PCM, with at least 5.1 or 7.1 channels;high-5.1/high-7.1: DTS or DD+ (DTS-HD HRA included), with at least 5.1 or 7.1 channels;standard-5.1/standard-7.1: AC3 or generic 5.1, with at least 5.1 or 7.1 channels;compressed-5.1: AAC or Opus, with at least 5.1 channels.
The values are minimum requirements: a release below the selected preset is
rejected. A title that has never set one, including every library row created
before this feature, defaults to no constraint. The same field is returned by
GET /api/series/{name} and GET /api/movies/{id}. The older pair of audio
(lossless, high, standard, compressed) and audio_channels (5.1,
7.1) is still accepted for API compatibility and is folded into the combined
preset on load.
Upgrade switch and quality profiles¶
POST /api/series/{name} and POST /api/movies/{id} accept
quality_profile_id too (0 or absent = keep the current values): the title takes
the profile's quality, audio, language, subtitle and disable_upgrades,
and its exclude is merged with the profile's. An unknown id answers 400.
POST /api/tmdb/add also accepts quality_profile_id: the new title then takes
quality, audio, language, subtitle and disable_upgrades from that
profile, whatever the request says, and exclude is added to the profile's
exclusions. An unknown id answers 400 here too: a stale profile never silently
becomes another policy.
POST /api/movies/{id} accepts subtitle_required (boolean, «Sottotitoli
obbligatori»): true makes subtitle a requirement, false only a preference; it
replaces subtitle_requirements. language_requirements is still read for
compatibility, but the interface no longer writes it: at startup Gextto folds it
into language, which it overrode.
POST /api/tmdb/add and POST /api/movies/{id} accept disable_upgrades
(boolean; the interface shows it as «Permetti upgrade: No»): a better release never replaces the archived file
of that title. POST /api/series/{name} already accepted it.
The items of GET/POST /api/quality-profiles carry id, name, quality,
audio (one of the presets above, empty = any), language, subtitle,
exclude, disable_upgrades and upgrades (allow/deny, the same switch as the profile editor writes it). A profile is a template: the add form and the
v3 endpoints (POST /api/v3/series, POST /api/v3/movie) copy its values,
audio and disable_upgrades included, into the new title. An empty
language means the configured default language; POST stores the default of
that moment, so a later change of the default does not move the profiles. Names
must be unique (a repeated name answers 400, naming it), at most 120 characters;
the list holds at most 200 profiles. The v3 list reports
upgradeAllowed as the opposite of disable_upgrades; the profiles generated
from the library (until the list is saved for the first time) carry the quality,
minimum audio, language, subtitles and upgrade switch of the titles they come
from—never a single title's exclusions. The v3 lookup records report the first
profile of the saved list as their qualityProfileId (the profile the add form
preselects), and POST /api/v3/series/POST /api/v3/movie answer 400 for a
qualityProfileId that does not exist while 0 or absent means that default.
qBittorrent-compatible subset (/api/v2)¶
The /api/v2 routes of the table are the subset of the qBittorrent Web API a
browser extension needs (Torrent Control and similar) to send a magnet link or a
.torrent file to Gextto: the torrent enters the queue and the active engine
downloads it, as if it had been added from the interface. It is a compatibility
layer on Gextto's own address, not a qBittorrent replacement: RSS, the search
plugins, categories, tags, the sync API and the file/peer details are not
implemented, and POST /api/v2/torrents/add refuses category, tags and a
contentLayout other than Original with 400 instead of ignoring them (in
Gextto a category is a download tag, and the content layout decides the folders
on disk, so a silent no-op would put the torrent somewhere else than the client
asked).
- The answers are qBittorrent's: the plain
Ok./Fails.texts and409when the torrent is already there. POST /api/v2/auth/loginaccepts the credentials of the access control and leaves theSIDcookie; while the access control is off, or with the local exemption for a client on the LAN, any credentials are accepted. TheSIDcookie opens only/api/v2: the rest of the interface still needs Gextto's own session.- The login and the logout are reachable without a session (that is how a client authenticates); the other routes need it like the rest of the API.
GET /api/v2/torrents/infohonourshashesand the commonfiltervalues and lists everything else; sorting, paging and the category/tag filters are not part of the subset.- These routes exist whatever engine is active, because Gextto stays the only master: the transfer is handed to gx-core or to qBittorrent-nox by the engine in use.
gx-torrent daemon routes¶
The gx-torrent daemon (also usable standalone, without Gextto) serves its own
API on its listen address (default 127.0.0.1:8890). Two dialects are exposed:
- the native API under
/api/v1; - the qBittorrent-compatible API under
/api/v2, so a client written for qBittorrent can talk to it unchanged. It is mounted only in standalone mode: when Gextto manages the daemon, Gextto is the single master and the whole/api/v2is not exposed (the routes below answer 404).
When the daemon is reachable on the network, protect it with the shared token
gxtorrent_token: the /api/v1 clients send the header X-Gx-Token: <token>,
or log in on the daemon page. The qBittorrent-compatible /api/v2 POSTs do not
use the token; they are protected by an Origin/Referer check instead, so a
qBittorrent client keeps working unchanged. What the token is and how to
generate it is in api-keys.md /
api-keys.it.md. The daemon also serves its own
server-rendered UI under /ui; those HTML routes are not part of this table.
Native API (/api/v1)¶
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/add |
add a torrent (magnet, URL or JSON body); content_layout = Original, Subfolder or NoSubfolder (empty = Original); seed_mode=1 skips the initial check (each piece is checked when first uploaded); file_priorities = comma-separated priorities, one per real file (0 = skip, 4 = normal, 5–7 = high; an all-zero list is refused). A URL that answers with a redirect to a magnet: link — indexarr and Jackett do this for a source that has no .torrent file to serve — is followed and added as that magnet |
| POST | /api/v1/add-file |
add a .torrent file upload (same content_layout, seed_mode and file_priorities) |
| GET | /api/v1/config |
daemon configuration |
| POST | /api/v1/config |
change the daemon configuration |
| POST | /api/v1/create-torrent |
create a .torrent from a folder; JSON fields include format (v1, v2 or hybrid) and source |
| GET | /api/v1/dht-feed |
recent DHT discovery feed |
| GET | /api/v1/dht-feed/publish |
sign and publish a BEP 46 feed (?name=…&hash=…), returns the dht:// URL |
| POST | /api/v1/dht-feed/publish |
sign and publish a BEP 46 feed (JSON name/hashes), returns the dht:// URL |
| GET | /api/v1/events |
engine events (Server-Sent Events) |
| GET | /api/v1/health |
health, version, build and fingerprint |
| POST | /api/v1/ipfilter |
update the IP filter |
| GET | /api/v1/metrics |
Prometheus metrics |
| POST | /api/v1/pins/clear |
clear the pinned torrents |
| GET | /api/v1/portcheck |
check the peer port |
| GET | /api/v1/stats |
session statistics |
| GET | /api/v1/stats/history |
statistics history |
| GET | /api/v1/torrents |
list torrents |
| DELETE | /api/v1/torrents/{hash} |
remove a torrent |
| GET | /api/v1/torrents/{hash} |
one torrent's details |
| POST | /api/v1/torrents/{hash}/{action} |
pause, resume, verify, rename, move, limits, ... |
| GET | /api/v1/torrents/{hash}/{what} |
files, peers or trackers |
The {what} of GET /api/v1/torrents/{hash}/{what} is one of files,
peers, trackers, webseeds, pieces, torrent-file, scrape,
dhtscrape, collections. The {action} of
POST /api/v1/torrents/{hash}/{action} is one of pause, resume,
verify, reannounce, restart, park, unpark, pin, top, rename,
rename-file, rename-folder, move, associate, seed-limits,
conn-limits, super-seeding, file-priorities, trackers, set-trackers,
webseeds.
qBittorrent-compatible API (/api/v2)¶
The daemon speaks the qBittorrent Web API at the 4.6 / API 2.9.3 level, with
the most-used 5.x additions (getFreeSpace, getDirectoryContent,
processInfo, setComment, the 5.x stopped flag). Authentication accepts the
SID login, the page session, or the API key configured in Settings →
Network (X-Api-Key or Authorization: Bearer). app/version reports the
version in Settings → qBittorrent version reported (default 4.6.7), so a
client that checks the version can be pointed at a compatible string without a
rebuild. This is a broad compatibility layer, not a claim that every route,
field and status code of every qBittorrent release is implemented: the gaps are
tracked in gx-core.md §6 and the fields are covered by the qBittorrent tests.
In the RSS API a feed's uid stays the same when the feed is renamed, moved or
given a new address, lastMatch and previouslyMatchedEpisodes are filled from
the daemon's record, and a rule action is read and written both in the legacy
flat fields and in the torrentParams object (where the tags are).
| Method | Path | Description |
|---|---|---|
| GET | /api/v2/app/buildInfo |
version and build info |
| GET | /api/v2/app/cookies |
cookies used for the .torrent fetches |
| GET | /api/v2/app/defaultSavePath |
default save path |
| GET | /api/v2/app/getDirectoryContent |
list a folder for the path pickers (name, is_dir, size) |
| GET | /api/v2/app/getFreeSpace |
free space of a folder in bytes |
| GET | /api/v2/app/networkInterfaceAddressList |
addresses of the interfaces |
| GET | /api/v2/app/networkInterfaceList |
network interfaces |
| GET | /api/v2/app/processInfo |
process launch time |
| GET | /api/v2/app/preferences |
preferences document |
| POST | /api/v2/app/setCookies |
set the cookies |
| POST | /api/v2/app/setPreferences |
change the preferences |
| POST | /api/v2/app/shutdown |
shut the daemon down |
| GET | /api/v2/app/version |
application version |
| GET | /api/v2/app/webapiVersion |
Web API version |
| POST | /api/v2/auth/login |
log in and get the session cookie |
| POST | /api/v2/auth/logout |
log out |
| GET | /api/v2/log/main |
main log |
| GET | /api/v2/log/peers |
peers log |
| POST | /api/v2/rss/addFeed |
add an RSS feed |
| POST | /api/v2/rss/addFolder |
create an RSS folder |
| GET | /api/v2/rss/items |
RSS feeds and folders tree |
| POST | /api/v2/rss/markAsRead |
mark the articles as read |
| GET | /api/v2/rss/matchingArticles |
articles matching a rule |
| POST | /api/v2/rss/moveItem |
move a feed or a folder |
| POST | /api/v2/rss/refreshItem |
refresh a feed now |
| POST | /api/v2/rss/removeItem |
remove a feed or a folder |
| POST | /api/v2/rss/removeRule |
remove a download rule |
| POST | /api/v2/rss/renameRule |
rename a download rule |
| GET | /api/v2/rss/rules |
download rules |
| POST | /api/v2/rss/setFeedRefreshInterval |
set the feed refresh interval |
| POST | /api/v2/rss/setFeedURL |
change a feed URL |
| POST | /api/v2/rss/setRule |
create or update a download rule |
| POST | /api/v2/search/delete |
delete a search (a finished search is also dropped 15 minutes after it stopped, so a client that never deletes it does not pin the results in memory) |
| POST | /api/v2/search/enablePlugin |
enable or disable a plugin |
| POST | /api/v2/search/installPlugin |
install a search plugin |
| GET | /api/v2/search/plugins |
installed search plugins |
| GET | /api/v2/search/results |
results of a search |
| POST | /api/v2/search/start |
start a search |
| GET | /api/v2/search/status |
search status |
| POST | /api/v2/search/stop |
stop a search |
| POST | /api/v2/search/uninstallPlugin |
uninstall a search plugin |
| POST | /api/v2/search/updatePlugins |
update the search plugins |
| GET | /api/v2/sync/maindata |
incremental state for the qBittorrent sync: the diff since the reply whose rid the client echoes (the last 16 replies are remembered, so several clients stay incremental), otherwise a full snapshot |
| GET | /api/v2/sync/torrentPeers |
peers of one torrent |
| POST | /api/v2/torrentcreator/addTask |
start a torrent creation task (sourcePath, pieceSize, private, comment, trackers, urlSeeds, torrentFilePath; torrentFormat = v1/v2/hybrid and source are accepted) |
| POST | /api/v2/torrentcreator/deleteTask |
delete a creation task |
| GET | /api/v2/torrentcreator/status |
creation task status |
| GET | /api/v2/torrentcreator/torrentFile |
download the created .torrent |
| POST | /api/v2/torrents/add |
add torrents (contentLayout, or the older root_folder; none = the torrent_content_layout preference; skip_checking = seed mode); a body over 129 MiB (four .torrent files of the 32 MiB maximum) is refused with 413 |
| POST | /api/v2/torrents/addPeers |
add peers |
| POST | /api/v2/torrents/addTags |
add tags to torrents |
| POST | /api/v2/torrents/addTrackers |
add trackers |
| POST | /api/v2/torrents/addWebSeeds |
add web seeds |
| POST | /api/v2/torrents/bottomPrio |
send torrents to the bottom of the queue |
| GET | /api/v2/torrents/categories |
categories |
| GET | /api/v2/torrents/count |
number of torrents, as text |
| POST | /api/v2/torrents/createCategory |
create a category |
| POST | /api/v2/torrents/createTags |
create tags |
| POST | /api/v2/torrents/decreasePrio |
lower the queue priority |
| POST | /api/v2/torrents/delete |
remove torrents |
| POST | /api/v2/torrents/deleteTags |
delete tags |
| GET | /api/v2/torrents/downloadLimit |
per-torrent download limits |
| POST | /api/v2/torrents/downloadLimit |
set the per-torrent download limits |
| POST | /api/v2/torrents/editCategory |
rename a category |
| POST | /api/v2/torrents/editTracker |
edit a tracker URL |
| POST | /api/v2/torrents/editWebSeed |
replace a web seed |
| GET | /api/v2/torrents/export |
download a .torrent |
| POST | /api/v2/torrents/filePrio |
set the file priorities |
| GET | /api/v2/torrents/files |
files of a torrent |
| POST | /api/v2/torrents/increasePrio |
raise the queue priority |
| GET | /api/v2/torrents/info |
torrent list |
| POST | /api/v2/torrents/pause |
pause torrents |
| GET | /api/v2/torrents/pieceHashes |
piece hashes |
| GET | /api/v2/torrents/pieceStates |
piece states |
| GET | /api/v2/torrents/properties |
torrent properties |
| POST | /api/v2/torrents/reannounce |
reannounce to the trackers |
| POST | /api/v2/torrents/recheck |
recheck the data |
| POST | /api/v2/torrents/removeCategories |
remove categories |
| POST | /api/v2/torrents/removeTags |
remove tags from torrents |
| POST | /api/v2/torrents/removeTrackers |
remove trackers |
| POST | /api/v2/torrents/removeWebSeeds |
remove web seeds |
| POST | /api/v2/torrents/rename |
rename a torrent |
| POST | /api/v2/torrents/renameFile |
rename a file |
| POST | /api/v2/torrents/renameFolder |
rename a folder |
| POST | /api/v2/torrents/resume |
resume torrents |
| POST | /api/v2/torrents/setAutoManagement |
automatic management on or off |
| POST | /api/v2/torrents/setCategory |
set the category |
| POST | /api/v2/torrents/setDownloadLimit |
set a torrent download limit |
| POST | /api/v2/torrents/setComment |
set the torrent comment |
| POST | /api/v2/torrents/setForceStart |
force start on or off |
| POST | /api/v2/torrents/setLocation |
move a torrent to another folder |
| POST | /api/v2/torrents/setShareLimits |
set the share limits |
| POST | /api/v2/torrents/setSuperSeeding |
super-seeding on or off |
| POST | /api/v2/torrents/setUploadLimit |
set a torrent upload limit |
| POST | /api/v2/torrents/start |
start torrents |
| POST | /api/v2/torrents/stop |
stop torrents |
| GET | /api/v2/torrents/tags |
tags |
| POST | /api/v2/torrents/toggleFirstLastPiecePrio |
toggle the first and last piece priority |
| POST | /api/v2/torrents/toggleSequentialDownload |
sequential download on or off |
| POST | /api/v2/torrents/topPrio |
send torrents to the top of the queue |
| GET | /api/v2/torrents/trackers |
trackers of a torrent |
| GET | /api/v2/torrents/uploadLimit |
per-torrent upload limits |
| POST | /api/v2/torrents/uploadLimit |
set the per-torrent upload limits |
| GET | /api/v2/torrents/webseeds |
web seeds of a torrent |
| POST | /api/v2/transfer/banPeers |
ban peers |
| GET | /api/v2/transfer/downloadLimit |
global download limit |
| GET | /api/v2/transfer/info |
global transfer info |
| POST | /api/v2/transfer/setDownloadLimit |
set the global download limit |
| POST | /api/v2/transfer/setSpeedLimitsMode |
set the alternative speed limits mode (1 on, 0 off) |
| POST | /api/v2/transfer/setUploadLimit |
set the global upload limit |
| GET | /api/v2/transfer/speedLimitsMode |
alternative speed limits mode |
| POST | /api/v2/transfer/toggleSpeedLimitsMode |
toggle the alternative speed limits |
| GET | /api/v2/transfer/uploadLimit |
global upload limit |
Total routes: 19 (gx-torrent /api/v1) + 109 (gx-torrent /api/v2).
Integrating with other systems¶
Prometheus and Grafana. GET /api/v1/metrics serves the daemon's state in
the Prometheus text format (0.0.4): transfer rates and lifetime totals,
torrents in total (gxtorrent_torrents), by state
(gxtorrent_torrents_by_state{state="seeding"}, the states are a partition)
and the slow ones as a condition (gxtorrent_slow_torrents, also downloading),
peers, queue slots, caches,
free disk, the IP filter, every engine session counter (gxtorrent_session_*,
uTP and disk counters included) and the process
(process_resident_memory_bytes, process_cpu_seconds_total,
process_start_time_seconds). It is computed only when asked: one scrape costs
about 0.2 ms.
The names follow the Prometheus conventions, so rate() and the standard
dashboards read them correctly: a counter ends in _total (lifetime totals,
process_cpu_seconds_total, the session counters whose key is a _total) and
nothing else does; rates carry their unit
(gxtorrent_download_rate_bytes_per_second); gauges with a unit end in it
(_bytes, _seconds). Every family has one HELP and one TYPE line, label
values are escaped, and the response carries
text/plain; version=0.0.4; charset=utf-8.
With a token, send it as a header:
scrape_configs:
- job_name: gx-torrent
metrics_path: /api/v1/metrics
static_configs: [{ targets: ["192.168.1.10:8890"] }]
# with gxtorrent_token set:
http_headers: { X-Gx-Token: { values: ["<token>"] } }
Events for automations. GET /api/v1/events is a Server-Sent Events
stream of the engine events (a torrent added, completed, failed, a peer
connected, …), one JSON object per data: line, with a comment every 30 s to
keep proxies from closing it. The stream belongs to one engine session: when
gx-torrent reopens it for a setting only the engine reads, the response ends
and the client is expected to reconnect (an EventSource does it by itself),
subscribing to the new session. It is the token-authenticated twin of the
page's /ui/events, for Home Assistant, Node-RED or a script:
curl -N -H 'X-Gx-Token: <token>' http://127.0.0.1:8890/api/v1/events
qBittorrent 5 calls. Besides the qBittorrent Web API v2 surface above, the
daemon answers torrents/count (the number, as text),
transfer/setSpeedLimitsMode (mode=1 turns the alternative limits on, 0
off) and the web seed editing calls: torrents/addWebSeeds and
torrents/removeWebSeeds take hash and urls separated by |;
torrents/editWebSeed takes hash, origUrl and newUrl. They answer 404 for
an unknown torrent, 400 for a URL that is not http(s), 409 when origUrl is not
a web seed of the torrent. torrents/add also accepts the 5.x stopped flag
(4.x clients send paused), and torrents/setShareLimits accepts the 5.2/5.3
shareLimitsMode and shareLimitAction fields, which the daemon ignores because
it has a single share-limit action.