Documentation
API Reference
Look up each supported public endpoint with auth, payload, status, and response details.
Last updated: July 17, 2026
This is the customer integration reference for the public ImageGrid API. It covers plan discovery, account context, source uploads, async jobs, generated tiles, artifact metadata, and signed artifact downloads.
Account signup, email verification, plan activation, billing, and API key issuance happen in the web UI. Use this reference for the JSON API your application calls.
- Base API host:
https://api.imagegrid.dev - Public docs host:
https://imagegrid.dev - Machine-readable spec: OpenAPI JSON
How to use this reference
Use this page when you need the endpoint-by-endpoint contract. For task-oriented walkthroughs, start with Getting Started, Uploads and Jobs, or Artifacts and Downloads.
Each endpoint block follows the same order:
| Field | Meaning |
|---|---|
| Auth | Whether the request needs an API key, signed URL, or no credentials. |
| Request | Path parameters, query parameters, body format, and content type. |
| Success | Expected success status and response shape. |
| Common failures | Statuses a client should handle intentionally. |
Authentication
Use API keys for authenticated API routes. Send the key in either header:
Authorization: Bearer igk_...
X-API-Key: igk_...
Signed artifact download URLs do not use API-key auth. Use the download_url exactly as returned by GET /v1/artifacts/{id} before it expires.
Request lifecycle
Most image workflows follow this sequence:
- Create an API key in the web UI.
- Upload a source image with
POST /v1/assets. - Create a processing job with
POST /v1/jobs. - Poll
GET /v1/jobs/{id}until the job reaches a terminal state. - Read artifact metadata with
GET /v1/artifacts/{id}. - Download bytes with the returned signed
download_url.
Tile workflows add one more option: jobs that create a tileset can serve individual XYZ tiles through GET /v1/tilesets/{id}/xyz/{z}/{x}/{y} with API-key auth.
Endpoint index
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/v1/plans |
None | List public plans and plan limits. |
GET |
/v1/account |
API key | Read account identity and current key permissions. |
POST |
/v1/assets |
API key | Upload a source image asset. |
POST |
/v1/jobs |
API key | Create an async image-processing job. |
GET |
/v1/jobs/{id} |
API key | Poll job state and generated artifacts. |
GET |
/v1/tilesets/{id}/xyz/{z}/{x}/{y} |
API key | Download one generated XYZ tile. |
GET |
/v1/artifacts/{id} |
API key | Read artifact metadata and a short-lived download URL. |
GET |
/v1/artifacts/{id}/download |
Signed URL | Download artifact bytes. |
GET /v1/plans
Lists the public commercial plans currently available for self-serve customers.
| Property | Value |
|---|---|
| Auth | None |
| Request body | None |
| Rate limit | 120 requests/minute/IP |
| Success | 200 OK |
| Common failures | 429, 500 |
Example:
curl https://api.imagegrid.dev/v1/plans
Response:
{
"plans": [
{
"code": "starter",
"display_name": "Starter",
"description": "For smaller sites, storefronts, creators, and lighter production workloads.",
"status": "active",
"is_public": true,
"billing_amount_minor": 1000,
"billing_interval": "monthly",
"included_processing_credits": 2000,
"limits": {
"max_upload_bytes": 20971520,
"max_input_edge_pixels": 6000,
"max_input_megapixels": 24,
"max_output_edge_pixels": 6000,
"max_output_megapixels": 24,
"max_generated_tiles_per_job": 25000,
"max_artifact_bytes_per_job": 1073741824,
"retention_ttl_seconds": 86400,
"max_grid_render_megapixels": 4,
"max_tile_patch_megapixels_per_job": 1,
"max_tile_patch_patches_per_job": 1,
"max_in_flight_jobs": 10
}
}
]
}
included_processing_credits is the customer-facing allowance for the billing period. limits carries hard caps that still apply while credit remains. Limit keys with no configured value are omitted. The same credit pool applies across resize, optimize, tile, grid-render, and tile-patch traffic.
GET /v1/account
Returns account context for the authenticated API key.
| Property | Value |
|---|---|
| Auth | API key |
| Request body | None |
| Rate limit | 240 requests/minute/account |
| Success | 200 OK |
| Common failures | 401, 429 |
Example:
curl \
-H "Authorization: Bearer igk_..." \
https://api.imagegrid.dev/v1/account
Response:
{
"account": {
"id": "00000000-0000-0000-0000-000000000000",
"slug": "acme",
"display_name": "Acme"
},
"subject_type": "api_key",
"api_key": {
"id": "00000000-0000-0000-0000-000000000000",
"name": "production-runtime",
"role_code": "api_runtime"
},
"permissions": [
"account.read"
],
"plan": {
"code": "growth",
"display_name": "Growth",
"status": "active",
"access_source": "stripe"
},
"quotas": {
"processing_credits_monthly": {
"limit": 10000,
"interval_seconds": 2592000
},
"max_grid_render_megapixels": {
"limit": 100
}
},
"effective_limits": {
"grid_megapixels": 100,
"tile_estimate": 100000,
"storage_estimate_bytes": 4294967296,
"processing_credits": 10000
},
"usage": {
"processing_credits": {
"used": 2500,
"remaining": 7500,
"limit": 10000
},
"cdn_delivery_bytes": {
"used": 0
}
},
"features": {
"cdn_delivery": true
},
"entitlements": {
"cdn_delivery": {
"enabled": true,
"feature_code": "cdn_delivery",
"status": "active",
"source": "stripe",
"provider": "bunny",
"add_on_code": "cdn_delivery",
"cancel_at_period_end": false
}
}
}
Use this endpoint as a lightweight credentials check when wiring a new integration. Integrations that need ImageGrid-managed CDN delivery should check features.cdn_delivery or entitlements.cdn_delivery.enabled before enabling CDN-backed behavior.
POST /v1/assets
Uploads a source image and returns stored asset metadata.
| Property | Value |
|---|---|
| Auth | API key |
| Content type | multipart/form-data |
| Required body | file part |
| Rate limit | 30 requests/minute/account |
| Success | 201 Created |
| Common failures | 400, 401, 403, 415, 429, 503 |
Supported source formats are still-image jpeg, png, webp, gif, avif, heif, and jxl when the current runtime can decode them. GIF output is flattened to a single still frame.
Plan upload caps are enforced before processing. Current self-serve upload caps are 20 MiB on Starter and 50 MiB on Growth; larger workloads can use the contact path for review.
Example:
curl -X POST \
-H "Authorization: Bearer igk_..." \
-F "file=@/path/to/source.png" \
https://api.imagegrid.dev/v1/assets
Response:
{
"id": "00000000-0000-0000-0000-000000000000",
"account_id": "00000000-0000-0000-0000-000000000000",
"source_type": "upload",
"status": "ready",
"original_filename": "source.png",
"content_type": "image/png",
"checksum_sha256": "abc123...",
"byte_size": 182736,
"width": 1600,
"height": 900,
"storage_key": "assets/.../original.png",
"created_at": "2026-04-15T12:00:00Z"
}
POST /v1/jobs
Creates an async image-processing job.
| Property | Value |
|---|---|
| Auth | API key |
| Content type | application/json |
| Operations | resize, optimize, tile, grid_render, tile_patch |
| Rate limit | 60 requests/minute/account |
| Success | 201 Created |
| Common failures | 400, 401, 404, 429 |
Accepted jobs may wait longer in the queue when the service is busy. ImageGrid does not return a separate queue-busy error for ordinary backlog pressure. A capacity-style 429 with code: "worker_capacity_exceeded" is reserved for jobs that exceed current processing limits.
Resize request:
{
"asset_id": "00000000-0000-0000-0000-000000000000",
"operation": "resize",
"options": {
"width": 320,
"fit": "inside",
"format": "webp"
}
}
Grid render request:
{
"operation": "grid_render",
"options": {
"width": 640,
"height": 320,
"block_width": 32,
"block_height": 32,
"format": "png"
}
}
Success response:
{
"id": "00000000-0000-0000-0000-000000000000",
"asset_id": "00000000-0000-0000-0000-000000000000",
"operation": "resize",
"status": "queued",
"options": {
"width": 320,
"fit": "inside",
"format": "webp",
"quality": 80
},
"result": {},
"queued_at": "2026-04-15T12:00:00Z",
"updated_at": "2026-04-15T12:00:00Z",
"attempt_count": 0,
"max_attempts": 3,
"artifacts": []
}
See Operations for the full options schema for each operation.
GET /v1/jobs/{id}
Reads the current state of a job and any generated artifacts.
| Property | Value |
|---|---|
| Auth | API key |
| Path parameters | id UUID |
| Request body | None |
| Rate limit | 240 requests/minute/account |
| Success | 200 OK |
| Common failures | 400, 401, 404, 429 |
When polling is throttled, respect the Retry-After header and retry with exponential backoff plus jitter instead of hot-loop polling.
Response:
{
"id": "00000000-0000-0000-0000-000000000000",
"asset_id": "00000000-0000-0000-0000-000000000000",
"operation": "resize",
"status": "succeeded",
"options": {
"width": 320,
"fit": "inside",
"format": "webp",
"quality": 80
},
"result": {},
"queued_at": "2026-04-15T12:00:00Z",
"started_at": "2026-04-15T12:00:02Z",
"completed_at": "2026-04-15T12:00:03Z",
"updated_at": "2026-04-15T12:00:03Z",
"attempt_count": 1,
"max_attempts": 3,
"artifacts": [
{
"id": "00000000-0000-0000-0000-000000000000",
"kind": "primary",
"content_type": "image/webp",
"byte_size": 9201,
"width": 320,
"height": 180,
"checksum_sha256": "abc123...",
"metadata": {},
"created_at": "2026-04-15T12:00:03Z"
}
]
}
Failed jobs keep the same shape and can add error_code and error_message. See Errors for retry guidance.
For completed tile, grid_render, and tile_patch jobs, accounts with the cdn_delivery entitlement receive direct browser delivery fields in result:
{
"tileset_id": "00000000-0000-0000-0000-000000000000",
"version": 3,
"format": "png",
"public_tile_url_template": "https://cdn.imagegrid.dev/cdn/tilesets/00000000-0000-0000-0000-000000000000/v/3/xyz/{z}/{x}/{y}.png?expires=1784275200&sig=abcd...",
"tile_url_template": "https://cdn.imagegrid.dev/cdn/tilesets/00000000-0000-0000-0000-000000000000/v/3/xyz/{z}/{x}/{y}.png?expires=1784275200&sig=abcd..."
}
The template contains {z}, {x}, and {y} placeholders for XYZ clients. It resolves to public tile bytes with CORS enabled and versioned public cache headers. The /v/{version}/ path and signature change when a tileset version changes, including tile_patch rerenders and rotation/preprocess rebuilds. The signed expiry prevents edge caches from outliving the delivery window. Deleted or expired tilesets stop resolving at the origin.
Browser tile clients should prefer public_tile_url_template or tile_url_template when present and substitute {z}, {x}, and {y} directly. Accounts without cdn_delivery do not receive these fields and should keep using an authenticated or local proxy fallback.
GET /v1/tilesets/{id}/xyz/{z}/{x}/{y}
Downloads one generated XYZ tile from a tileset owned by the authenticated account.
| Property | Value |
|---|---|
| Auth | API key |
| Path parameters | id UUID, z zoom level, x tile column, y tile row including extension such as 0.png |
| Request body | None |
| Success | 200 OK with tile bytes |
| Common failures | 400, 401, 404 |
This endpoint is useful when a tile, grid_render, or tile_patch workflow produces a reusable tileset and the client wants to request individual map-style tiles through an authenticated application server or proxy. It remains the fallback when public CDN delivery fields are not present.
Example:
GET /v1/tilesets/{id}/xyz/4/0/0.png
Authorization: Bearer igk_...
Successful responses return tile bytes with the generated tile content type, such as image/png, image/jpeg, or image/webp.
GET /v1/artifacts/{id}
Loads artifact metadata plus a short-lived signed download URL.
| Property | Value |
|---|---|
| Auth | API key |
| Path parameters | id UUID |
| Request body | None |
| Rate limit | 240 requests/minute/account |
| Success | 200 OK |
| Common failures | 400, 401, 404, 429 |
Response:
{
"id": "00000000-0000-0000-0000-000000000000",
"kind": "primary",
"content_type": "image/webp",
"byte_size": 9201,
"width": 320,
"height": 180,
"checksum_sha256": "abc123...",
"metadata": {},
"created_at": "2026-04-15T12:00:03Z",
"download_url": "https://api.imagegrid.dev/v1/artifacts/00000000-0000-0000-0000-000000000000/download?expires=1760000000&sig=abcd...",
"download_expires_at": "2026-04-15T12:15:03Z"
}
Fetch this endpoint again if a previously issued signed URL expires.
GET /v1/artifacts/{id}/download
Downloads artifact bytes directly. This endpoint uses signed expires and sig query parameters rather than API-key auth.
| Property | Value |
|---|---|
| Auth | Signed URL |
| Path parameters | id UUID |
| Query parameters | expires Unix timestamp, sig HMAC signature |
| Request body | None |
| Rate limit | 600 requests/minute/IP |
| Success | 200 OK with artifact bytes |
| Common failures | 400, 403, 404, 429 |
Example:
GET /v1/artifacts/{id}/download?expires=<unix>&sig=<hex>
Use the full download_url returned by GET /v1/artifacts/{id}. Do not construct signatures client-side.
Status and error model
Successful write requests use 201 Created; successful reads use 200 OK. Error responses are JSON and always include at least an error string:
{
"error": "invalid api key"
}
Some policy failures include a stable code, such as worker_capacity_exceeded, when the client can make a more specific decision. See Errors for validation failures, signed-download failures, retry behavior, and async job failure fields.
Source of truth
The machine-readable contract lives at OpenAPI JSON. This page is the readable companion for humans who want examples, integration order, and failure notes alongside the endpoint list.