{"info":{"description":"prnt.li is a public file host: no account, no signup. Upload, read metadata, unlock a password-protected file, delete.\n\n**Every endpoint is anonymous by default.** An API key is optional and buys a higher rate limit and an exemption from the large-download throttle \u2014 nothing else. It is currently **not configured on this server**, so every request takes the anonymous path.\n\n**Delete is capability-based.** The token is the permission; anyone holding a link can delete the file, and always could. Treat links as deletable.","license":{"identifier":"LicenseRef-Proprietary","name":"Unlicensed"},"summary":"Upload a file, get a link back.","title":"prnt.li API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/api/config":{"get":{"description":"Derived from the running configuration, so it cannot drift from what is enforced.","operationId":"config","responses":{"responses":{"200":{"description":"Limits."}}},"summary":"Current limits.","tags":["upload"]}},"/api/delete/{token}":{"post":{"description":"Capability-based: the token is the permission and no API key is consulted. Hard delete \u2014 rows, events and bytes.","operationId":"delete","parameters":[{"description":"The 32-hex, dash, 10-alnum token from the file's URL.","in":"path","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"404":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"No such file."},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"freed":{"description":"Bytes reclaimed.","type":"integer"},"ok":{"type":"boolean"}},"type":"object"}}},"description":"Deleted."}}},"summary":"Delete a file, permanently.","tags":["files"]}},"/api/file/{token}":{"get":{"description":"Excluded on purpose: the uploader's IP and user agent, and the password hash and salt.","operationId":"fileInfo","parameters":[{"description":"The 32-hex, dash, 10-alnum token from the file's URL.","in":"path","name":"token","required":true,"schema":{"type":"string"}},{"description":"Optional. Raises the per-IP hourly ceiling tenfold and exempts downloads from the large-file throttle. It does not unlock password-protected files and does not grant delete, which stays capability-based on the token. Also accepted as `Authorization: Bearer <key>`.","in":"header","name":"X-API-Key","required":false,"schema":{"type":"string"}}],"responses":{"400":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Bad request, or every file in the batch was rejected."},"401":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Wrong password."},"404":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"No such file."},"413":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Larger than the per-file limit."},"429":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Hourly limit reached from this address, or too many password attempts."},"503":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Too many uploads already in flight."},"507":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Out of storage or disk space."},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"burn":{"type":"boolean"},"burn_claimed":{"type":"boolean"},"created_at":{"description":"ISO-8601, UTC, Z suffix.","type":"string"},"downloads":{"type":"integer"},"expired":{"type":"boolean"},"expires_at":{"description":"ISO-8601, UTC, Z suffix.","type":["string","null"]},"kind":{"enum":["image","video","audio","file"],"type":"string"},"mime":{"type":"string"},"name":{"type":"string"},"password_protected":{"type":"boolean"},"size":{"type":"integer"},"throttled_serves":{"type":"integer"},"token":{"type":"string"},"url":{"type":"string"},"views":{"type":"integer"}},"type":"object"}}},"description":"The file's metadata."}}},"summary":"Metadata for one file.","tags":["files"]}},"/api/openapi.json":{"get":{"operationId":"openapi","responses":{"responses":{"200":{"description":"OpenAPI 3.1."}}},"summary":"This document.","tags":["upload"]}},"/api/unlock/{token}":{"post":{"description":"A browser gets a cookie from /pw/. A script cannot use a cookie, so it gets the same signed value in a header and sends it back as `X-PRNT-Unlock`. The signature is scoped to this one file. Shares the per-file attempt limiter with the browser form, so the API is not a second, unlimited oracle for the same passwords.","operationId":"unlock","parameters":[{"description":"The 32-hex, dash, 10-alnum token from the file's URL.","in":"path","name":"token","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"properties":{"password":{"type":"string"}},"required":["password"],"type":"object"}}},"required":true},"responses":{"400":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Bad request, or every file in the batch was rejected."},"401":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Wrong password."},"404":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"No such file."},"413":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Larger than the per-file limit."},"429":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Hourly limit reached from this address, or too many password attempts."},"503":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Too many uploads already in flight."},"507":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Out of storage or disk space."},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"expires_in":{"description":"Seconds until it expires.","type":"integer"},"header":{"description":"The header's name.","type":"string"},"ok":{"type":"boolean"},"token":{"type":"string"},"unlock":{"description":"Send this back as the unlock header.","type":"string"}},"type":"object"}}},"description":"Unlocked."}}},"summary":"Exchange a password for an unlock token.","tags":["files"]}},"/api/upload":{"post":{"description":"Accepts two request shapes and picks between them on Content-Type, so a client that can only stream a body does not have to hand-assemble a multipart document.\n\n* `multipart/form-data` \u2014 the primary shape, and what every HTTP client library sends by default.\n* anything else \u2014 the body **is** the file.\n\nBoth go through the same size, quota and storage checks. Expiry accepts `expires_in` (seconds from now) on the raw path and `expires_at` (absolute) on the multipart path; prefer `expires_in`, which does not depend on the client agreeing with the server about what time it is.","operationId":"upload","parameters":[{"description":"Optional. Raises the per-IP hourly ceiling tenfold and exempts downloads from the large-file throttle. It does not unlock password-protected files and does not grant delete, which stays capability-based on the token. Also accepted as `Authorization: Bearer <key>`.","in":"header","name":"X-API-Key","required":false,"schema":{"type":"string"}},{"description":"Display name on a raw-body upload. A header rather than a query parameter because query strings end up in access logs.","in":"header","name":"X-Filename","required":false,"schema":{"type":"string"}},{"description":"Fallback for the header.","in":"query","name":"filename","required":false,"schema":{"type":"string"}},{"description":"Seconds from now. Clamped to 365 days.","in":"query","name":"expires_in","required":false,"schema":{"minimum":1,"type":"integer"}},{"description":"Absolute ISO-8601 expiry.","in":"query","name":"expires_at","required":false,"schema":{"type":"string"}},{"description":"Delete after the first view.","in":"query","name":"burn_after_view","required":false,"schema":{"type":"string"}},{"description":"Protect the file. Note that on the raw path this is a query parameter and therefore lands in access logs \u2014 use multipart if the password matters.","in":"query","name":"password","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"description":"The file itself, as the request body. One file per request; the per-upload file count does not apply.","format":"binary","type":"string"}},"multipart/form-data":{"schema":{"properties":{"burn_after_view":{"description":"Delete the file after its first view.","enum":["1","true","yes","on"],"type":"string"},"expires_at":{"description":"Absolute expiry, ISO-8601. Clamped server-side to one minute and 365 days; out-of-range values are pulled to the nearest bound rather than rejected, so `9999-01-01` is not a way to disable expiry. Defaults to 168h.","type":"string"},"files[]":{"description":"Repeat this part once per file. At most 10.","items":{"format":"binary","type":"string"},"type":"array"},"password":{"description":"Protect the file. Unlock it with POST /api/unlock/{token}.","maxLength":128,"type":"string"}},"required":["files[]"],"type":"object"}}},"description":"Either a multipart body or the raw file."},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"errors":{"items":{"properties":{"error":{"type":"string"},"name":{"type":"string"}},"type":"object"},"type":"array"},"files":{"items":{"properties":{"burn":{"description":"True if this file was set to delete itself after one view.","type":"boolean"},"kind":{"enum":["image","video","audio","file"],"type":"string"},"name":{"description":"Display name, sanitised.","type":"string"},"size":{"description":"Bytes.","type":"integer"},"token":{"description":"The token in the URL. Also the delete capability.","type":"string"},"url":{"description":"Absolute URL to serve the file. This is the thing you give to somebody.","type":"string"}},"type":"object"},"type":"array"},"urls":{"items":{"type":"string"},"type":"array"}},"type":"object"}}},"description":"At least one file was stored. A batch can partially fail: check `errors` even on a 200, because one oversized file does not cost you the others."},"400":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Bad request, or every file in the batch was rejected."},"401":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Wrong password."},"404":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"No such file."},"413":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Larger than the per-file limit."},"429":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Hourly limit reached from this address, or too many password attempts."},"503":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Too many uploads already in flight."},"507":{"content":{"application/json":{"schema":{"description":"A human-readable message. Safe to show a user.","properties":{"error":{"type":"string"}},"required":["error"],"type":"object"}}},"description":"Out of storage or disk space."}},"summary":"Upload one or more files.","tags":["upload"]}}},"servers":[{"url":"https://prnt.li"}],"tags":[{"description":"Getting bytes in.","name":"upload"},{"description":"Reading and removing what you stored.","name":"files"}]}
