Developer reference
REST API for image hosting — integrate from scripts, ShareX, desktop tools, backends, or your own site.
The stable surface for third parties is the Bearer API under /api/v1/ after you create a key in API. Use it as a ShareX custom uploader, presigned upload pipeline, or backend integration. See the ShareX setup guide for a copy-paste custom uploader. OpenAPI (JSON) summarizes the v1 surface; the reference below is the full guide.
Need limits first? Call GET /api/v1/upload without auth.
Uploads are back to normal. Sorry for the brief outage.
This reference is for webmasters and developers who integrate with imgtree: automation, mobile or desktop clients, forum tools (for example ShareX), or server-side scripts.
Base URL: use your site’s origin (for example https://img.example.com). All paths below are rooted at that origin.
Live limits: send an unauthenticated GET /api/v1/upload to receive JSON with the current max file size, allowed image types, rate-limit notes, and a short index of upload-related routes for this deployment.
Browser note: imgtree does not send broad Access-Control-* headers on the API. Call the API from your server or backend, or place your own proxy or edge rules if you need direct browser fetch from third-party pages.
Authorization: Bearer <your_api_key>
The sections below describe the version 1 HTTP API (/api/v1/...), which uses this header. Keys are tied to your account; uploads count against your storage quota.
| Flow | Best for | Notes |
|---|---|---|
Multipart POST /api/v1/upload | ShareX, form posts, small/medium files | Simplest; subject to host body-size limits |
JSON + base64 POST /api/v1/upload | Server scripts, one image per request | Base64 inflates payload size |
| Presign → PUT → complete | Large originals | Bypasses app-server body limit for the main file |
For automation, start with multipart. Switch to presign when files exceed your host’s request body cap or when uploading from a client that already speaks S3-style PUT URLs.
Implementation note: Legacy multipart handlers (POST /api/upload, POST /api/upload/guest, and multipart POST /api/v1/upload) are thin route adapters. They read the request body and delegate to lib/store-image.ts (storeImageFromBuffer), which applies shared type/size policy (lib/upload/image-policy.ts), Sharp thumbnail generation, R2 storage, and DB insert. Response JSON shapes are unchanged. For large originals, prefer presign → PUT → complete so the main file bytes bypass the app server’s HTTP body limit.
GET /api/v1/upload for the exact wording on this deployment).GET /api/v1/images accepts limit up to 100.When limited, the API responds with 429 Too Many Requests. Retry with backoff.
Returns machine-readable capabilities for this instance: max upload size, allowed MIME types, presigned URL lifetime, batch limits, and brief endpoint descriptions. No Authorization header.
Use this in installers or scripts so you do not hard-code limits.
Upload one or more images using multipart (typical for ShareX and form posts) or JSON with base64 for a single image (convenient from servers).
Content-Type: multipart/form-data
Authorization: Bearer <your_api_key>
file: <binary> # repeat the field name "file" for multiple files
albumId: <uuid> # optional — must belong to your account
Content-Type: application/json
Authorization: Bearer <your_api_key>
{
"filename": "screenshot.png",
"contentType": "image/png",
"file_base64": "<base64 or data:image/png;base64,...>",
"albumId": "<optional uuid>"
}
The field base64 is accepted as an alias for file_base64.
Sizing: Base64 increases payload size. For large files, prefer multipart or the presign → PUT → complete flow below.
Unsupported Content-Type: the server responds with 415.
Image types: image/jpeg, image/png, image/gif, image/webp
Max size: Defined by this deployment (see GET /api/v1/upload). Multipart uploads may also be limited by your host’s maximum request body size.
Large files: Use POST /api/v1/upload/presign, PUT the bytes to put_url, then POST /api/v1/upload/complete with completion_token. That path bypasses your app server’s body limit for the main file.
Rate limiting: By default, about 30 requests per minute per client IP (see the discovery response for wording used on this instance).
Response 200
{
"success": true,
"images": [
{
"id": "abc12xyz",
"url": "https://{origin}/i/abc12xyz",
"direct_url": "https://{origin}/direct/{slug}.{ext}",
"thumb_url": "https://{origin}/thumbnail/{slug}.jpg",
"delete_url": "https://{origin}/api/images/<uuid>?token=<delete_token>",
"codes": {
"bbcode": "[img]https://…[/img]",
"bbcode_thumb": "[url=https://…][img]https://…/thumb[/img][/url]",
"html": "<img src=\"https://…\" alt=\"file.jpg\" />",
"markdown": "",
"direct": "https://…"
}
}
]
}
id is the image slug (public page /i/{slug}). delete_url uses the internal image UUID.
Common errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Account inactive |
429 | Too many requests |
400 | Bad request (for example no files) |
500 | Server error |
Per-file failures in multipart batches appear as objects with an error string instead of URLs.
Returns a time-limited URL to upload the original file bytes with PUT, using the same Content-Type you declare.
Request — Content-Type: application/json
{
"filename": "photo.jpg",
"contentType": "image/jpeg",
"sizeBytes": 1234567,
"albumId": "<optional uuid>"
}
Response 200
{
"put_url": "https://…",
"completion_token": "<opaque>"
}
Then: PUT to put_url with the raw bytes and header Content-Type matching contentType.
Up to 20 presigns in one call (same auth and album rules as a single presign).
Request — Content-Type: application/json
{
"albumId": "<optional uuid>",
"files": [
{ "filename": "a.jpg", "contentType": "image/jpeg", "sizeBytes": 1000 },
{ "filename": "b.png", "contentType": "image/png", "sizeBytes": 2000 }
]
}
Response 200
{
"success": true,
"uploads": [
{ "put_url": "https://…", "completion_token": "…" },
{ "error": "Filename required", "status": 400 }
]
}
Order matches files. After each successful PUT, call complete or complete/batch with the matching tokens.
Request — Content-Type: application/json
{ "completion_token": "<from presign>" }
token is accepted as an alias.
Response 200 — Same shape as POST /api/v1/upload (success, images[]).
Finalize up to 20 direct uploads after PUT to each put_url.
Request — Content-Type: application/json
{
"completion_tokens": ["<from presign>", "<from presign>"]
}
tokens is accepted as an alias for completion_tokens.
Response 200
{
"success": true,
"images": [
{ "id": "<slug>", "url": "…", "direct_url": "…", "thumb_url": "…", "delete_url": "…", "codes": { } },
{ "error": "Invalid or expired upload token" }
]
}
Order matches the input tokens. Your API key’s upload counter increases by the number of successful finalizations.
Lists images owned by the API key (not deleted), with pagination.
Query: page (default 1), limit (default 24, max 100).
Response 200
{
"success": true,
"images": [
{
"id": "<uuid>",
"slug": "…",
"url": "https://{origin}/i/{slug}",
"direct_url": "…",
"thumb_url": "…",
"original_name": "…",
"created_at": "…"
}
],
"page": 1,
"limit": 24,
"total": 42,
"has_more": true
}
Soft-deletes an image by UUID (imageId), using the API key. Behaviour matches an authenticated delete in the web app, without cookies.
Replace the URL with your site and paste your API key:
{
"Name": "imgtree",
"DestinationType": "ImageUploader",
"RequestMethod": "POST",
"RequestURL": "https://{your-site}/api/v1/upload",
"Headers": {
"Authorization": "Bearer <your_api_key>"
},
"Body": "MultipartFormData",
"FileFormName": "file",
"URL": "$json:images[0].url$",
"ThumbnailURL": "$json:images[0].thumb_url$",
"DeletionURL": "$json:images[0].delete_url$"
}
Multipart vs presigned upload: ShareX’s single-request custom uploader works well with multipart POST /api/v1/upload. If your hosting enforces a small HTTP body limit (often a few megabytes), very large files may fail before they reach the app. For larger files, use presign → PUT → complete from a script or another client, or keep multipart uploads under the limit.
These routes power the imgtree web app. They expect a logged-in browser session (session cookie), not the Bearer API key. Use them only if you are building tooling that runs as the signed-in user in a trusted environment.
| Method | Path | Description |
|---|---|---|
GET | /api/images | Paginated list (page, limit ≤ 100) |
PATCH | /api/images/[imageId] | Update allowed fields (owner only) |
DELETE |
GET /api/images returns { images, page, limit, total, has_more }.
| Method | Path | Description |
|---|---|---|
GET | /api/albums | List albums |
POST | /api/albums | Create album |
GET | /api/albums/[albumId] |
| Method | Path | Description |
|---|---|---|
GET | /api/keys | List keys (metadata only) |
POST | /api/keys | Create key (secret returned once) |
DELETE | /api/keys/[keyId] |
| Method | Path | Description |
|---|---|---|
POST | /api/upload | Signed-in multipart (files, optional albumId, isPublic) |
POST | /api/upload/presign |
Multipart rows above delegate to storeImageFromBuffer (see Upload flows compared). Presign + complete routes use runPresignUpload and the direct-upload completion module instead.
Reserved for accounts with the admin role.
| Method | Path | Description |
|---|---|---|
GET | /api/admin/stats | Aggregated statistics |
GET | /api/admin/users | User list / search |
PATCH | /api/admin/users/[userId] |
/api/images/[imageId] |
Soft delete; owner session or ?token=<delete_token> from a delete_url (rate limited) |
| Album detail |
PATCH | /api/albums/[albumId] | Update album |
DELETE | /api/albums/[albumId] | Delete album |
| Revoke key |
| Presign for signed-in user |
POST | /api/upload/complete | Complete after PUT (session) |
POST | /api/upload/guest | Public multipart upload |
POST | /api/upload/guest/presign | Guest presign |
POST | /api/upload/guest/complete | Guest complete after PUT |
| Update user (role, active state, quota, etc.) |