svgproof

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.svg

Keys 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.

NameValuesDefaultWhat it does
formatsvg | pngsvg

Output format.

backgroundstringauto

auto: a baked-in checkerboard (fake transparency) is removed, a solid background is kept. keep: everything as in the image. remove: transparent background; shapes of the same color on top of it (letter counters) stay. A hex color (ffffff, or %23ffffff): a solid background in that color, replacing the traced one or added under transparent art.

max_colorsinteger ≥ 1—

Merge the palette down to at most this many flat colors. Omit for the automatic palette.

palettestring—

Brand colors, comma-separated hex. Every color of the result is replaced by the nearest one of these (gradients become flat). At most 256.

scalenumber > 0 ≤ 161

PNG only: output size relative to the uploaded image. The longest side is capped at 4096 px.

widthinteger ≥ 1 ≤ 4096—

PNG only: output width in pixels (overrides scale; the height follows the aspect ratio).

knockoutbooleantrue

With a background edit, shapes inside the art in the background's color (letter counters) go too. false keeps them.

modelive | testlive

test: free output with a watermark, for integrating. Test keys and (on servers that say so) calls without a key are always test.

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.

HeaderMeaning
Vz-PathsNumber of paths.
Vz-NodesPath segments (lines + curves): how simple the file is.
Vz-ColorsColors in the result.
Vz-MsEngine time in milliseconds.
Vz-BytesSVG size in bytes.
RateLimit-LimitSize of your request bucket (the burst).
RateLimit-RemainingRequests left in the bucket.
RateLimit-ResetSeconds until the bucket is full again.
RateLimit-PolicyRefill rate, `<requests>;w=60` (requests per 60 seconds).
Vz-Mode`live` or `test` (watermarked, free).
Vz-Credits-ChargedWith an API key: credits this request cost (0 in test mode or for a replayed `Idempotency-Key`).
Vz-Credits-LeftWith an API key: the balance after this request (absent for unlimited keys).
Vz-Request-IdOn 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.

CodeHTTPMeaning
bad_request400A parameter is invalid. The message names it.
unsupported_format400The requested format is not available (svg, png).
decode415The body is not an image we can read.
too_large413Over 17 megapixels or 8192 px on a side. Checked from the file header, before decoding.
photo_like422A photo or a textured image. We vectorize logos and illustrations.
rate_limited429Too many requests, or too many at once. Wait Retry-After seconds. See rate limits.
busy503Every core is taken and the waiting line is full. Wait Retry-After seconds.
timeout408The request, upload included, took longer than 60 seconds.
not_found404Editor sessions only: the session expired. Not returned by /v1/vectorize.
internal500Something broke on our side.
unavailable503The server is shutting down. Retry.
invalid_key401The API key is unknown or was revoked.
key_required401This endpoint needs an API key: Authorization: Bearer svp_....
no_credits402The balance does not cover the request. Nothing was charged. Add credits, or send mode=test.
too_many_images400More images in one batch than allowed (see Batch).

Limits

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.json

Examples

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: 4

Transparent 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.png

JSON 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