API Reference
Complete reference for the img.pro REST API. All requests go to https://api.img.pro/v1.
Authentication
Include your API key as a Bearer token:
Authorization: Bearer YOUR_API_KEY
Sign up at img.pro, then create a key from the dashboard.
The upload and import endpoints work without authentication. Anonymous uploads expire after 30 days and are rate limited by IP (20/hour, 100/day). Include a Bearer token for ownership, permanent storage, and higher limits.
Keep your API key secret
POST /v1/upload
Upload an image file. Authentication optional. The filename is taken from the multipart file part — clients can’t override it.
| Parameter | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | Image file. Supported: JPEG, PNG, GIF, WebP, AVIF, HEIC, SVG, BMP, ICO. Max 70 MB (10 MB for SVG, 20 MB anonymous). |
| caption | string | No | Free-text caption / description for the image. |
| ttl | string | No | Time-to-live: seconds (e.g., 3600) or duration (5m–90d). Omit for permanent storage. |
| any field | string | No | Any other field is stored as attribution metadata (e.g., author, license, source_url) |
Authenticated
curl -X POST "https://api.img.pro/v1/upload" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@photo.jpg" \
-F "caption=Hero shot from launch day"
{
"id": "abc123",
"link": "https://img.pro/abc123",
"caption": "Hero shot from launch day",
"extension": "jpeg",
"width": 1920,
"height": 1080,
"filesize": 245678,
"created_at": 1704067200
}
Anonymous (no auth)
curl -X POST "https://api.img.pro/v1/upload" \
-F "file=@photo.jpg"
Same response shape, plus expires_at and an upgrade hint pointing to registration. Anonymous uploads are rate-limited — sign up for an API key to remove the cap.
{
"id": "abc123",
"link": "https://img.pro/abc123",
"extension": "jpeg",
"width": 1920,
"height": 1080,
"filesize": 245678,
"expires_at": 1706659200,
"created_at": 1704067200,
"upgrade": {
"type": "register",
"url": "https://img.pro/auth/register?from_cta=api",
"message": "Sign up for permanent storage, larger files (70 MB), and higher limits",
"label": "Create Account"
}
}
Response field notes
id,link,extension, andcreated_at— always present.caption— sits underlinkwhen set; omitted otherwise.status— only present while"processing"or"failed".non_editable: true— only present for SVG/BMP/ICO (formats Cloudflare can't transform); everything else is editable by default.nsfw— only present when flagged by moderation.expires_at— only present for ephemeral uploads.
POST /v1/import
Import an image from a URL. Authentication optional. The filename is derived from the URL path — clients can’t override it.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Image URL to import (must be publicly accessible, 30s timeout). |
| caption | string | No | Free-text caption / description. |
| ttl | string | No | Time-to-live (e.g., 7d). Omit for permanent storage. |
| external_id | string | No | External dedup key — unique per team. If a non-expired record with this ID already exists, it is returned (HTTP 200, X-Deduplicated: true) instead of re-fetching the URL. |
| any field | string | No | Stored as attribution metadata. |
curl -X POST "https://api.img.pro/v1/import" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/image.jpg",
"caption": "Imported from example.com",
"external_id": "ext-12345"
}'
{
"id": "def456",
"link": "https://img.pro/def456",
"caption": "Imported from example.com",
"extension": "jpeg",
"width": 2560,
"height": 1440,
"filesize": 384291,
"external_id": "ext-12345",
"source_url": "https://example.com/image.jpg",
"created_at": 1704067200
}
source_url is set automatically to the imported URL.
GET /v1/media
List media with cursor-based pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ids | string | No | Comma-separated IDs to fetch specific items |
| limit | integer | No | 1–100 (default: 50) |
| cursor | string | No | Pagination cursor from previous response |
curl "https://api.img.pro/v1/media?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "abc123",
"link": "https://img.pro/abc123",
"caption": "Hero shot from launch day",
"extension": "jpeg",
"width": 1920,
"height": 1080,
"filesize": 245678,
"created_at": 1704067200
}
],
"next_cursor": "eyJpZCI6MTIzfQ",
"has_more": true
}
GET /v1/media/:id
Get a single media item. Public media can be fetched without authentication.
curl "https://api.img.pro/v1/media/abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"id": "abc123",
"link": "https://img.pro/abc123",
"caption": "Hero shot from launch day",
"extension": "jpeg",
"width": 1920,
"height": 1080,
"filesize": 245678,
"created_at": 1704067200
}
PATCH /v1/media/:id
Update editable fields. Only the params below (plus arbitrary attribution metadata) are accepted — sending tags, namespace, nsfw, tool, defaults, or description returns 422.
| Parameter | Type | Required | Description |
|---|---|---|---|
| caption | string | No | Update caption. Send empty string or null to clear. |
| public | boolean | No | Flip the media’s public/private state. true / 1 / "true" = public; false / 0 / "false" = private. |
| ttl | string | No | New TTL (e.g., 7d) or null to make permanent. |
| any field | string|null | No | Merged with existing attribution metadata. null removes the field. |
curl -X PATCH "https://api.img.pro/v1/media/abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"caption": "Updated caption", "public": true, "camera": null}'
{
"id": "abc123",
"link": "https://img.pro/abc123",
"caption": "Updated caption",
"extension": "jpeg",
"width": 1920,
"height": 1080,
"filesize": 245678,
"created_at": 1704067200
}
DELETE /v1/media/:id
Permanently delete a media item and all its CDN variants. Returns 204 No Content.
curl -X DELETE "https://api.img.pro/v1/media/abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
Batch Operations
PATCH /v1/media/batch
Update editable fields on up to 100 items at once. Same field restrictions as the single-item PATCH — caption, public, and ttl apply uniformly to every id in the batch.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Array of media IDs (max 100). |
| caption | string | No | Same caption applied to every item (empty string or null clears). |
| public | boolean | No | Flip every item’s public/private state in one call. |
| ttl | string | No | Same TTL applied to every item (e.g. 7d) or null to make all permanent. |
| any field | string|null | No | Merged into every item’s attribution metadata. null removes the field. |
curl -X PATCH "https://api.img.pro/v1/media/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids": ["abc123", "def456"], "ttl": "7d", "public": false}'
{
"updated": 2
}
DELETE /v1/media/batch
Delete up to 100 items by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ids | string[] | Yes | Array of media IDs to delete (max 100). |
curl -X DELETE "https://api.img.pro/v1/media/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids": ["abc123", "def456"]}'
{
"deleted": 2
}
GET /v1/usage
Current quota and usage statistics.
curl "https://api.img.pro/v1/usage" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"monthly": {
"uploads": 42,
"uploads_limit": 5000,
"uploads_remaining": 4958,
"resets_at": 1709251200
},
"totals": {
"media_stored": 142,
"storage_used_bytes": 52428800,
"storage_limit_bytes": 5368709120,
"storage_remaining_bytes": 5316280320
},
"plan": "free"
}
Image Transforms
Transforms live as query parameters on the link field. Convert formats, adjust color/tone, blur or sharpen, rotate or flip, remove backgrounds with one param, and strip EXIF before sharing — all inline via the URL.
https://img.pro/{id}?format=webp&brightness=1.1&metadata=none
format, brightness, contrast, gamma, saturation, blur, sharp, rotate, flip, segment, background, metadata. See the Image Transforms reference for value ranges, defaults, and combining examples. Anything that needs explicit dimensions (resize, crop, face crop) lives in the image tools.
Custom Fields
Any field you send that isn't a reserved name is stored as attribution metadata.
Set fields
Include them alongside other parameters — no wrapper needed:
curl -X POST "https://api.img.pro/v1/upload" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@photo.jpg" \
-F "caption=Sunset over the Pacific" \
-F "author=Jane Doe" \
-F "license=cc-by-4.0"
Read fields
Custom fields appear at the top level of the response — what you send is what you get back:
{
"id": "abc123",
"link": "https://img.pro/abc123",
"caption": "Sunset over the Pacific",
"extension": "jpeg",
"author": "Jane Doe",
"license": "cc-by-4.0",
"created_at": 1704067200
}
Update fields
PATCH merges with existing attribution metadata. Set a field to null to remove it:
curl -X PATCH "https://api.img.pro/v1/media/abc123" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"alt_text": "A vivid sunset", "camera": null}'
Reserved fields
These names are used by the system. Sending them in upload metadata is silently ignored; sending them on PATCH returns 422 (with the exception of caption, public, ttl, and external_id, which are valid PATCH targets):
id name link extension width height filesize status tags tag_mode namespace external_id date caption description editable non_editable nsfw expires_at created_at tool defaults public ttl file
Errors
Every error includes a machine-readable error code and a human-readable message. Some include an action object — see the Error Reference for full details.
unauthorizedMissing or invalid API keyforbiddenKey lacks required permissionquota_exceededUpload or storage limit reachednot_foundMedia item not foundexternal_id_duplicateAnother item uses this external_idvalidation_errorInvalid parameters (per-field errors)rate_limitedToo many requestsupload_failedServer error during uploadimport_failedServer error during importfetch_failedCould not fetch import URLQuota Headers
Every authenticated response includes quota information in headers:
X-Monthly-Uploads-UsedUploads used this billing periodX-Monthly-Uploads-LimitMonthly upload limitX-Monthly-Uploads-RemainingUploads remainingX-Storage-UsedStorage used (bytes)X-Storage-LimitStorage limit (bytes)X-Storage-RemainingStorage remaining (bytes)