More seeders
Users start seeding content they'd already downloaded but disconnected from the tracker. Old releases get new seeders instantly.
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.
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.
Users start seeding content they'd already downloaded but disconnected from the tracker. Old releases get new seeders instantly.
The client sends only filenames, sizes and SHA1 of the first 32 MB. Actual files never leave the user's computer.
FastAPI + SQLite in WAL mode. Holds hundreds of thousands of torrents on any cheap VPS. Auto-backup with rotation included.
Four HTTP endpoints, JSON-based protocol. Auth and rate limits belong to the tracker (reverse proxy or your own middleware).
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.
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.
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.
The ReSeeder client calls endpoints sequentially. Each step is independent and idempotent.
POST /match/handshake — handshakeThe 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.
POST /match/phase1 — match by name + sizeThe 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.
POST /match/phase2 — match by size onlyFiles 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.
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.
All requests are application/json, all responses are JSON. Authorization is delegated to the tracker.
{
"status": "ok",
"stats": {
"torrents": 182540,
"files": 1248301,
"scan_paths": 3
}
}
{
"user_id": "12345"
}
{
"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).
{
"user_id": "12345",
"phase": 1,
"files": [
{"name": "movie.mkv", "size": 4294967296},
{"name": "show.s01e01.mkv", "size": 1073741824}
]
}
{
"status": "ok",
"matched_torrents": [
{
"torrent_id": 42,
"info_hash": "abc123...",
"name": "Movie Name",
"torrent_file": "<base64 .torrent>"
}
],
"matched_files_count": 1
}
{
"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.
{
"status": "ok",
"matched_torrents": [ ... ],
"matched_files_count": 3
}
{
"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.
{"status": "ok"}
{"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.
Five commands minimum. For production — systemd unit in the next section.
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).
Recommended setup: two systemd services — API and watcher. Nginx as a reverse proxy for HTTPS and tracker-side rate limiting.
/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
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
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
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/.
Stored in the settings table. Edit via GUI or SQL directly. Reloaded on every request — no restart needed.
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.
How many files the client can send in one request. Default: 10000.
How many torrents are returned in a single response. Default: 500.
Auto-backup interval for the DB (online, via SQLite Backup API). 0 disables it. Production recommendation: 6.
Backup folder and retention count. A relative path is resolved against the DB directory — works identically on Linux and Windows.
If 1, SHA1 is computed for every .torrent during scanning and duplicates are grouped in the log. Slows down scanning on large collections.
The archive contains all Python sources, README and requirements.txt. MIT license — use and modify freely.
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.
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.