API for trackers

↓ Download utility

ReSeeder API is a Python server-side utility that indexes a tracker's .torrent files and returns matched releases to users based on their media-library signatures. Users plug in their files — they get torrents, no manual searching. Two integration tiers are supported: an archive of the user's personal download history and the full match API.

⊙ Python 3.10+ ⊙ FastAPI + SQLite ⊙ Linux / Windows ⊙ Open Source

Why a tracker wants this

A match between what the user already has on disk and what's being seeded on the tracker. No files are sent to the server — only signatures.

More seeders

Users start seeding content they'd already downloaded but disconnected from the tracker. Old releases get new seeders instantly.

Privacy

The client sends only filenames, sizes and SHA1 of the first 32 MB. Actual files never leave the user's computer.

Low resource footprint

FastAPI + SQLite in WAL mode. Holds hundreds of thousands of torrents on any cheap VPS. Auto-backup with rotation included.

Simple integration

Four HTTP endpoints, JSON-based protocol. Auth and rate limits belong to the tracker (reverse proxy or your own middleware).

Two integration tiers

A tracker doesn't have to deploy the full match API upfront. Start with the "lite" tier — give each user an archive of their own download history; matching runs on the client side.

Lite · user history archive

For the tracker: a single authenticated endpoint returning an archive of .torrent files for the releases this specific user has downloaded. Most trackers already have this data — the "my downloads" page in a profile. No database, no indexing, no background service.

For the user: the ReSeeder client authenticates using whichever method the tracker supports (passkey, login/password, or API key), downloads the personal archive, and runs the match against the local media library. The archive is small (tens/hundreds of torrents, not millions), private, and matched strictly against "their own" releases.

Full · match API

For the tracker: deploy the FastAPI utility (documented below). Indexing, database, four HTTP endpoints, authorization via reverse proxy.

For the user: the client sends only signatures (filename, size, SHA1 of the first 32 MB), the server replies with ready-to-use .torrent files. Catches releases beyond the user's personal history — including things downloaded from other trackers.

Both tiers are declared in trackers.json through the archive and match sections. A tracker may support just one, both, or neither. The UI buttons are always present — clicking an unsupported capability just writes a log entry saying the feature is not implemented for this tracker.

How it works

The ReSeeder client calls endpoints sequentially. Each step is independent and idempotent.

1

POST /match/handshake — handshake

The client receives utility parameters: minimum file size, per-request and per-response limits. It immediately knows how many files to send at once and which filters to apply locally.

2

POST /match/phase1 — match by name + size

The client sends a list of files with names and sizes. The server returns matched .torrent files (base64 with metadata). The fast path for files with their original names.

3

POST /match/phase2 — match by size only

Files not found in phase 1 (e.g. renamed) are sent without names. Matches are verified by the client using SHA1 on piece chunks — the server returns all torrents with a matching size, and the client picks the right one.

4

POST /match/report — report (optional)

The client sends the match result — what was successfully confirmed. The server logs it for statistics (retention, popular releases). Does not affect the client's workflow.

API reference

All requests are application/json, all responses are JSON. Authorization is delegated to the tracker.

GET /health Health check + DB stats

Response

{
  "status": "ok",
  "stats": {
    "torrents": 182540,
    "files": 1248301,
    "scan_paths": 3
  }
}
POST /match/handshake Utility parameters for the client

Request

{
  "user_id": "12345"
}

Response

{
  "status": "ok",
  "min_file_size": 104857600,
  "max_files_per_request": 10000,
  "max_torrents_per_response": 500
}

user_id is the tracker-provided user identifier. Used only for logging (match_log).

POST /match/phase1 Match by name + size

Request

{
  "user_id": "12345",
  "phase": 1,
  "files": [
    {"name": "movie.mkv", "size": 4294967296},
    {"name": "show.s01e01.mkv", "size": 1073741824}
  ]
}

Response

{
  "status": "ok",
  "matched_torrents": [
    {
      "torrent_id": 42,
      "info_hash": "abc123...",
      "name": "Movie Name",
      "torrent_file": "<base64 .torrent>"
    }
  ],
  "matched_files_count": 1
}
POST /match/phase2 Match by size only

Request

{
  "user_id": "12345",
  "phase": 2,
  "files": [{"size": 4294967296}],
  "exclude_torrent_ids": [42, 55]
}

exclude_torrent_ids — IDs of torrents already returned in phase 1. The server will not repeat them.

