Repository navigation
HTTP API Reference
Complete reference for the HTTP API the server exposes (server.py) — this is what the CLI client, webhooks, and cron submitters all talk to underneath. For the higher-level "how do I submit a job" walkthrough, see Contexts; this page is the endpoint-by-endpoint detail.
This page covers the JSON /api/* endpoints. The server also exposes a human-facing, server-rendered /dashboard* (no /api/ prefix, returns HTML not JSON) — see Dashboard — and, when enabled, a Prometheus /metrics endpoint in text exposition format rather than JSON — see Metrics.
Base URL: http://localhost:5000 (or whatever server.host/server.port you've configured)
Every endpoint except /api/health and /api/version requires the API key, via either:
# Query parameter
curl "http://localhost:5000/api/execute?context=nightly&key=YOUR_KEY"
# Header (equivalent, keeps the key out of logs/URLs)
curl -X POST -H "X-API-Key: YOUR_KEY" "http://localhost:5000/api/execute?context=nightly"A missing or incorrect key returns 401 Unauthorized — see Security for key management guidance.
Queue a job for execution.
| Query Parameter | Required | Description |
|---|---|---|
context |
No | Context string to match against rules' context: field. Omit for context-agnostic rules only |
hash |
No | Restrict evaluation to a single torrent (its 40-char hex hash) |
key |
Yes* | API key (*or use the X-API-Key header instead) |
Response — 202 Accepted:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"context": "nightly",
"hash": null,
"status": "pending",
"created_at": "2026-01-15T10:30:00.123456",
"started_at": null,
"completed_at": null,
"result": null,
"error": null
}Errors: 400 invalid parameters, 401 unauthorized, 500 internal error.
List jobs with filtering and pagination.
| Query Parameter | Required | Description |
|---|---|---|
status |
No | One of pending, processing, completed, failed, cancelled
|
context |
No | Filter by context |
limit |
No | Max results, default 50
|
offset |
No | Pagination offset, default 0
|
key |
Yes | API key |
Response — 200 OK:
{
"total": 150,
"limit": 50,
"offset": 0,
"jobs": [ { "job_id": "...", "context": "nightly", "status": "completed", "...": "..." } ]
}An invalid status value returns 400.
Fetch a single job by its UUID. 200 with the job object, or 404 if it doesn't exist.
A completed job's result (v0.7+) looks like:
{
"total_torrents": 42,
"torrents_processed": 3,
"rules_matched": 4,
"actions_executed": 3,
"actions_skipped": 1,
"errors": 0,
"dry_run": false,
"by_rule": {
"force-start-public-capped": { "matched": 2, "actions_executed": 2, "actions_skipped": 0, "errors": 0 },
"tag-private-trackers": { "matched": 2, "actions_executed": 1, "actions_skipped": 1, "errors": 0 }
}
}by_rule is keyed by each rule's id --
see Rules-Architecture. Every
enabled rule that ran appears here, even with all-zero counts, if it matched
nothing; a disabled rule never appears. Jobs completed before v0.7 won't have
this key at all -- treat its absence the same as an empty object.
Cancel a pending job. 200 on success; 400 if the job has already moved past pending (processing/completed/failed/cancelled jobs can't be cancelled); 404 if the job doesn't exist.
{ "job_id": "550e8400-...", "status": "cancelled", "message": "Job cancelled successfully" }No authentication required — safe to use as a Docker/Compose healthcheck or load balancer probe.
Response — 200 OK (healthy):
{
"status": "healthy",
"version": "0.5.9",
"queue": { "backend": "SQLiteQueue", "pending_jobs": 2, "processing_jobs": 0 },
"worker": { "status": "running", "last_job_completed": "2026-01-15T10:25:00" },
"timestamp": "2026-01-15T10:30:00.000000"
}Response — 503 (unhealthy) if the queue backend is unreachable, the worker thread has died, or more than 5 jobs are stuck in processing at once — the errors array names which.
Aggregate counters and performance data.
{
"jobs": { "total": 500, "pending": 1, "processing": 0, "completed": 490, "failed": 8, "cancelled": 1 },
"performance": { "average_execution_time": "1.24s" },
"queue": { "backend": "SQLiteQueue", "depth": 1 },
"worker": { "status": "running", "last_job_completed": "2026-01-15T10:25:00" },
"timestamp": "2026-01-15T10:30:00.000000"
}No authentication required.
{ "version": "0.5.9", "api_version": "1.0", "python_version": "3.11.7" }pending → processing → completed or failed. A pending job can additionally move to cancelled via DELETE /api/jobs/<job_id>; once a job leaves pending, cancellation is no longer possible — let it finish and check the result instead.
/api/execute returns immediately (202) with a pending job — it doesn't wait for the rules to actually run. To get the result synchronously, either use the CLI client's --wait flag, or poll:
JOB_ID=$(curl -s -X POST "http://localhost:5000/api/execute?context=nightly&key=$KEY" | jq -r .job_id)
until [ "$(curl -s "http://localhost:5000/api/jobs/$JOB_ID?key=$KEY" | jq -r .status)" != "pending" ] && \
[ "$(curl -s "http://localhost:5000/api/jobs/$JOB_ID?key=$KEY" | jq -r .status)" != "processing" ]; do
sleep 1
done
curl -s "http://localhost:5000/api/jobs/$JOB_ID?key=$KEY" | jq .resultEvery non-2xx response follows the same shape:
{ "error": "Unauthorized", "message": "Invalid or missing API key" }| Status | Meaning |
|---|---|
400 |
Invalid request parameters (bad status filter, etc.) |
401 |
Missing or incorrect API key |
404 |
Job ID (or endpoint) not found |
500 |
Unhandled server-side error — check docker logs
|
503 |
/api/health reporting the server itself is unhealthy |
qbt-rules | Client-server automation engine for qBittorrent
GitHub • Docker Image • Releases • Issues • Discussions • Contributing • License
Requirements: Docker • qBittorrent with Web UI enabled
Released into the public domain under the Unlicense
- Rules Architecture
- Contexts
- Conditions
- Actions
- Available Fields
- Reusable References
- HTTP API Reference
- Dashboard
- Metrics
- 🚀 Client-server architecture (HTTP API + worker)
- 📦 Persistent job queue (SQLite or Redis)
- ♻️ Reusable references (
$ref,${vars.x}) - 🐳 Docker-first deployment