Skip to content

HTTP API Reference

Stephen Cox edited this page Sep 28, 2026 · 5 revisions

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)

Authentication

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.

POST /api/execute

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.

GET /api/jobs

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.

GET /api/jobs/<job_id>

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.

DELETE /api/jobs/<job_id>

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" }

GET /api/health

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.

GET /api/stats

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"
}

GET /api/version

No authentication required.

{ "version": "0.5.9", "api_version": "1.0", "python_version": "3.11.7" }

Job Lifecycle

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.

Polling for Completion

/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 .result

Error Response Format

Every 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

GitHub • Releases


📚 Getting Started

📖 Core Concepts

🔒 Security

🔧 Configuration

🆘 Help & Support

🧑‍💻 For Developers


🔑 Key Features

  • 🚀 Client-server architecture (HTTP API + worker)
  • 📦 Persistent job queue (SQLite or Redis)
  • ♻️ Reusable references ($ref, ${vars.x})
  • 🐳 Docker-first deployment

🔗 Quick Links

Clone this wiki locally