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
Get an upload token from POST /api/uploads/token
Upload the image to Vercel Blob using the @vercel/blob/client SDK
Send the blob URL to POST /api/optimize/from-blob with options
Receive the optimized image binary with metrics in response headers
Supported Formats
| Format | Input | Output |
|---|---|---|
| JPEG | ✓ | ✓ |
| PNG | ✓ | ✓ |
| WebP | ✓ | ✓ |
| AVIF | ✓ | ✓ |
| GIF | ✓ | — |
GIF images are accepted as input and processed as their first frame. GIF output is not supported.
Limits
| Limit | Value |
|---|---|
| Max input file size | 50 MB |
| Max direct upload (multipart) | 4.5 MB |
| Max image dimensions | 100 megapixels |
| Batch max files | 50 |
| Batch max decompressed size | 200 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.
Complete flowEnd-to-end optimization using the Blob-first architecture. This is the recommended approach for images of any size.
Endpoints
/api/healthHealth check endpoint.
/api/uploads/tokenInternal 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.
/api/optimize/from-blobOptimize 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.
/api/optimizeDirect 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.
/api/batch/from-blobBatch 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.
/api/uploads/deleteDelete one or more blobs by URL. Only valid Vercel Blob URLs are accepted (SSRF protection).
Options
| Option | Type | Default | Description |
|---|---|---|---|
| format | "avif"|"webp"|"jpeg"|"png" | "webp" | Output format. |
| quality | 1-100 | 80 (AVIF: 50) | Encoding quality. Defaults: AVIF 50, WebP/JPEG/PNG 80. |
| maxWidth | number | - | Max width in pixels (preserves aspect ratio). |
| maxHeight | number | - | Max height in pixels (preserves aspect ratio). |
| rotate | string: "0"|"90"|"180"|"270" | "0" | Rotate the image clockwise. Must be a string, not a number. |
| stripMetadata | boolean | true | Remove EXIF and other metadata. |
| crop | boolean | false | Enable crop mode. |
| cropX, cropY | number | - | Crop origin (top-left). |
| cropW, cropH | number | - | 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.
| Endpoint | Limit |
|---|---|
| /api/optimize | 10 req/min/IP |
| /api/optimize/from-blob | 10 req/min/IP |
| /api/uploads/token | 10 req/min/IP |
| /api/uploads/delete | 10 req/min/IP |
| /api/batch/from-blob | 2 req/min/IP |
Rate limit status is returned in response headers:
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).