Response

{
  "status": "ok",
  "matched_torrents": [ ... ],
  "matched_files_count": 3
}
POST /match/report Client-side result report

Request

{
  "user_id": "12345",
  "confirmed": [{"info_hash": "abc123...", "pieces_verified": 12}],
  "rejected":  [{"info_hash": "def456..."}]
}

Body shape is arbitrary — the server accepts any JSON and logs it. Used for match-quality statistics.

Response

{"status": "ok"}
POST Errors Error format for phases 1/2
{"status": "error", "code": "TOO_MANY_FILES",  "message": "Maximum 10000 files"}
{"status": "error", "code": "INVALID_REQUEST", "message": "..."}
{"status": "error", "code": "SERVER_ERROR",    "message": "Internal utility error"}

HTTP status: 400 for validation/business-logic errors, 500 for unhandled exceptions.

Install and run

Five commands minimum. For production — systemd unit in the next section.

Quick start

cd reseeder_api
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# initial scan of a folder with .torrent files
python -m reseeder_api --scan /var/trackers/torrents

# stats
python -m reseeder_api --stats

# start the API
python -m reseeder_api --serve --host 0.0.0.0 --port 8080

Swagger UI is available at http://<host>:8080/docs. The database is created automatically on first run (default: reseeder_api.db in the working directory, overridable via the --db flag).

Linux deployment

Recommended setup: two systemd services — API and watcher. Nginx as a reverse proxy for HTTPS and tracker-side rate limiting.

systemd: API server

/etc/systemd/system/reseeder-api.service

[Unit]
Description=ReSeeder API — tracker matching utility
After=network.target

[Service]
Type=simple
User=reseeder
Group=reseeder
WorkingDirectory=/opt/reseeder_api
ExecStart=/opt/reseeder_api/.venv/bin/python -m reseeder_api \
    --db /var/lib/reseeder_api/reseeder_api.db \
    --serve --host 127.0.0.1 --port 8080
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

systemd: watcher

A separate service for real-time monitoring of .torrent folders. Uses inotify via watchdog.

[Unit]
Description=ReSeeder API — torrent folder watcher
After=network.target

[Service]
Type=simple
User=reseeder
Group=reseeder
WorkingDirectory=/opt/reseeder_api
ExecStart=/opt/reseeder_api/.venv/bin/python -m reseeder_api \
    --db /var/lib/reseeder_api/reseeder_api.db --watch
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Activation

sudo useradd -r -s /bin/false reseeder
sudo mkdir -p /var/lib/reseeder_api
sudo chown reseeder:reseeder /var/lib/reseeder_api
sudo systemctl daemon-reload
sudo systemctl enable --now reseeder-api reseeder-watch

Reverse proxy (nginx)

location /reseeder_api/ {
    proxy_pass http://127.0.0.1:8080/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    # add rate limiting and authorization here
}

For large collections (hundreds of thousands of .torrent files) raise the inotify limit: fs.inotify.max_user_watches=524288 in /etc/sysctl.d/.

Utility settings

Stored in the settings table. Edit via GUI or SQL directly. Reloaded on every request — no restart needed.

min_file_size

Minimum file size considered for matching (bytes). Sent to the client in handshake — the client filters its own media files locally. Does not affect indexing. Default: 100 MB.

max_files_per_request

How many files the client can send in one request. Default: 10000.

max_torrents_per_response

How many torrents are returned in a single response. Default: 500.

backup_interval_hours

Auto-backup interval for the DB (online, via SQLite Backup API). 0 disables it. Production recommendation: 6.

backup_dir / backup_keep

Backup folder and retention count. A relative path is resolved against the DB directory — works identically on Linux and Windows.

hash_duplicates_log

If 1, SHA1 is computed for every .torrent during scanning and duplicates are grouped in the log. Slows down scanning on large collections.

Download utility

The archive contains all Python sources, README and requirements.txt. MIT license — use and modify freely.

reseeder_api · version 1.0

FastAPI + SQLite, CLI plus an optional Tkinter GUI (5 tabs: scanning, statistics, settings, backup, API testing). The archive is ~35 KB; after pip install it's ≈ 50 MB with dependencies.

Integration questions

Need help deploying on your tracker or want to join the default-supported list — get in touch.

Happy to help with deployment, write middleware for your tracker's auth, or add support for non-standard .torrent storage layouts.