TSP torrent search API
One file, run as a Cloudflare Worker, that searches 10 public torrent indexes at once and answers in TSP, the Torrent Search Protocol. Free tier is enough. No account here, no build step.
1Take your key
Generated in this page, in your browser. Nothing is sent anywhere; reload and you get a different one.
Save it. It is what your client sends; the Worker's own page shows it too.
2Deploy it
With a Cloudflare account: one click
- Sign in, or make an account. Cloudflare shows you the Worker it is about to create: this file, your key already in it.
- Deploy.
- Open the Worker's URL. It shows its address, its key, and a search you can click.
That link is the one the Workers Playground's own Deploy button makes, with this file inside it.
Your key travels in the part after #, which a browser keeps to itself and never sends
to a server; Cloudflare's page reads it there, exactly as it would from a paste.
By hand: paste
This is worker.js with your key already in it, about 80 KiB of readable JavaScript, no dependencies.
- Go to Workers & Pages → Create → Worker, name it, Deploy.
- Edit code, select everything in the editor, paste over it, Deploy again.
- Open the Worker's URL. It shows its address, its key, and a search you can click.
Whichever way, that is all. The Worker holds no secrets beyond the key, keeps nothing, and stores nothing:
each request is asked, merged and forgotten. Its page shows the key to anyone who opens the URL, so the
address is the secret; if you would rather it did not, set TSP_SHOW_KEY=0 and it shows the key
only when the URL carries it.
3Use it
https://your-worker.workers.dev/api/v1/search?q=ubuntu&apikey=…What it answers
{ "query": "ubuntu", "count": 87, "torrents": [ { "name": "…", "infohash": "…", "magnet": "magnet:?xt=urn:btih:…", "size_bytes": 6343219200, "seeders": 165, "leechers": 12, "category": "software", "sources": ["knaben", "piratebay"] } ], "engines": ["knaben", "piratebay", "…"] }Other routes
/api/v1/indexers | what it can search, and what is on |
/api/v1/health | which catalogue it is running, no key needed |
/api/v1/try?d=… | run a descriptor that is not in the catalogue yet |
Settings, if you want them
TSP_APIKEY | the key. The deploy form sets it; a pasted file has it baked in; this overrides either |
TSP_SHOW_KEY | 0 to show the key only to a request that already carries it |
TSP_INDEXES | search only these, by id, comma separated; this also turns on anything switched off |
TSP_NSFW | 0 to leave adult indexes out; they are searched by default |
TSP_BROWSE | 0 to answer an empty query with nothing instead of browsing |
TSP_LIMIT | most rows per index (default 100) |
TSP_TIMEOUT | seconds to wait on one index (default 8) |
TSP_FEED_URL | a catalogue of your own |
TSP_FEED | 0 to pin the compiled catalogue and never refetch |
TSP_ALSO | search these too, by id, even where the catalogue has them off; unlike TSP_INDEXES it does not freeze the list |
TSP_CACHE | seconds a merged answer is kept and paged from (default 600); 0 for none. Only a custom domain has a working cache; on workers.dev every search is fresh |
Set them under Settings → Variables and Secrets. Each one takes effect on the next request, and they survive a rebuild. The same file also runs as a service for strangers, with keys it signs and hands out itself; the README's "Hosting it for others" says how.
What it searches
10 indexes are on by default, out of 14 this project describes.
The list comes from prajwalch/TorrentSearch,
which tracks 43 sites, read at 2d718a34a7e1.
| AnimeTosho | json | anime |
| Dmhy | rss | anime, books, games |
| Eztv | json | series |
| Knaben | json | anime, apps, books |
| ThePirateBay | json | apps, books, games |
| Rutor | html | anime, apps, books |
| Sukebei adult | rss | porn |
| TorrentDownloads | rss | anime, apps, books |
| TorrentsCSV | json | other |
| Yts | json | movies |
A deployed Worker refetches this list hourly, so indexes that are fixed, added or retired reach it without you doing anything. What is compiled into the file is only what it falls back to when the feed cannot be reached.