Convert PNG to SVG | SVGProof

Turn a logo into a clean, print-ready SVG

Hosted MCP Server

npx add-mcp 'https://mcp.svgproof.com/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

API

One request, the SVG comes back. No job polling, no SDK. The same engine and the same output as the editor. Working with an AI agent? Use the MCP server.

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 [email protected] \
  -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. It returns a PNG preview only (format=png, 512 px on the long side, or smaller with width), the same file as the website's free download, with Vz-Mode: test and the real node and color counts in the headers: enough to build and check an integration. SVG, JSON answers and batches need a live key and answer 403 test_mode_limit in test mode. Each account gets 10 free previews in all, counted down in Vz-Test-Previews-Left; after that test mode answers 402 test_previews_used. While the account has a subscription (Pro or an API plan) test mode is unlimited. Calls without a key are refused with key_required.

Retries are safe with an Idempotency-Key header (up to 128 visible characters, for example your order id): a repeat of the same request (same parameters and file) within 24 hours returns the same file and is not charged again. A key reused for a different request is refused with idempotency_key_reused: send a new key with each new request.

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 | pngsvgOutput format.
backgroundstringautoauto: 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 ≤ 161PNG 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).
knockoutbooleantrueWith a background edit, shapes inside the art in the background's color (letter counters) go too. false keeps them.
modelive | testlivetest: free, for integrating: 10 previews per account (unlimited with a subscription), a PNG preview only (format=png, up to 512 px on the long side, 512 px when no size is given). Anything else answers 403 test_mode_limit. Test keys 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` (free PNG preview).
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-Test-Previews-LeftIn test mode: free test previews this account has left (absent while it has a subscription).
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_large413The file is over 5 MB, or the image is over 17 megapixels or 8192 px on a side. Checked from the size and 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.
test_mode_limit403Test mode returns a PNG preview up to 512 px only. SVG, JSON answers, larger PNGs and batches need a live key. Nothing was read or charged.
test_previews_used402The account has used its free test previews. A subscription makes test mode unlimited; a live key with credits returns the SVG. Nothing was read or charged.
too_many_images400More images in one batch than allowed (see Batch).
idempotency_key_reused422This Idempotency-Key was used for a different request in the last 24 hours. Nothing was charged. Send a new key with each new request.

Limits

  • Images: PNG, JPEG, WEBP, GIF or BMP, at most 5 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: scale up 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 [email protected] -F [email protected] -F [email protected] \
  -o logos.zip
unzip -l logos.zip    # logo-a.svg, logo-b.svg, logo-c.svg, manifest.json

Data handling

  • Images are processed in memory on our server in Frankfurt (Fly.io) and are not saved to permanent storage, backups or logs. A /v1/vectorize or /v1/batch image is deleted when the response is sent, and is not used for training. We keep technical totals, such as processing times and failure rates, never the image.
  • Our request log records method, path, status, duration, request id and key id: no IP addresses, no image content. Keyless calls are rate limited by IP address, in memory only.
  • Per key we keep monthly usage counts and, for 24 hours, the Idempotency-Key values you send with a SHA-256 fingerprint of each such request (a hash, not the image).
  • If you send your own customers' images, you are the controller and we are your processor. Our data processing agreement: svgproof.com/dpa. Questions: [email protected].

Examples

Snap to brand colors

curl "https://api.svgproof.com/v1/vectorize?palette=141155,03517b,ffffff,c0392b" \
  -H "Authorization: Bearer $SVGPROOF_KEY" \
  -F [email protected] -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 [email protected] -o [email protected]

JSON with palette and background

curl "https://api.svgproof.com/v1/vectorize?background=remove" \
  -H "Authorization: Bearer $SVGPROOF_KEY" \
  -H "Accept: application/json" \
  -F [email protected]