Skip to content
Afina
Back to app

API Documentation

Afina is a fast image optimization API deployed on Vercel. It supports AVIF, WebP, JPEG, and PNG output with resize, crop, rotate, quality control, and metadata stripping. Images up to 50 MB are supported via the Blob-first upload flow.

Base URL

https://afina.vercel.app

In development, use http://localhost:3000

Authentication

No authentication required. All endpoints are publicly accessible. Access is controlled via CORS origin allowlist and rate limiting.

Recommended Flow

1

Get an upload token from POST /api/uploads/token

2

Upload the image to Vercel Blob using the @vercel/blob/client SDK

3

Send the blob URL to POST /api/optimize/from-blob with options

4

Receive the optimized image binary with metrics in response headers

Supported Formats

FormatInputOutput
JPEG
PNG
WebP
AVIF
GIF

GIF images are accepted as input and processed as their first frame. GIF output is not supported.

Limits

LimitValue
Max input file size50 MB
Max direct upload (multipart)4.5 MB
Max image dimensions100 megapixels
Batch max files50
Batch max decompressed size200 MB
Request timeout (single image)25 s
Request timeout (batch)50 s

Quick Start

Complete example: upload an image to Vercel Blob, optimize it, and download the result.

POSTComplete flow

End-to-end optimization using the Blob-first architecture. This is the recommended approach for images of any size.

Endpoints

GET/api/health

Health check endpoint.

POST/api/uploads/token

Internal endpoint called by the @vercel/blob/client SDK during uploads. You never call this endpoint directly — the SDK handles it automatically when you pass handleUploadUrl.

POST/api/optimize/from-blob

Optimize an image from a Vercel Blob URL. This is the primary endpoint for images of any size (up to 50 MB). Returns the optimized image binary with metrics in response headers.

POST/api/optimize

Direct multipart upload for small files (< 4.5 MB). For larger files, use the Blob flow. Returns the optimized image binary with the same response headers as /api/optimize/from-blob.

POST/api/batch/from-blob

Batch optimize a ZIP of images (max 50 files, 200 MB decompressed). Upload the ZIP to Blob first, then send the URL. Returns a ZIP containing optimized images and a report.json summary.

POST/api/uploads/delete

Delete one or more blobs by URL. Only valid Vercel Blob URLs are accepted (SSRF protection).

Options

OptionTypeDefaultDescription
format"avif"|"webp"|"jpeg"|"png""webp"Output format.
quality1-10080 (AVIF: 50)Encoding quality. Defaults: AVIF 50, WebP/JPEG/PNG 80.
maxWidthnumber-Max width in pixels (preserves aspect ratio).
maxHeightnumber-Max height in pixels (preserves aspect ratio).
rotatestring: "0"|"90"|"180"|"270""0"Rotate the image clockwise. Must be a string, not a number.
stripMetadatabooleantrueRemove EXIF and other metadata.
cropbooleanfalseEnable crop mode.
cropX, cropYnumber-Crop origin (top-left).
cropW, cropHnumber-Crop dimensions.

Rate Limiting

All endpoints are rate-limited per IP address using a sliding window of one minute. Rate limiting is disabled in development mode.

EndpointLimit
/api/optimize10 req/min/IP
/api/optimize/from-blob10 req/min/IP
/api/uploads/token10 req/min/IP
/api/uploads/delete10 req/min/IP
/api/batch/from-blob2 req/min/IP

Rate limit status is returned in response headers:

X-RateLimit-LimitMaximum requests allowed per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets

Error Codes

All errors return a JSON body with the following structure:

{
  "statusCode": 400,
  "statusMessage": "Bad Request",
  "data": {
    "error": "Missing 'file' in form data"
  }
}

For 429 responses, data also includes a retryAfter field (seconds until the rate limit resets).

400Invalid request (bad options, invalid URL, missing file)
403Origin not allowed (CORS)
413File too large (max 50 MB per image, 4.5 MB for direct upload)
429Rate limited — retry after X-RateLimit-Reset. Body includes retryAfter (seconds).
500Internal processing error

Response Headers

X-Input-FormatOriginal image format (jpeg, png, webp, avif, gif)
X-Output-FormatOutput format (avif, webp, jpeg, png)
X-Input-BytesOriginal file size in bytes
X-Output-BytesOptimized file size in bytes
X-Process-MsProcessing duration in milliseconds
Cache-Controlpublic, max-age=31536000, immutable (from-blob endpoint only)
X-RateLimit-LimitMaximum requests allowed per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets