API Documentation

Compress images programmatically with a simple REST API.

Quick start

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.

Authentication

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.

Endpoints

POST /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.

Parameters

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.

Response - small file (<2MB)

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"

Response - large file (≥2MB)  202 Accepted

{
  "id": "abc123xyz",
  "status": "processing",
  "poll_url": "/api/v1/status/abc123xyz",
  "estimated_seconds": 4
}
GET /api/v1/status/:id

Check the status of an async compression job. Poll this until status is complete or failed.

Response - complete

{
  "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"
}

Response - processing / pending

{
  "id": "abc123xyz",
  "status": "processing"
}

Response - failed

{
  "id": "abc123xyz",
  "status": "failed",
  "error": {
    "code": "compression_failed",
    "message": "Compression failed"
  }
}
GET /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.

Errors

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
400missing_sourceNeither file nor url was provided.
400invalid_paramA parameter value is invalid. Check the param field for which one.
400invalid_urlThe url is malformed or resolves to a private IP.
401unauthorizedMissing or invalid API key.
413file_too_largeFile exceeds the 25MB limit.
415unsupported_formatFile type is not JPG, PNG, WebP, or AVIF.
422fetch_failedCould not fetch the remote URL (timeout, non-2xx response, etc).
422not_readyDownload requested but job is not yet complete.
429rate_limit_exceededToo many requests. Wait until the reset time in the error message.
500compression_failedInternal error during compression. Try again.

Webhooks

Pass a webhook_url on async requests and we'll POST the job result to that URL when processing completes.

Payload

{
  "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"
}

Verifying signatures

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))