API
One request, the SVG comes back. No job polling, no SDK. The same engine and the same output as the editor.
Quick start
POST https://api.svgproof.com/v1/vectorize with your API key and the image as a multipart image field (or the raw bytes as the body). The response body is the file.
curl https://api.svgproof.com/v1/vectorize \
-H "Authorization: Bearer $SVGPROOF_KEY" \
-F image=@logo.png \
-o logo.svgKeys and credits
Send your key as Authorization: Bearer svp_live_…. A live key pays one credit per delivered image: a refused image (photo_like, decode, …), a rate-limited or timed-out request costs nothing. Every answer says what it cost in Vz-Credits-Charged and what is left in Vz-Credits-Left; GET /v1/account shows your balance, usage per month and limits.
Test mode is free: add mode=test, or use a svp_test_… key. You get the real vector with translucent diagonal bands over it (Vz-Mode: test), enough to build and check an integration, not to ship. Calls without a key are limited per address and may be refused with key_required or watermarked, depending on the plan.
Retries are safe with an Idempotency-Key header (up to 128 visible characters, for example your order id): a repeat within 24 hours returns the same file and is not charged again.
curl https://api.svgproof.com/v1/account -H "Authorization: Bearer $SVGPROOF_KEY"
# {"credits": 1460, "usage": [{"month": "2026-10", "images": 40, "credits": 40}],
# "limits": {"per_minute": 600, "burst": 100, "max_parallel": 8, "max_batch": 100}, …}Parameters
All optional, as query parameters on the URL.
| Name | Values | Default | What it does |
|---|---|---|---|
format | svg | png | svg | Output format. |
background | string | auto |
|
max_colors | integer ≥ 1 | — | Merge the palette down to at most this many flat colors. Omit for the automatic palette. |
palette | string | — | Brand colors, comma-separated hex. Every color of the result is replaced by the nearest one of these (gradients become flat). At most 256. |
scale | number > 0 ≤ 16 | 1 | PNG only: output size relative to the uploaded image. The longest side is capped at 4096 px. |
width | integer ≥ 1 ≤ 4096 | — | PNG only: output width in pixels (overrides |
knockout | boolean | true | With a |
mode | live | test | live |
|
background=remove clears same-colored areas inside the art too (the holes of an O, a gap in a shield): they are the background seen through the art. knockout=false keeps them, for a white star on a badge that must stay white. A hex background (background=141155) shows through those holes.
Response
The file, with its size in headers. Send Accept: application/json to get the SVG inside a JSON body together with the palette and the background instead.
| Header | Meaning |
|---|---|
Vz-Paths | Number of paths. |
Vz-Nodes | Path segments (lines + curves): how simple the file is. |
Vz-Colors | Colors in the result. |
Vz-Ms | Engine time in milliseconds. |
Vz-Bytes | SVG size in bytes. |
RateLimit-Limit | Size of your request bucket (the burst). |
RateLimit-Remaining | Requests left in the bucket. |
RateLimit-Reset | Seconds until the bucket is full again. |
RateLimit-Policy | Refill rate, `<requests>;w=60` (requests per 60 seconds). |
Vz-Mode | `live` or `test` (watermarked, free). |
Vz-Credits-Charged | With an API key: credits this request cost (0 in test mode or for a replayed `Idempotency-Key`). |
Vz-Credits-Left | With an API key: the balance after this request (absent for unlimited keys). |
Vz-Request-Id | On every response: quote it when you contact support. |
{
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 600 480\" …",
"width": 600.0, "height": 480.0, "src_width": 600.0, "src_height": 480.0,
"stats": { "paths": 12, "nodes": 287, "colors": 7, "bytes": 13799, "ms": 22.4 },
"background": {
"traced": "solid", "checker_removed": false, "palette_index": 0,
"edit": "remove", "hex": null, "gradient": null, "area": 0.0
},
"palette": [
{ "index": 0, "hex": "#fcf5fc", "gradient": null, "paths": 2, "area": 0.0, "removed": true, "background_only": false },
{ "index": 1, "hex": "#7330ff", "gradient": [[0.0, "#be51ef"], [0.86, "#7330ff"], [1.0, "#7330ff"]],
"paths": 2, "area": 0.156, "removed": false, "background_only": false }
]
}palette[].paths and area do not count the background layer; the background is reported once, under background. traced says what the trace found under the art (solid, gradient, none); hex is what is there now (null = transparent).
Rate limits
Each API key gets 600 requests per minute (a bucket of 100, so bursts are fine) and at most 8 at once, wherever the requests come from. A batch counts one request per image. Without a key it is 60 per minute per address (bucket 20, 4 at once). A refused request costs you nothing. Higher limits come with higher plans.
Every response says where you stand: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset (seconds until the bucket is full) and RateLimit-Policy. When you are over, you get 429 with Retry-After (and error.retry_after in the body): wait that long. 503 busy (every core taken, the waiting line full) works the same way.
import { readFile } from 'node:fs/promises';
async function vectorize(path, params = {}) {
const body = await readFile(path);
for (let attempt = 0; attempt < 5; attempt++) {
const form = new FormData();
form.append('image', new Blob([body]), 'logo.png');
const res = await fetch(`https://api.svgproof.com/v1/vectorize?${new URLSearchParams(params)}`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SVGPROOF_KEY}` },
body: form,
});
if (res.status === 429 || res.status === 503) {
const wait = Number(res.headers.get('retry-after') ?? 1);
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
if (!res.ok) throw new Error((await res.json()).error.message);
return res.text();
}
throw new Error('still rate limited after 5 attempts');
}Errors
Errors are JSON with a stable code: {"error": {"code": "decode", "message": "…"}}. Match on the code; the message may change.
| Code | HTTP | Meaning |
|---|---|---|
bad_request | 400 | A parameter is invalid. The message names it. |
unsupported_format | 400 | The requested format is not available (svg, png). |
decode | 415 | The body is not an image we can read. |
too_large | 413 | Over 17 megapixels or 8192 px on a side. Checked from the file header, before decoding. |
photo_like | 422 | A photo or a textured image. We vectorize logos and illustrations. |
rate_limited | 429 | Too many requests, or too many at once. Wait Retry-After seconds. See rate limits. |
busy | 503 | Every core is taken and the waiting line is full. Wait Retry-After seconds. |
timeout | 408 | The request, upload included, took longer than 60 seconds. |
not_found | 404 | Editor sessions only: the session expired. Not returned by /v1/vectorize. |
internal | 500 | Something broke on our side. |
unavailable | 503 | The server is shutting down. Retry. |
invalid_key | 401 | The API key is unknown or was revoked. |
key_required | 401 | This endpoint needs an API key: Authorization: Bearer svp_.... |
no_credits | 402 | The balance does not cover the request. Nothing was charged. Add credits, or send mode=test. |
too_many_images | 400 | More images in one batch than allowed (see Batch). |
Limits
- Images: PNG, JPEG, WEBP, GIF or BMP, at most 32 MB, 17 megapixels and 8192 px on a side.
- Logos, icons, wordmarks and flat illustrations work best. Photos and heavily textured art are rejected with
photo_like. - PNG output:
scaleup to 16, long side capped at 4096 px, transparent where there is no background. palette: at most 256 colors. Every color of the result moves to the nearest one (a weighted RGB distance), gradients become flat.
Batch
POST https://api.svgproof.com/v1/batch with up to 100 files, each in a field named image, and the same query parameters as /v1/vectorize for all of them. The images are traced in parallel and each succeeds or fails on its own; only delivered images are charged. The answer is a zip with one file per image and manifest.json (per image: ok, file or error, stats), or with Accept: application/json one JSON document with each SVG inline.
curl https://api.svgproof.com/v1/batch \
-H "Authorization: Bearer $SVGPROOF_KEY" \
-F image=@logo-a.png -F image=@logo-b.jpg -F image=@logo-c.webp \
-o logos.zip
unzip -l logos.zip # logo-a.svg, logo-b.svg, logo-c.svg, manifest.jsonExamples
Snap to brand colors
curl "https://api.svgproof.com/v1/vectorize?palette=141155,03517b,ffffff,c0392b" \
-H "Authorization: Bearer $SVGPROOF_KEY" \
-F image=@logo.png -o logo-brand.svg -D -
# vz-colors: 4Transparent PNG at 2×
Remove the background and render 1200 px wide (a 600 px logo at 2×).
curl "https://api.svgproof.com/v1/vectorize?format=png&width=1200&background=remove" \
-H "Authorization: Bearer $SVGPROOF_KEY" \
-F image=@logo.png -o logo@2x.pngJSON with palette and background
curl "https://api.svgproof.com/v1/vectorize?background=remove" \
-H "Authorization: Bearer $SVGPROOF_KEY" \
-H "Accept: application/json" \
-F image=@logo.png