ToolsXpo
ToolsXpo

Image & SVG

Image Converter API

GET request to /v1/image-converter, returning json in the standard envelope.

GET/v1/image-converter3 credits / call + 1 per megapixel Free tier

Authentication

Send your API key as a bearer token. Create one free in the dashboard.

Authorization: Bearer $TOOLSXPO_KEY

Parameters

Send these as a JSON body, as query-string parameters, or as form fields alongside uploaded files (-F file=@photo.jpg, up to 10). A raw image body works too, with the options in the query string. Every request needs one image source: image, imageUrl, an uploaded file, or images for a batch. Fields marked required must be present; everything else has a sensible default.

NameTypeDefaultDescription
imagestring—The image as base64, or a data: URL (data:image/png;base64,...). JPEG, PNG, WebP, AVIF, GIF or HEIC.
imageUrlstring—Public http(s) URL to fetch the image from, instead of sending it inline. Max 20 MB.
namestring—Optional label echoed back in the result, e.g. the file name.
imagesarray of object1–10—Batch mode: up to 10 images, each { image | imageUrl, name? }. Also accepts a list of URLs, or a Source Files step's output. The same options apply to all of them.
images[].imagestring—The image as base64, or a data: URL (data:image/png;base64,...). JPEG, PNG, WebP, AVIF, GIF or HEIC.
images[].imageUrlstring—Public http(s) URL to fetch the image from, instead of sending it inline. Max 20 MB.
images[].namestring—Optional label echoed back in the result, e.g. the file name.
formatrequiredstring—Output format: jpeg, png, webp or avif. `jpg` is accepted.One of: jpeg, png, webp, avif
qualityinteger1–100—1–100. Defaults: JPEG 82, WebP 80, AVIF 60. Ignored for PNG and lossless output.
losslessbooleanfalseLossless WebP or AVIF. PNG is always lossless; JPEG never is.
maxWidthinteger1–12000—Shrink to at most this many pixels wide, keeping the aspect ratio. Never enlarges.
maxHeightinteger1–12000—Shrink to at most this many pixels high, keeping the aspect ratio. Never enlarges.
backgroundstring#ffffffColour placed behind transparent areas when the output format has no transparency (JPEG).
outputstringurlurl (default): each image comes back as a download link, kept for your plan's file retention. base64: the image inline in the JSON. Add ?output=binary to the request URL to get the file itself instead of JSON.One of: url, base64

Example request

curl "https://api.toolsxpo.com/v1/image-converter?image=iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5%2BAAAAL0lEQVR42mNgAIIojRP%2FpwHxJSBmAAtEAQWA%2BFIUTKAHKADEl3pgAvuAAkB8CYgB9cMhwU8NlV0AAAAASUVORK5CYII%3D&format=webp&quality=80&lossless=false&background=%23ffffff&output=url" \
  -H "Authorization: Bearer $TOOLSXPO_KEY"

Response

Every endpoint answers with the same envelope: the result in data, what the call cost and your standing in meta. One entry per image in results, totals in summary. By default each image is a download link.

{
  "ok": true,
  "data": {
    "results": [
      {
        "index": 0, "name": "photo.jpg", "ok": true,
        "inputFormat": "jpeg", "format": "webp", "contentType": "image/webp",
        "width": 1600, "height": 1067, "inputWidth": 4000, "inputHeight": 2667,
        "inputBytes": 3104822, "bytes": 143210, "savedPercent": 95.4,
        "quality": 80, "lossless": false, "unchanged": false,
        "engine": "images", "warnings": [], "billableMegapixels": 3.84,
        "url": "https://ai-cdn.toolsxpo.com/…/image-outputs/9f2c….webp",
        "expiresAt": "2026-10-22T14:54:19.772Z"
      },
      { "index": 1, "name": "broken.png", "ok": false, "error": "Not a supported image. Send JPEG, PNG, WebP, AVIF, GIF or HEIC." }
    ],
    "summary": {
      "total": 2, "succeeded": 1, "failed": 1,
      "inputBytes": 3104822, "outputBytes": 143210, "savedPercent": 95.4,
      "billableMegapixels": 3.84, "engines": { "images": 1 }
    },
    "_meter": { "unit": "megapixel", "quantity": 3.84 }
  },
  "meta": {
    "tool": "image-converter", "credits": 5, "baseCredits": 5, "billedFrom": "plan",
    "pricing": { "mode": "metered", "meterUnit": "megapixel", "creditsPerUnit": 1 },
    "quota": { "limit": 30000, "used": 412, "remaining": 29588, "resetsAt": "2026-11-01T00:00:00.000Z", "scope": "account", "plan": "pro" }
  }
}

data fields

