# img.pro — Full API Reference > Image sharing API. Upload or import images, get a shareable viewer link with inline transforms. Base URL: `https://api.img.pro` Authentication: `Authorization: Bearer ` header. The upload and import endpoints work without auth (anonymous mode). ## Getting Started 1. Sign up at https://img.pro/auth/register 2. Create an API key from the dashboard at https://img.pro/keys 3. Upload images with `POST /v1/upload` or `POST /v1/import` The API speaks in `link` — a canonical viewer URL on img.pro. Treat that link as the unit of sharing: it renders the image, supports inline transforms via query string, and is the URL your users see. --- ## POST /v1/upload — Upload Image Accepts multipart form data. Works with or without authentication. Request: ```bash curl -X POST https://api.img.pro/v1/upload \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "file=@photo.jpg" \ -F "caption=Hero shot" ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | file | binary | Yes | Image file (JPEG, PNG, GIF, WebP, AVIF, HEIC, SVG, BMP, ICO) | | caption | string | No | Free-text caption / description | | ttl | string | No | Seconds (min 300) or duration string ("5m", "2h", "7d", up to 90d). Omit for permanent. | | (any field) | string | No | Stored as attribution metadata (e.g., `author`, `license`, `source_url`) | Response (201): ```json { "id": "abc123", "link": "https://img.pro/abc123", "caption": "Hero shot", "extension": "jpeg", "width": 1920, "height": 1080, "filesize": 245678, "created_at": 1704067200 } ``` Anonymous uploads also include: - `expires_at` — Unix timestamp when the image will be deleted - `upgrade` — Object with `type`, `url`, `message`, `label` guiding to registration File size limits: - Anonymous: 20 MB - Authenticated: 70 MB (10 MB for SVG) Custom metadata: Any form field not in the reserved list above is stored as attribution metadata. Example: `-F "author=Jane Doe" -F "license=cc-by-4.0"`. These appear at the top level of the response. Errors: - `422 validation_error` — Missing file, invalid format, or file too large - `403 quota_exceeded` — Monthly upload or storage quota reached (action: upgrade) - `429 rate_limited` — Too many requests (anonymous only) - `500 upload_failed` — Server error during processing --- ## POST /v1/import — Import from URL Fetches an image from a URL and stores it. Same response shape as upload. Request: ```json POST https://api.img.pro/v1/import Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "url": "https://example.com/photo.jpg", "caption": "Imported from example.com", "external_id": "ext-123" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | url | string | Yes | URL of image to import (must return an image content type) | All other fields are the same as upload. Response shape is identical. Additional errors: - `422 validation_error` — Missing URL, invalid URL, or unsupported format - `502 fetch_failed` — Could not fetch the URL - `504 fetch_failed` — URL fetch timed out (30s limit) - `500 import_failed` — Server error during import --- ## GET /v1/media — List Images Authentication required. Request: ```bash curl https://api.img.pro/v1/media?limit=20 \ -H "Authorization: Bearer YOUR_API_KEY" ``` | Param | Type | Description | |-------|------|-------------| | ids | string | Comma-separated IDs to fetch specific items | | cursor | string | Pagination cursor from previous response | | limit | integer | Results per page (1-100, default 50) | Response (200): ```json { "data": [ { "id": "abc123", "link": "https://img.pro/abc123", "extension": "jpeg", "width": 1920, "height": 1080, "filesize": 245678, "created_at": 1704067200 } ], "next_cursor": "eyJpZCI6MTIzfQ", "has_more": true } ``` --- ## GET /v1/media/:id — Get Image Details Works without authentication for public images. With auth, returns any image owned by the team. Request: ```bash curl https://api.img.pro/v1/media/abc123 \ -H "Authorization: Bearer YOUR_API_KEY" ``` Response (200): Same shape as a single item from the list endpoint, plus attribution metadata fields at the top level. Errors: - `404 not_found` — Image doesn't exist or isn't accessible --- ## PATCH /v1/media/:id — Update Image Authentication required. Only `caption`, `public`, `ttl`, and arbitrary attribution metadata are accepted — sending `tags`, `namespace`, `nsfw`, `tool`, `defaults`, or `description` returns 422. Request: ```json PATCH https://api.img.pro/v1/media/abc123 Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "caption": "Updated caption", "public": true, "author": "Updated Author", "old_field": null } ``` | Field | Type | Description | |-------|------|-------------| | caption | string | Free-text caption. Empty string or null clears it. | | public | boolean | Flip the media's public/private state. | | ttl | string | New TTL (e.g., `7d`) or `null` to make permanent. | | (any field) | string | Merged with existing attribution metadata. `null` removes. | Response (200): Updated media object. --- ## DELETE /v1/media/:id — Delete Image Authentication required. Soft delete — image is marked deleted but can't be recovered via API. Request: ```bash curl -X DELETE https://api.img.pro/v1/media/abc123 \ -H "Authorization: Bearer YOUR_API_KEY" ``` Response: `204 No Content` (no body). --- ## PATCH /v1/media/batch — Batch Update Authentication required. Update `caption`, `public`, or `ttl` on up to 100 images at once. Same field restrictions as the single-item PATCH. Request: ```json PATCH https://api.img.pro/v1/media/batch Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "ids": ["abc123", "def456", "ghi789"], "ttl": "7d" } ``` Response (200): ```json { "updated": 3, "media": [ ...updated media objects... ] } ``` --- ## DELETE /v1/media/batch — Batch Delete Authentication required. Request: ```json DELETE https://api.img.pro/v1/media/batch Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "ids": ["abc123", "def456"] } ``` Response (200): ```json { "deleted": 2 } ``` --- ## GET /v1/usage — Check Quota Authentication required. Request: ```bash curl https://api.img.pro/v1/usage \ -H "Authorization: Bearer YOUR_API_KEY" ``` Response (200): ```json { "monthly": { "uploads": 42, "uploads_limit": 100, "uploads_remaining": 58, "resets_at": 1706745600 }, "totals": { "media_stored": 142, "storage_used_bytes": 1073741824, "storage_limit_bytes": 5368709120, "storage_remaining_bytes": 4294967296 }, "plan": "free" } ``` --- ## Image Transforms Transforms live as query parameters on the `link` field. Append `?format=webp` to convert formats, `?brightness=1.1` to adjust tone, etc.: | Param | Values | Description | |-------|--------|-------------| | format | jpg, webp, avif, png, gif | Convert output format | | brightness | 0 – 10 (decimal) | Multiplier centered at 1. 0 = pure black, 1 = unchanged, 2 = twice as bright. Useful range ~0.5–2 | | contrast | 0 – 10 (decimal) | Multiplier centered at 1. 0 = flat mid-gray, >1 = punchier. Useful range ~0.7–2 | | gamma | 0 – 10 (decimal) | Multiplier. 0 and 1 = unchanged, <1 lightens midtones, >1 darkens midtones (direction is opposite of some editors). Useful range ~0.5–2.5 | | saturation | 0 – 10 (decimal) | Multiplier centered at 1. 0 = grayscale, >1 = vivid. Useful range ~0.5–2 | | blur | 0 – 250 (integer) | Gaussian blur radius in pixels. 0 = no blur | | sharp | 0 – 10 (decimal) | Unsharp-mask intensity. 0 = no sharpen | | rotate | 90, 180, 270 | Clockwise rotation in degrees. Other values silently ignored | | flip | h, v, hv | Mirror horizontally, vertically, or both | | segment | foreground | Remove background (output PNG to keep alpha) | | background | CSS named color, #hex (URL-encode # as %23), rgb(...), rgba(...). Default #FFFFFF | Fill background; pair with `segment=foreground` to swap | | metadata | `copyright` (default), `none`, `keep` | EXIF retention. Default `copyright` strips GPS/device but keeps copyright tags. `none` strips all. `keep` retains all (including GPS — use deliberately) | Editorial caps note: brightness/contrast/saturation/gamma have no upper bound in Cloudflare's API; our 0–10 cap keeps cache cardinality bounded and stops degenerate output. Reach out if you need a value outside the cap. For resize / crop / face crop (anything that needs explicit dimensions) use the dashboard tools at https://img.pro/tools — they output links with dimensions baked in. All transforms are cached globally at the edge. See https://img.pro/api/transforms for the full parameter reference. --- ## Plans & Pricing | Plan | Uploads/mo | Storage | Retention | Max file | Price | |------|-----------|---------|-----------|----------|-------| | Anonymous | Rate-limited per IP | Shared | 30 days | 20 MB | Free | | Free | 100/mo | 5 GB | Permanent | 70 MB | Free | | Pro | 1,000/mo | 50 GB | Permanent | 70 MB | $19/mo | | Scale | 10,000/mo | 500 GB | Permanent | 70 MB | $49/mo | | Max | 100,000/mo | 5 TB | Permanent | 70 MB | $199/mo | Quotas reset on the first of each month. Upgrade links are signed one-click URLs (no login required) — included in quota error responses. --- ## Error Format Every error response follows this shape: ```json { "error": "error_code", "message": "Human-readable description", "action": { "type": "upgrade | wait", "url": "https://...", "label": "Button text", "seconds": 3600 } } ``` | Action type | When | What to do | |------------|------|------------| | upgrade | Quota reached | Show `action.url` to user — signed upgrade link | | wait | Rate limited | Retry after `action.seconds`. Also check `Retry-After` header | Common error codes: - `validation_error` (422) — Invalid input, check `errors` object for field-level details - `unauthorized` (401) — Missing or invalid API key - `forbidden` (403) — Key doesn't have required ability (read/write) - `not_found` (404) — Resource doesn't exist or isn't accessible - `external_id_duplicate` (409) — `external_id` already exists - `quota_exceeded` (403) — Upload or storage quota reached - `rate_limited` (429) — Too many requests - `upload_failed` (500) — File processing failed (invalid format, too large, or internal error) - `fetch_failed` (502/504) — URL import failed or timed out --- ## Quota Headers Authenticated upload/import responses include these headers: ``` X-Monthly-Uploads-Used: 42 X-Monthly-Uploads-Limit: 100 X-Monthly-Uploads-Remaining: 58 X-Storage-Used: 1073741824 X-Storage-Limit: 5368709120 X-Storage-Remaining: 4294967296 ``` Anonymous responses include rate limit headers (concrete numbers vary — rely on `Retry-After` on 429s rather than caching the cap): ``` X-RateLimit-Limit: X-RateLimit-Remaining: X-RateLimit-Reset: X-RateLimit-Limit-Daily: X-RateLimit-Remaining-Daily: ``` --- ## Links - Interactive docs: https://img.pro/api - Quick start guide: https://img.pro/api/quick-start - API reference: https://img.pro/api/reference - Error reference: https://img.pro/api/errors - AI agents guide: https://img.pro/api/ai-agents - OpenAPI spec: https://img.pro/openapi.yaml - Pricing: https://img.pro/pricing