API

Everything the web page does, over plain HTTP. No keys, no rate limits, no signup. Every endpoint returns JSON unless it returns bytes.

Upload in one request

curl -T ./movie.mkv https://files.nitaimaarek.com/
curl --data-binary @dump.log https://files.nitaimaarek.com/dump.log
curl -F file=@photo.jpg https://files.nitaimaarek.com/api/form

The response is JSON: {"id":"aB3xY9k","url":"…/f/aB3xY9k","download":"…/d/aB3xY9k","token":"…"}. Add ?raw=1 to get just the URL as text — handy in shell pipelines.

Query options (all optional)

expires0 · 1h · 1d · 7d · 30d · or seconds
listed0 keeps it off the public Browse page
passpassword required to download
noteshort description shown on the file page
nameoverride the stored filename

Resumable upload

What the browser uses. Survives a dropped connection at any size.

POST /api/new            {"name":"x.iso","size":123,"expires":0,"pass":"","listed":true}
                         -> {"id":"…","token":"…","offset":0}
PUT  /api/put/<id>?token=…&offset=N     raw bytes appended at N
GET  /api/status/<id>?token=…          -> {"offset":N}   (where to resume)
POST /api/done/<id>?token=…           -> the finished file's JSON

Import from a torrent

curl -X POST -d '{"magnet":"magnet:?xt=urn:btih:…"}' https://files.nitaimaarek.com/api/torrent
curl -X POST -d '{"url":"https://…/thing.torrent"}'    https://files.nitaimaarek.com/api/torrent
curl -X POST --data-binary @thing.torrent              https://files.nitaimaarek.com/api/torrent

All three answer {"job":"…"} and are polled at /api/import/<job> like a URL import. A magnet pasted into /api/import is routed here too. Every file inside the torrent becomes a normal stored file with its own id and delete token; the job's files array lists them all. Upload options (expires, listed, pass, note) work as query parameters and apply to each one.

Limits: 9 GB and 200 files per torrent, and it still has to fit in the free space. A .torrent is measured before anything is fetched; a magnet the moment its metadata arrives. An import gives up after 10 minutes with no peers, or 30 minutes without progress. The box does not keep seeding what it pulled — the stored copy gets its own torrent, and that is what it seeds.

Import a URL

POST /api/import   {"url":"https://…","expires":"7d"}  -> {"job":"…"}
GET  /api/import/<job>   -> {"state":"running","got":123,"total":456}

The transfer runs on the server, so closing the tab does not stop it.

Browse from a terminal

curl https://files.nitaimaarek.com/list
curl 'https://files.nitaimaarek.com/list?page=3'
curl 'https://files.nitaimaarek.com/list?kind=video&per=50'

Plain text, ten public files a page, newest first — an id, a size, an age and a name per line. ?page=N walks back through the rest; a page past the end comes back empty rather than failing, so a loop just stops. The same filters as the Browse page apply (q, kind, sort), ?per=N sets the page size up to 500, and ?format=json returns the JSON shape instead.

Torrents and magnet links

Any file that is not password-protected can be handed to a torrent client:

curl https://files.nitaimaarek.com/m/<id>      # the magnet URI, one line
curl -O https://files.nitaimaarek.com/t/<id>   # the .torrent file

The .torrent carries this server as a web seed (BEP 19), so a client pulls the bytes straight over HTTP with nobody else in the swarm — and anyone who has the file then seeds it to the next person. Public trackers are listed so a swarm can form. Hashing happens once, on the first request, and is cached from then on; the first call on a very large file takes a while.

A magnet holds no metadata of its own — a client has to get that from a peer — so this box runs a seed for every torrent it hands out, announcing to the trackers above and on the DHT. That is what makes a magnet work from cold, with no web seed involved. The magnet also carries ws, xs and as as HTTP fallbacks.

Public files are hashed in the background, so the magnet is ready before anyone asks. An unlisted file is hashed the first time someone asks for its magnet, and joins the swarm at that point. Password-protected files are never hashed, seeded or announced. Two uploads of identical bytes under the same name are one torrent — same infohash, same swarm.

GET /m/<id>magnet URI as text/plain
GET /t/<id>the .torrent file
GET /api/magnet/<id>JSON: magnet, infohash, webseed, trackers, piece_length
GET /d/<id>/<name>the bytes under a URL ending in the filename — this is the web seed

Download

GET /d/<id>attachment, honours Range — resume with curl -C -
GET /r/<id>inline with the real content type, for hotlinking and embeds
GET /api/zip?ids=a,b,cstreams one zip64 archive of several files, with a real Content-Length
GET /api/file/<id>metadata as JSON
GET /list?page=public listing as plain text, ten a page — for curl
GET /api/list?q=&kind=&sort=&limit=&page=public listing as JSON, with page, pages and a next link
GET /api/statscounts, bytes stored, free space

Password-protected files take ?pass=… on any of those.

Delete

POST /api/delete/<id>?token=<token from upload>

The token is the only way to delete a file, and it is only ever shown to the uploader. Lose it and the file stays until it expires.

Limits

No cap on a single file, no cap on how many, no throttling. The one hard rule is that uploads stop while the disk has less than 3 GB free, so the box never fills up under the other services running on it. Check /api/stats for current headroom.