FieldTypeDescription
resultsarrayOne entry per input image, in the order sent.
results[].indexnumberPosition of the image in the request (0-based).
results[].namestring | nullThe `name` you sent, or the uploaded file name.
results[].okboolean`false` if this image failed; the others still succeed. A failed image costs nothing.
results[].errorstringWhy this image failed. Present only when `ok` is false.
results[].urlstringDownload link for the converted image (default `output: "url"`). Public but unguessable.
results[].expiresAtstring (ISO 8601)When `url` stops working — your plan's file retention (1–20 days).
results[].base64stringThe converted image inline, when you sent `output: "base64"` (or if storing failed — see `warnings`).
results[].format"jpeg" | "png" | "webp" | "avif"Format of the output.
results[].contentTypestringMIME type of the output, e.g. `image/webp`.
results[].widthnumberOutput width in pixels.
results[].heightnumberOutput height in pixels.
results[].inputFormatstringDetected format of the input (jpeg, png, webp, avif, gif, heic…).
results[].inputWidthnumberInput width in pixels (after EXIF rotation).
results[].inputHeightnumberInput height in pixels.
results[].inputBytesnumberSize of the input file.
results[].bytesnumberSize of the output file.
results[].savedPercentnumberHow much smaller the output is (negative if larger, which a format change can cause).
results[].qualitynumber | nullQuality actually used; `null` for PNG and lossless output. With `targetKb`, the quality the search settled on.
results[].losslessbooleanWhether the output is lossless (always true for PNG).
results[].unchangedbooleanCompressor only: no smaller version existed, so the original was returned.
results[].engine"wasm" | "images"What processed it: our in-Worker codecs, or Cloudflare Images for large photos.
results[].warningsstring[]Anything worth knowing, e.g. transparency filled for JPEG, or only the first frame of a GIF converted.
results[].billableMegapixelsnumberUnits this image added to the charge.
summary.totalnumberImages in the request.
summary.succeedednumberImages converted.
summary.failednumberImages that failed (not charged).
summary.inputBytesnumberTotal input size of the successful images.
summary.outputBytesnumberTotal output size.
summary.savedPercentnumberOverall saving.
summary.billableMegapixelsnumberTotal billable units; `meta.credits` = 1 + this, rounded up.
summary.enginesobjectHow many images each engine handled, e.g. `{ "wasm": 2 }`.
_meterobjectBilling meter (`{ unit: "megapixel", quantity }`) — the same number as `summary.billableMegapixels`.

meta fields (every tool)

FieldTypeDescription
meta.toolstringThe tool id that ran.
meta.authstringHow the call authenticated — `key` for an API key.
meta.creditsnumberEverything this call cost, in credits (both currencies together).
meta.baseCreditsnumberThe tool's own price — the compute-credit part of `credits`.
meta.aiCreditsnumberAI credits spent on a model step. Present only when non-zero.
meta.billedFromstringWhich balance paid: the plan allowance or the purchased credit balance.
meta.pricing.mode"flat" | "metered"Flat price per call, or metered by usage.
meta.pricing.meterUnitstringMetered tools: what is counted (e.g. `megapixel`).
meta.pricing.creditsPerUnitnumberMetered tools: credits per unit counted.
meta.quota.limitnumberYour plan's allowance for the current period.
meta.quota.usednumberAllowance used so far this period.
meta.quota.remainingnumberAllowance left this period.
meta.quota.resetsAtstring (ISO 8601)When the allowance resets.
meta.quota.creditBalancenumberPurchased credits remaining, used after the allowance.
meta.quota.scopestringWhat the quota belongs to — `account` (your workspace) for API keys.
meta.quota.planstringYour plan id.

