# imgdb.io API Image hosting with direct links. Upload an image over HTTP, get back a URL that can be hotlinked anywhere. No account, no API key, no SDK. Base URL: https://imgdb.io/api/v1 All requests and responses are plain HTTP; every response body is JSON except the images themselves. ## Authentication None. The API is open. Limits are counted per IP address. If an operator of a self-hosted copy sets WAICORE_API_KEYS, callers must send either `Authorization: Bearer ` or `X-API-Key: ` and get 401 without one. On https://imgdb.io no key is needed. ## Limits - 50 requests per minute, per IP. - 250 stored images per day, per IP. An album charges this once per photo it stores. - 16 MB per image. - 64 images per album. - 150 MB per request, enforced by the reverse proxy. - A 429 response carries a `Retry-After` header, in seconds. ## Accepted formats PNG, JPG, GIF, WEBP, AVIF and BMP. BMP is converted to PNG. Every image is re-encoded on arrival, which strips EXIF and anything appended after the image data. SVG is rejected. Links that live longer than 72h are compressed harder. ## Link lifetimes `ttl` is a number of seconds and must be one of these. Anything else falls back to 259200. `0` means the link never expires. | ttl | meaning | |-----|---------| | 3600 | 1h | | 7200 | 2h | | 18000 | 5h | | 43200 | 12h | | 86400 | 24h | | 259200 | 72h (default) | | 604800 | 7d | | 1209600 | 14d | | 2592000 | 30d | | 7776000 | 90d | | 0 | ∞ | --- ## POST /api/v1/upload Stores one image and returns a direct link. Send the image either as `multipart/form-data` with a `file` field, or as the raw bytes in the request body with a matching `Content-Type` (`image/png`, `image/jpeg`, `image/gif`, `image/webp`, `image/avif`). Parameters, all optional: | name | where | meaning | |------|-------|---------| | `ttl` | query string or form field | link lifetime in seconds, from the table above | | `once` | query string or form field | `1` makes a one-time link: the image is deleted the moment it is viewed | | `password` | `X-Upload-Password` header, or form field | puts the image behind a password gate. Prefer the header: query strings end up in server logs | A password takes precedence over `once` if both are sent. Response `201`: ```json { "url": "https://imgdb.io/i/Ym5TEo4.png", "id": "Ym5TEo4.png", "kind": "image", "type": "image/png", "size": 12345, "expires": 1782509812 } ``` - `kind` is `image` for a plain link, `protected` for a password gate, or `once` for a one-time link. - `id` is the identifier inside `url`. Only `kind: "image"` uploads can be put into an album. - `expires` is epoch seconds, or `null` for a link that never expires. Request: ```bash curl -F "file=@photo.png" \ "https://imgdb.io/api/v1/upload?ttl=86400" ``` --- ## POST /api/v1/album One link for up to 64 images. Two ways to send it. **Files in one request** - `multipart/form-data` with repeated `file` fields, plus the optional `ttl` and `password`: ```bash curl -F "file=@a.png" -F "file=@b.jpg" -F "file=@c.gif" \ "https://imgdb.io/api/v1/album?ttl=604800" ``` **Ids of images already uploaded** - `application/json`. Use this past the 150 MB request cap, since each image then gets its own 16 MB budget: ```bash curl -X POST https://imgdb.io/api/v1/album \ -H "Content-Type: application/json" \ -d '{"items":["Ym5TEo4.png","Rk2p1Za.jpg"],"ttl":604800}' ``` Response `201`: ```json { "url": "https://imgdb.io/a/Ym5TEo4", "id": "Ym5TEo4", "count": 2, "expires": 1783114612, "protected": false, "items": ["Ym5TEo4.png", "Rk2p1Za.jpg"] } ``` Things that bite: - In the two-step flow each image keeps the lifetime it was uploaded with. Give the images at least the album's `ttl`, or the album will outlive its own contents. - A password moves the images into a private folder reachable only through the album's gate page, and the response then omits `items`. - Album members must be plain image uploads. One-time and password-protected images cannot be grouped. --- ## GET /api/v1/info/ Metadata for an image or an album, without downloading it. `` is what `/upload` or `/album` returned as `id`. ```bash curl https://imgdb.io/api/v1/info/Ym5TEo4.png ``` ```json { "kind": "image", "id": "Ym5TEo4.png", "url": "https://imgdb.io/i/Ym5TEo4.png", "type": "image/png", "size": 12345, "expires": 1782509812 } ``` An album answers with `kind`, `count`, `expires`, `protected` and, when it is not protected, `items`. One-time and password-protected links are deliberately not exposed here: a one-time link can only be resolved by viewing it, which consumes it. --- ## Errors Every failure is JSON, `{ "error": "..." }`, with a matching status code. | code | meaning | |------|---------| | 400 | bad request: no file, empty body, or malformed JSON | | 404 | no such id | | 405 | wrong HTTP method for this path | | 410 | the link has expired | | 413 | over the size cap for an image or a request | | 415 | not one of the accepted formats | | 422 | the file is corrupt or is not really an image | | 429 | rate limited; see the `Retry-After` header | | 500 | server error | --- ## Examples ### curl ```bash # a direct link, default 72h lifetime curl -F "file=@photo.png" https://imgdb.io/api/v1/upload # 24h link that burns after the first view curl -F "file=@photo.png" \ "https://imgdb.io/api/v1/upload?ttl=86400&once=1" # behind a password curl -H "X-Upload-Password: hunter2" \ -F "file=@photo.png" https://imgdb.io/api/v1/upload # raw bytes instead of multipart, link never expires curl --data-binary @photo.png -H "Content-Type: image/png" \ "https://imgdb.io/api/v1/upload?ttl=0" # one album out of three files curl -F "file=@a.png" -F "file=@b.jpg" -F "file=@c.gif" \ "https://imgdb.io/api/v1/album?ttl=604800" # what is behind a link curl https://imgdb.io/api/v1/info/Ym5TEo4.png ``` ### JavaScript ```js // browser or Node 18+ - no dependencies async function upload(file, ttl = 86400) { const body = new FormData(); body.append("file", file); // a File or a Blob const res = await fetch( `https://imgdb.io/api/v1/upload?ttl=${ttl}`, { method: "POST", body }, ); const data = await res.json(); if (!res.ok) throw new Error(data.error); return data; // { url, id, kind, type, size, expires } } const { url } = await upload(myFile); console.log(url); // https://imgdb.io/i/Ym5TEo4.png ``` ### Python ```python # pip install requests import requests BASE = "https://imgdb.io/api/v1" def upload(path: str, ttl: int = 86400) -> dict: with open(path, "rb") as f: r = requests.post( f"{BASE}/upload", files={"file": f}, params={"ttl": ttl}, timeout=60, ) if not r.ok: raise RuntimeError(r.json().get("error", r.text)) return r.json() def album(paths: list[str], ttl: int = 604800) -> dict: """Two-step: upload each image, then group the ids.""" ids = [upload(p, ttl)["id"] for p in paths] r = requests.post( f"{BASE}/album", json={"items": ids, "ttl": ttl}, timeout=60, ) r.raise_for_status() return r.json() print(upload("photo.png")["url"]) ``` --- This file is generated from the running server, so its numbers are the ones being enforced right now. The same documentation for humans is at https://imgdb.io/docs.