Vectorize
Chuyển đổi hình ảnh raster thành SVG, EPS, PDF hoặc DXF, với các cài đặt sẵn và kiểm soát từng tùy chọn. Không lưu trạng thái.
Tài liệu
vectorize API v0.4.8
Raster → vector conversion over HTTP. Every request is stateless; nothing is stored.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/trace | Convert 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/options | Presets, option metadata, output formats, and input — the image formats this build decodes, with their media types and extensions. |
| GET | /api/v1/usage | Today's usage and remaining quota of the calling key. |
| GET | /healthz | Liveness, queue depth, workers. |
| GET | /metrics | Prometheus 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, with X-Usage-Reset on a monthly plan for the moment its counter starts over. A used-up plan answers 402 with upgradeUrl.
Response headers: X-Vectorize-Paths, X-Vectorize-Colors, X-Vectorize-Ms, X-Vectorize-Upscale, X-Vectorize-Downscale. 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, answered with 413, and tracing at up to 10 megapixels. A larger picture is not refused: it is scaled down to that ceiling, traced there, and drawn at its full size — a phone photo or a 600 dpi scan carries no detail a tracer could use past that point — and X-Vectorize-Downscale says by how much. A preset's upscale that would not fit is reduced the same way, reported in X-Vectorize-Upscale. Only a picture more than twelve times the ceiling is refused with 413, before it is decoded.
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
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.
| Option | Range | Meaning |
|---|---|---|
superpixels | 0–32 px | Groups similar neighbouring pixels before colouring. Smooths photo noise and gradient banding; too large blurs small details. |
smooth | 0–10 | Edge-preserving smoothing of the source. Flattens sensor noise and JPEG blocks while keeping real edges. |
denoise | 0–5 passes | Median filter passes. Removes isolated specks and JPEG "mosquito" noise around edges. |
cleanEdges | 0–4 passes | Removes one-pixel halos between areas left by anti-aliasing. Turn off if thin lines disappear. |
gapFill | 0–2 px | Strokes each shape in its own colour to hide hairline seams some renderers show between neighbours. |
upscale | 1–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. |
maxColors | 0–64 | Number of colours in the palette. Fewer colours = flatter, smaller file; more = closer to the original. |
filterSpeckle | 0–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. |
colorPrecision | 1–8 bits | Colour bits kept when clustering without a palette. Lower merges similar colours. |
layerDifference | 0–128 | Minimum colour distance between layers when clustering without a palette. |
cornerThreshold | 0–180 ° | Angle above which a bend becomes a sharp corner instead of a smooth curve. |
lengthThreshold | 3.5–10 px | Minimum segment length when fitting splines. Larger = fewer, longer curves. |
spliceThreshold | 0–180 ° | Angle below which two segments are joined into one curve. |
pathPrecision | 0–4 decimals | Decimal places written for coordinates. Fewer = smaller file, slightly less exact. |
threshold | 0–255 | Luminance cut-off for black & white. Lower keeps only dark pixels; higher keeps more. |
paletteMerge | 0–20 ΔE | Merges palette colours closer than this ΔE so a 5-colour image does not get 24 near-duplicates. |
strokeWidth | 0–20 px | Fixed width for centre-line strokes; "measured" uses each stroke's own thickness. |
gradientTolerance | 1–20 | How far a run of colour bands may deviate from a straight colour ramp and still become one gradient. |
Presets
| Preset | Effective 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} |