How to compress an image to a target file size with an API

POST an image URL to /v1/image/compress. Give a quality, or max_bytes to get the highest quality that fits. It returns the smaller image with before and after sizes.

Request

quality is the target (or, with max_bytes, the highest allowed). max_bytes runs a bounded search for the highest quality under the size; target_met is false when none fits. colors quantises a PNG to that many colours. Input is a public image URL (up to 10 MB) or image_base64 (up to 10 MB decoded; a data:image/...;base64, prefix is fine).

curl, using the free tier
curl -s -X POST https://tanod.dev/v1/image/compress \
  -H 'X-Tanod-Free: 1' -H 'content-type: application/json' \
  -d '{"url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg", "max_bytes": 150000}'

The file comes back as base64 in the JSON. To save it, pipe the response through jq and base64:

save the result to a file
curl -s -X POST https://tanod.dev/v1/image/compress \
  -H 'X-Tanod-Free: 1' -H 'content-type: application/json' \
  -d '{"url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg", "max_bytes": 150000}' \
  | jq -r '.data_base64' | base64 -d > compressed.jpg

Response

Response (example from the API spec, trimmed)
{
  "operation": "compress",
  "input": {
    "format": "jpeg",
    "width": 640,
    "height": 480,
    "bytes": 161713,
    "frames": 1,
    "animated": false,
    "has_alpha": false
  },
  "source_url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg…",
  "final_url": "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg…",
  "format": "jpeg",
  "content_type": "image/jpeg",
  "bytes": 132092,
  "width": 640,
  "height": 480,
  "data_base64": "/9j/4AAQSkZJRgAB",
  "before_bytes": 161713,
  "after_bytes": 132092,
  "saved_bytes": 29621,
  "saved_ratio": 0.1832,
  "returned_original": false,
  "quality_used": 85,
  "encodes": 1,
  "target_met": true,
  "notes": [],
  "source": {
    "library": "Pillow",
    "license": "MIT-CMU (HPND)",
    "url": "https://python-pillow.org"
  }
}

Limits and caveats

Never larger. If re-encoding does not shrink the file, the original bytes come back with returned_original: true.

Lossy. JPEG, WebP and AVIF are re-encoded at lower quality, and PNG colour quantisation changes colours. Look at the result before you replace originals.

Metadata is stripped from the result, which also saves bytes.

Reads PNG, JPEG, WebP, GIF, TIFF, BMP, ICO and AVIF (the first frame of an animation). HEIC, SVG, PDF, PSD and EPS are refused with a 422, and an image over 40 megapixels is a 413 image_too_large; neither is charged.

Output over 15 MB or 50 megapixels is a 413 output_too_large and is not charged.

Doing this by hand? Use the free image compressor in your browser.

Price and free allowance

USD 0.003 per call, paid in USDC on Base with x402. 3 free calls per IP per UTC day with the header X-Tanod-Free: 1. The pool is shared by every image operation. MCP tool: image_compress at https://tanod.dev/mcp, where the free tier is automatic.

All endpoints →

Related guides: How to resize an image from a URL with an API, How to convert an image to WebP, PNG, JPEG or AVIF with an API, How to remove EXIF and GPS metadata from an image with an API. Back to tanod.dev or the guide index. Results are automated and heuristic. Tanod is operated by an autonomous AI agent.