Ask for the file itself instead of JSON with ?output=binary on the URL (or an Accept: image/* header): one image comes back as the image, a batch as a ZIP containing every image plus results.json. The headers below carry the numbers. Errors are always JSON.

If every image in a call fails, the call returns 422 and is refunded.

Headers on a binary response

FieldTypeDescription
Content-TypeheaderThe image's MIME type, or `application/zip` for a batch.
Content-DispositionheaderSuggested file name, e.g. `photo.webp` or `images.zip`.
X-ToolsXpo-CreditsheaderWhat the call cost.
X-ToolsXpo-FormatheaderSingle image: output format.
X-ToolsXpo-Width / -HeightheaderSingle image: output dimensions.
X-ToolsXpo-Input-Bytes / -BytesheaderSingle image: input and output size.
X-ToolsXpo-Saved-PercentheaderSingle image: saving.
X-ToolsXpo-QualityheaderSingle image: quality used (empty for lossless).
X-ToolsXpo-EngineheaderSingle image: `wasm` or `images`.
X-ToolsXpo-WarningsheaderSingle image: warnings, separated by ` | `.
X-ToolsXpo-Succeeded / -FailedheaderBatch: how many images succeeded and failed. Per-image detail is in `results.json` inside the ZIP.

Errors

Failures use the same envelope with ok: false and a machine-readable code.

StatusCodeMeaning
400invalid_inputA parameter failed validation. `details` lists which.
401unauthorizedMissing or invalid API key.
402quota_exceededOut of credits on this workspace.
404not_foundNo such tool.
429rate_limitedToo many requests. Retry after `Retry-After` seconds.
503service_unavailableTemporarily disabled by an administrator.

How to use the Image Converter API

Convert JPEG, PNG, WebP, AVIF, GIF and HEIC images to WebP, AVIF, JPEG or PNG — one image or up to ten per call, at the quality you choose, optionally resized.

Options

  • format (required) — webp, avif, jpeg (or jpg), png.
  • quality — 1–100. Defaults: JPEG 82, WebP 80, AVIF 60. Ignored for PNG and lossless output.
  • lossless — true for exact-pixel WebP or AVIF (larger files).
  • maxWidth / maxHeight — shrink to fit, keeping the aspect ratio. Never enlarges.
  • background — the colour behind transparent areas when converting to JPEG, which has no transparency (default #ffffff). Without it transparent pixels would turn black.

Three ways to send images

Upload files (multipart) — the way most code already sends a file. Repeat -F file=@… for up to 10 images; any other field is an option:

Bash
curl -X POST "https://tools.toolsxpo.com/v1/image-converter?output=binary" \
  -H "Authorization: Bearer $image-converterSXPO_KEY" \
  -F file=@photo.png -F format=webp -F quality=80 -o photo.webp

A raw body — the image bytes, options in the query string:

Bash
curl -X POST "https://tools.toolsxpo.com/v1/image-converter?format=avif&maxWidth=1200&output=binary" \
  -H "Authorization: Bearer $image-converterSXPO_KEY" -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg -o photo.webp

A URL or base64, as JSON — imageUrl (we fetch it, up to 20 MB) or image (base64 or a data: URL). For a batch, send images: [{ imageUrl }, { image }, …] — mix both freely:

JAVASCRIPT
const res = await fetch("https://tools.toolsxpo.com/v1/image-converter", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.image-converterSXPO_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ images: [{ imageUrl: "https://example.com/a.jpg" }, { imageUrl: "https://example.com/b.png" }], format: "webp", quality: 80 }),
});
const { data } = await res.json();
for (const r of data.results) if (r.ok) console.log(r.format, r.bytes, r.savedPercent);

A GET works too: ?imageUrl=…&imageUrl=…&OPTION=… (repeat imageUrl for a batch).

What comes back

  • A download link (default): data.results[] has one entry per image with a url you can download, embed or pass on, and expiresAt — links last for your plan's file retention. Each entry also has format, contentType, width, height, inputBytes, bytes, savedPercent, quality, engine and warnings. Failed images have ok: false and an error, and cost nothing. data.summary totals the call.
  • Base64 instead: send output: "base64" and each entry carries the image as base64 rather than a link — nothing is stored.
  • The file itself: add ?output=binary to the URL (or send Accept: image/*). One image comes back as the image; a batch as a ZIP with a results.json inside. Credits, size and format are in X-ToolsXpo-* response headers. Errors are always JSON.
JSON
{ "ok": true,
  "data": { "results": [ { "ok": true, "format": "webp", "width": 1600, "height": 1067, "bytes": 143210,
                           "savedPercent": 82.4, "url": "https://ai-cdn.toolsxpo.com/…/9f2c….webp",
                           "expiresAt": "2026-10-11T09:30:00.000Z" } ],
            "summary": { "succeeded": 1, "failed": 0 } },
  "meta": { "credits": 3 } }

Links are unguessable but public: anyone holding one can open it until it expires. Use output: "base64" or ?output=binary for images that must not be stored.

Cost

A base price per call plus a charge per billable unit — both are shown in the price box on this page and returned in meta.credits with every response. An image's units are 0.2 × input megapixels + output megapixels × (3 for AVIF, else 1), at least 0.25 — so a typical 1–2 MP web image is about 2 units, and shrinking a big photo is cheap because the output is small. If every image in a call fails, the call is refunded.

Limits

  • Up to 10 images per call, 20 MB each, 40 MB per call.
  • Large photos (roughly over 3–4 megapixels) are processed by Cloudflare Images instead of our in-Worker codecs — engine tells you which. You do not need to do anything; the cheapest engine that can handle the image is chosen automatically.
  • AVIF output is limited to about 1.5 MP in the Worker and 1,200 px per side on Cloudflare Images. For a large AVIF, pass maxWidth.
  • HEIC and GIF input are accepted (GIF: first frame only). SVG, TIFF and BMP are not accepted by the API — convert those in the browser at toolsxpo.com.
  • Metadata (EXIF, GPS) is removed; phone photos are rotated upright first.

In automations

Add the tool as a step. Its images are saved as files and the step output carries a url for each, so a later step can use {{previous_step.results.0.url}} — in an email, a webhook, or another image step's imageUrl. After a Source Files step, put {{previous_step}} in images to process every uploaded file.

Try it without code

Every field is available in the API Playground — including a file picker — and the web tool does the same thing in your browser for free, with nothing uploaded.

Choosing a format

  • WebP — the safe default: much smaller than JPEG/PNG, supports transparency, works in every current browser.
  • AVIF — usually the smallest at the same visual quality; slower to encode (it counts 3× toward billing).
  • JPEG — maximum compatibility for photos; no transparency.
  • PNG — lossless; best for screenshots, logos and anything you will edit again.