Upload a file, get a link back. No account, no signup, and no credential required — the API is as anonymous as the site.
# upload one or more files
curl -F 'files[]=@photo.jpg' https://prnt.li/api/upload
# upload from something that can only stream a body
curl --data-binary @movie.mp4 'https://prnt.li/api/upload?filename=movie.mp4'
# what is this file?
curl https://prnt.li/api/file/<token>
# delete it
curl -X POST https://prnt.li/api/delete/<token>
| Route | What it does |
|---|---|
| POST /api/upload | Upload files. Multipart, or a raw body. |
| GET /api/file/<token> | Metadata: name, size, type, expiry, view and download counts. |
| POST /api/delete/<token> | Delete permanently. |
| POST /api/unlock/<token> | Exchange a password for an unlock token. |
| GET /api/config | The limits currently in force. |
| GET /api/openapi.json | OpenAPI 3.1 — machine-readable, for generators. |
Multipart is the primary one, because that is what every HTTP client
library sends by default. A raw body is also accepted for clients
that would otherwise have to hand-assemble a multipart document to
move one file. They are told apart by Content-Type, and
both go through the same size, quota and storage checks.
A raw upload carries one file, so the per-upload file
count does not apply to it. The display name comes from the
X-Filename header, or
?filename= as a fallback — a header, because query
strings end up in access logs.
An API key is optional and buys exactly two things: the per-IP hourly ceiling rises tenfold, and downloads are exempt from the large-file throttle.
not configured here This server currently has no keys set, so every request takes the anonymous path.
What a key deliberately does not buy:
Send one as X-API-Key or
Authorization: Bearer <key>.
/pw/ hands out a cookie. A script cannot use a cookie,
so POST /api/unlock/<token> returns the same
signed value in a header:
UNLOCK=$(curl -s -d 'password=hunter2' https://prnt.li/api/unlock/<token> \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["unlock"])')
curl -H "X-PRNT-Unlock: $UNLOCK" https://prnt.li/f/<token>/secret.pdf
The signature is scoped to that one file. That is
load-bearing: the cookie version signs a bare 1 and
relies on the cookie's name to scope it, and a header has no
name — so an unscoped signature would unlock every
password-protected file on the host at once.
Errors are {"error": "..."} — a flat object, a single
string, safe to show a user.
| Code | Meaning |
|---|---|
| 400 | Bad request, or every file in the batch was rejected. |
| 401 | Wrong password. |
| 404 | No such file. |
| 413 | Larger than the per-file limit. |
| 429 | Hourly limit reached, or too many password attempts. |
| 503 | Too many uploads already in flight. |
| 507 | Out of storage or disk space. |
A batch can partially fail and still return 200. One
oversized file does not cost you the other nine, so an
errors array is present on success and must be checked
even then. Only a request where nothing was stored returns
400.
| Value | |
|---|---|
| per file | 1024 MiB |
| files per upload | 10 |
| per IP | 20/hour |
| expiry, clamped to | 365 days |
Expiry is clamped rather than trusted: an anonymous client sending
9999-01-01 would otherwise have a way to disable it.
An inbox.li project, hosted in Norway.