vectorize API v0.4.7

Raster → vector conversion over HTTP. Every request is stateless; nothing is stored.

Endpoints

MethodPathDescription
POST/api/v1/traceConvert an image. Body: multipart/form-data with field image (and optional options JSON), or raw image bytes with an image/* content type, or JSON {"image": "<base64>", "options": {…}}. Query: format=svg|eps|pdf|dxf|json, dpi, unit=px|mm|cm|in|pt, width, plus any option below.
GET/api/v1/optionsPresets, option metadata, output formats, and input — the image formats this build decodes, with their media types and extensions.
GET/api/v1/usageToday's usage and remaining quota of the calling key.
GET/healthzLiveness, queue depth, workers.
GET/metricsPrometheus metrics, behind the same API key.

Authentication: required — this API needs an account. Sign in or create one, make an API key for Vectorize there, and send it as X-Api-Key: ak_… (or an access token as Authorization: Bearer). Every account starts with a free allowance of traces; GET /api/v1/usage says what is left, and so do the X-Usage-Plan, X-Usage-Used, X-Usage-Limit and X-Usage-Remaining headers on each answer. A used-up plan answers 402 with upgradeUrl.

Response headers: X-Vectorize-Paths, X-Vectorize-Colors, X-Vectorize-Ms, X-Vectorize-Upscale. Errors are JSON {"error": "…"} with 400 (bad option), 401, 402 (plan used up), 413 (too large), 415 (format not decodable by this build), 422 (undecodable), 429, 503 (busy).

Accepted image formats: PNG, JPEG, WebP, GIF, BMP. Anything else answers 415 naming the format it was — HEIF/HEIC in particular, which is what an iPhone camera writes by default and which the API has no decoder for. The web app converts those because a browser decodes them with the operating system's own decoder and hands over pixels; the API only has what is compiled into it. Convert to JPEG or PNG first.

Limits on this instance: uploads up to 20 MB, images up to 10 megapixels, both answered with 413. The ceiling is on the source image: when a preset's upscale would not fit under it the factor is reduced rather than the request refused, and X-Vectorize-Upscale reports what was applied.

Examples

curl -X POST 'https://vectorize-api.dudko.dev/api/v1/trace?preset=photo&maxColors=16&format=svg' \
     -H 'X-Api-Key: KEY' -F [email protected] -o photo.svg

curl -X POST 'https://vectorize-api.dudko.dev/api/v1/trace?format=pdf&dpi=300' \
     -H 'X-Api-Key: KEY' -H 'Content-Type: image/png' --data-binary @logo.png -o logo.pdf

curl -X POST 'https://vectorize-api.dudko.dev/api/v1/trace?format=json&preset=poster&text=trace' \
     -H 'X-Api-Key: KEY' -F [email protected] -F 'options={"maxColors":8,"transparentBackground":true}'

Try it

result appears here

For an assistant

An MCP endpoint at https://vectorize-api.dudko.dev/mcp is the front door to this API for an assistant: api_access says how the API works, trace_options lists the presets and options, and api_request checks the options and returns the exact request with a short-lived credential for the signed-in account. No picture passes through it. Point an MCP client at that address; it finds the rest — which authorization server issues the tokens, and what scope to ask for — at https://vectorize-api.dudko.dev/.well-known/oauth-protected-resource/mcp. A token is accepted only if it was issued for this address and no other.

Options

All optional; unset options fall back to the preset. Enumerations: preset bw|poster|photo, mode color|binary, layering shared|stacked|cutout, curveMode spline|polygon|pixel, strokeMode fill|centerline, text off|trace|raster; booleans transparentBackground, adaptiveThreshold, gradients, salientColors; palette is a comma-separated list of #rrggbb.

OptionRangeMeaning
superpixels0–32 pxGroups similar neighbouring pixels before colouring. Smooths photo noise and gradient banding; too large blurs small details.
smooth0–10Edge-preserving smoothing of the source. Flattens sensor noise and JPEG blocks while keeping real edges.
denoise0–5 passesMedian filter passes. Removes isolated specks and JPEG "mosquito" noise around edges.
cleanEdges0–4 passesRemoves one-pixel halos between areas left by anti-aliasing. Turn off if thin lines disappear.
gapFill0–2 pxStrokes each shape in its own colour to hide hairline seams some renderers show between neighbours.
upscale1–4 ×Enlarges the source before tracing. Smoother curves on small, anti-aliased images; slower on big ones. Reduced automatically when the enlarged image would not fit the pixel ceiling, rather than the image being refused.
maxColors0–64Number of colours in the palette. Fewer colours = flatter, smaller file; more = closer to the original.
filterSpeckle0–128 px²Ignores patches smaller than this many pixels (side length). Higher = cleaner but drops small details. In black & white mode, dots and punctuation that sit on a text line are kept even when they are smaller.
colorPrecision1–8 bitsColour bits kept when clustering without a palette. Lower merges similar colours.
layerDifference0–128Minimum colour distance between layers when clustering without a palette.
cornerThreshold0–180 °Angle above which a bend becomes a sharp corner instead of a smooth curve.
lengthThreshold3.5–10 pxMinimum segment length when fitting splines. Larger = fewer, longer curves.
spliceThreshold0–180 °Angle below which two segments are joined into one curve.
pathPrecision0–4 decimalsDecimal places written for coordinates. Fewer = smaller file, slightly less exact.
threshold0–255Luminance cut-off for black & white. Lower keeps only dark pixels; higher keeps more.
paletteMerge0–20 ΔEMerges palette colours closer than this ΔE so a 5-colour image does not get 24 near-duplicates.
strokeWidth0–20 pxFixed width for centre-line strokes; "measured" uses each stroke's own thickness.
gradientTolerance1–20How far a run of colour bands may deviate from a straight colour ramp and still become one gradient.

Presets

PresetEffective options
bw{"mode":"binary","layering":"stacked","curveMode":"spline","denoise":0,"smooth":0,"upscale":2,"maxColors":0,"cleanEdges":0,"gapFill":0,"transparentBackground":false,"superpixels":0,"adaptiveThreshold":true,"paletteMerge":6,"strokeMode":"fill","strokeWidth":0,"gradients":false,"gradientTolerance":6,"salientColors":false,"text":"off","filterSpeckle":4,"colorPrecision":6,"layerDifference":16,"cornerThreshold":60,"lengthThreshold":4,"spliceThreshold":45,"maxIterations":10,"pathPrecision":2,"threshold":128}
poster{"mode":"color","layering":"shared","curveMode":"spline","denoise":0,"smooth":0,"upscale":1,"maxColors":16,"cleanEdges":1,"gapFill":0.5,"transparentBackground":false,"superpixels":0,"adaptiveThreshold":false,"paletteMerge":6,"strokeMode":"fill","strokeWidth":0,"gradients":true,"gradientTolerance":6,"salientColors":true,"text":"trace","filterSpeckle":4,"colorPrecision":8,"layerDifference":16,"cornerThreshold":60,"lengthThreshold":4,"spliceThreshold":45,"maxIterations":10,"pathPrecision":2,"threshold":128}
photo{"mode":"color","layering":"shared","curveMode":"spline","denoise":1,"smooth":3,"upscale":1,"maxColors":24,"cleanEdges":1,"gapFill":0.5,"transparentBackground":false,"superpixels":8,"adaptiveThreshold":false,"paletteMerge":4,"strokeMode":"fill","strokeWidth":0,"gradients":true,"gradientTolerance":8,"salientColors":true,"text":"off","filterSpeckle":8,"colorPrecision":8,"layerDifference":48,"cornerThreshold":180,"lengthThreshold":4,"spliceThreshold":45,"maxIterations":10,"pathPrecision":2,"threshold":128}