Compress images programmatically with a simple REST API.
The Compressorize API is a standard REST API that returns JSON and binary files over HTTPS. Use it from any language - Ruby, Python, JavaScript, PHP, Go, or anything that can make an HTTP request.
Compress an image in one request:
curl -X POST https://compressorize.com/api/v1/compress \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "[email protected]" \ --output compressed.jpg
Get your API key from your account.
Pass your API key as a Bearer token in the Authorization header on every request:
Authorization: Bearer YOUR_API_KEY
Rate limits: Free - 20 requests/min · Paid - 100 requests/min.
Limits reset every 60 seconds. The response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
/api/v1/compress
Compress an image. Files under 2MB are processed immediately and the compressed file is returned directly. Files 2MB and over are processed asynchronously - you'll receive a job ID to poll.
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
multipart | One of file/url | The image file to compress. JPG, PNG, WebP, or AVIF. Max 25MB. |
url |
string | One of file/url | Public URL of an image to fetch and compress. Must be http/https and resolve to a public IP. |
format |
string | No | Output format: jpeg, png, webp, avif, or auto. Default: auto (keeps original format). |
quality |
string/integer | No | smart (default) uses perceptual analysis to pick the best quality. Or pass an integer 1–100. |
max_width |
integer | No | Resize image to this max width (px), preserving aspect ratio. |
max_height |
integer | No | Resize image to this max height (px), preserving aspect ratio. |
strip_metadata |
boolean | No | Strip EXIF/metadata from output. Default: true. |
webhook_url |
string | No | HTTPS URL to notify when an async job completes. Must be https. See Webhooks. |
Returns the compressed image as a binary file download with these response headers:
X-Original-Size: 1200000 X-Compressed-Size: 340000 X-Savings-Percent: 72 X-Format: webp X-Job-Id: abc123xyz Content-Disposition: attachment; filename="compressed.webp"
{
"id": "abc123xyz",
"status": "processing",
"poll_url": "/api/v1/status/abc123xyz",
"estimated_seconds": 4
}
/api/v1/status/:id
Check the status of an async compression job. Poll this until status is complete or failed.
{
"id": "abc123xyz",
"status": "complete",
"download_url": "/api/v1/download/abc123xyz",
"original_size": 1200000,
"compressed_size": 340000,
"savings_percent": 72,
"format": "webp",
"dimensions": { "width": 1920, "height": 1080 },
"expires_at": "2025-01-15T14:30:00Z"
}
{
"id": "abc123xyz",
"status": "processing"
}
{
"id": "abc123xyz",
"status": "failed",
"error": {
"code": "compression_failed",
"message": "Compression failed"
}
}
/api/v1/download/:id
Redirects (302) to a time-limited download URL for the compressed file. The link is valid for 1 hour. Job results are available for 24 hours after completion - after that they are permanently deleted.
All errors return JSON in this shape:
{
"error": {
"code": "invalid_param",
"message": "Invalid format 'tiff'. Use: jpeg, png, webp, avif, or auto",
"param": "format"
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | missing_source | Neither file nor url was provided. |
| 400 | invalid_param | A parameter value is invalid. Check the param field for which one. |
| 400 | invalid_url | The url is malformed or resolves to a private IP. |
| 401 | unauthorized | Missing or invalid API key. |
| 413 | file_too_large | File exceeds the 25MB limit. |
| 415 | unsupported_format | File type is not JPG, PNG, WebP, or AVIF. |
| 422 | fetch_failed | Could not fetch the remote URL (timeout, non-2xx response, etc). |
| 422 | not_ready | Download requested but job is not yet complete. |
| 429 | rate_limit_exceeded | Too many requests. Wait until the reset time in the error message. |
| 500 | compression_failed | Internal error during compression. Try again. |
Pass a webhook_url on async requests and we'll POST the job result to that URL when processing completes.
{
"id": "abc123xyz",
"status": "complete",
"download_url": "https://compressorize.com/api/v1/download/abc123xyz",
"original_size": 1200000,
"compressed_size": 340000,
"savings_percent": 72,
"format": "webp",
"dimensions": { "width": 1920, "height": 1080 },
"expires_at": "2025-01-15T14:30:00Z"
}
Every webhook request includes an X-Compressorize-Signature header. Verify it using your webhook signing secret:
# Ruby
expected = OpenSSL::HMAC.hexdigest("SHA256", YOUR_WEBHOOK_SECRET, request.body.read)
verified = ActiveSupport::SecurityUtils.secure_compare(expected, request.headers["X-Compressorize-Signature"])
# Python
import hmac, hashlib
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
verified = hmac.compare_digest(expected, request.headers["X-Compressorize-Signature"])
# JavaScript (Node.js)
const crypto = require("crypto")
const expected = crypto.createHmac("sha256", YOUR_WEBHOOK_SECRET).update(body).digest("hex")
const verified = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
# PHP
$expected = hash_hmac("sha256", $body, $secret);
$verified = hash_equals($expected, $_SERVER["HTTP_X_COMPRESSORIZE_SIGNATURE"]);
# Go
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(body))
expected := hex.EncodeToString(mac.Sum(nil))
verified := hmac.Equal([]byte(expected), []byte(signature))