La documentazione dell'API è mantenuta in inglese, così nomi dei parametri, codici di errore ed esempi di codice corrispondono esattamente all'API.
APIOne POST. An image in, a vector back.
The JustVectorize API is a single multipart request. Send an image, get back a URL to the finished SVG, EPS, PDF, AI, DXF or PNG. Every tracing knob the web app exposes is a form field here, and all of them are optional.
https://api.justvectorize.comOverview
There is no SDK to install and no job queue to poll. You POST a file to /api/v1/vectorize with your API key in a header, the request blocks while the image is traced, and the response hands back a URL to the finished file, which you then fetch — two requests, no polling in between. A typical trace takes a few seconds; a large, detailed image traced with draw_style=fill_gradients can take considerably longer, and remove_bg=true adds roughly 1-2 seconds.
- Input — PNG, JPG or WEBP, sent as
multipart/form-data. - Output — JSON holding a
fileUrl, a direct download link to the traced file in storage. - Billing — one credit per production image, and test requests are free. Failed requests are not charged. See Pricing.
Authentication
Every request carries an API key as a bearer token in the Authorization header. Keys are created and revoked on your account page — sign in, open the API keys panel, and create one. A key is shown in full whenever you reveal it, so you can copy it again later, and revoking one takes effect immediately.
Authorization: Bearer jv_live_xxxxxxxxxxxxxxxxxxxxxxxxAn API key spends your credits. Call the API from your own backend and keep the key in an environment variable — never in browser JavaScript, a mobile binary, or a committed config file. If a key leaks, revoke it on the account page and create a new one; nothing else needs to change.
A missing or revoked key returns 401. An account with no credits left returns 402 — see Errors.
Pricing
What a request costs is set by its image_mode field. Leave it out and you get a production result.
| Mode | Credits | Description |
|---|---|---|
test | 0 | Integrating and testing are free, with no subscription required. Send image_mode=test while you build. The trace comes back in the format you asked for, with a watermark across it. To judge result quality without the lettering, try the same image in the web app. |
preview | 0.2 | A preview you can show your end users before they buy. Send image_mode=preview to get the trace as a PNG with an X drawn corner to corner. It is always a PNG, so format is ignored. |
production | 1 | The default. Traces the image and returns the finished file, unmarked. |
A request that fails costs nothing, whatever its mode. For how each mode marks the image, see image_mode in the parameter reference.
Quickstart
The smallest useful request is a file and nothing else; every other field falls back to a default chosen to look right on a typical image. This traces logo.png and writes logo.svg:
# The response is JSON — {"fileUrl": "https://…"} — so trace, then fetch.
url=$(curl -s -X POST https://api.justvectorize.com/api/v1/vectorize \
-H "Authorization: Bearer $JUSTVECTORIZE_API_KEY" \
-F "file=@logo.png" \
-F "format=svg" | jq -r .fileUrl)
curl -o logo.svg "$url"Adding parameters is the same request with more form fields. The call below asks for a four-color DXF sized for a cutter:
# A four-color logo, cut as a DXF in millimetres, 120 mm wide, with
# every edge between shapes stroked exactly once. Straight edges and
# circular arcs are refitted as real primitives on every trace.
curl -X POST https://api.justvectorize.com/api/v1/vectorize \
-H "Authorization: Bearer $JUSTVECTORIZE_API_KEY" \
-F "file=@logo.png" \
-F "format=dxf" \
-F "n_colors=4" \
-F "draw_style=stroke_edges" \
-F "width=120" \
-F "unit=mm" \
-F "dxf_units=mm" | jq -r .fileUrlEndpoints
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/vectorize | Traces one image and returns a URL to the finished file. The only endpoint most integrations need. |
One request traces one image. There is no batch endpoint — send them concurrently, a few at a time, and back off on 502.
The request
POST https://api.justvectorize.com/api/v1/vectorize with a multipart/form-data body. Do not set Content-Type yourself — your HTTP client writes it, with the multipart boundary, when you hand it a form body.
| Part | Required | Notes |
|---|---|---|
file | Yes | The image, as a file part. PNG, JPG or WEBP, up to 20 MB. Anything over max_pixels in area, or over 2048 px on its longest side, is downsampled before tracing. |
format | No | svg, eps, pdf, ai, dxf or png. Defaults to svg. |
| Everything else | No | Flat string form fields, one per knob — see the parameter reference. Booleans go as true / false, and palette as a JSON string. |
An omitted field means “use the default”, so you only ever send the knobs you actually care about. Values are validated server-side; an out-of-range one fails the whole request with 422 rather than being silently clamped, and so does a field name the API does not recognise.
Some combinations are accepted but have nothing to act on: stroke_color under a fill style, the svg_* fields with format=png, shape_stacking and group_by under stroke_edges, or scale sent together with width or height (scale wins). The trace still runs; the field is simply not used.
The response
On success the status is 200 and the body is JSON carrying a single field, fileUrl — a direct link to the traced file. Fetch it with a plain GET; that request needs no API key and costs no credit.
{
"fileUrl": "https://storage.googleapis.com/creative-ais/Pe887UovP6-FrWIpJvi8S.svg+xml"
}The stored file is served with the Content-Type for the format you asked for:
| format | Content-Type |
|---|---|
svg | image/svg+xml |
eps | application/postscript |
pdf | application/pdf |
ai | application/illustrator |
dxf | image/vnd.dxf |
png | image/png |
The URL is a temporary download link, not permanent hosting — fetch the file and keep your own copy rather than pointing your app or your users at it. Its filename comes from the content type, not from your upload, so name the file yourself when you save it.
A failure answers with JSON too — { "error": "…" } instead of a fileUrl — so branch on the status code before you read either field.
Custom palettes
Left alone, the tracer picks the colors itself. The palette field takes that decision away from it: pass a JSON string holding a list whose entries are either plain color strings, or objects that also say how far each entry reaches and what its matches become.
| Key | Required | Notes |
|---|---|---|
color | Yes | The color to match, in CSS syntax: #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl(), a color name, or transparent. A plain string entry is shorthand for an object with only color. |
remapped | No | What the matched colors are painted as in the output. Omit it to paint them as color. |
tolerance | No | How far a detected color may sit from color and still match: the Euclidean distance over ARGB, divided by 255, from 0 to 2.0. Defaults to 2.0, which matches every color. |
# Straighten near-misses of two brand colors (within a tolerance of 0.1)
# and paint the near-black as true black. Every other color keeps what the
# tracer found; drop both tolerances to snap everything onto the two.
curl -X POST https://api.justvectorize.com/api/v1/vectorize \
-H "Authorization: Bearer $JUSTVECTORIZE_API_KEY" \
-F "file=@logo.png" \
-F 'palette=[{"color":"#1a1a1a","remapped":"#000000","tolerance":0.1},{"color":"#ff5c00","tolerance":0.1}]' \
| jq -r .fileUrlEach detected color snaps to the nearest entry whose tolerance reaches it, and a tie goes to the entry listed first. Because the default tolerance matches everything, a list of plain colors is a hard snap: the output carries no color you did not list, which makes palette the reliable way to hold a brand palette across a whole batch. Small tolerances only clean up near-matches and leave every other color alone. Two exceptions to the hard snap: draw_style=fill_gradients brings in ramp colors from outside the palette, and an empty list is ignored.
Parameter reference
Every field below is optional and is sent as a string form field. The defaults are what you get when the field is absent; the order of the groups is the order the pipeline runs in, from the pixels going in to the file coming out.
Output
The same trace underneath in every case — these decide how it is drawn and written out.
| Param | Default | Values | Notes |
|---|---|---|---|
image_mode | production | production · preview · test | production: the trace, unmarked. preview: the trace rendered to PNG with an X drawn corner to corner over it — always PNG, so format is ignored. test: justvectorize.com is painted in diagonal rows over the whole image before it is traced ("just" in Geist Regular, "vectorize.com" in Geist SemiBold), so the lettering is part of the vector. It is painted after upscaling, at the resolution being traced. See Pricing for what each mode costs. |
format | svg | svg · eps · pdf · ai · dxf · png | SVG, EPS and PDF carry the same path data. ai is that PDF saved for Adobe Illustrator, which has stored .ai files as PDF since version 9 and opens it as an editable document. DXF renders each region as a HATCH. PNG is the SVG rendered to pixels. |
draw_style | fill_shapes | fill_shapes · fill_gradients · stroke_shapes · stroke_edges | Fill each shape with a flat color; fill it with a linear or radial gradient wherever one fits better than a flat color (see Gradients); stroke each shape's own outline, so shared edges are drawn twice; or stroke each edge between shapes exactly once — the one for pen plotters, laser cutters and engravers. Strokes are always flat colors. |
stroke_width | 1.0 | > 0 | In traced pixels, so it scales with the output size. Only read by the two stroke styles. |
stroke_color | black | original · black · white · #rgb · #rrggbb · r,g,b | original gives each shape its own color; under stroke_edges an edge takes the mean of the two shapes either side of it. Ignored under fill_shapes and fill_gradients. |
centerline_width | 6.0 | ≥ 0 | Stroke styles only. A shape no wider than this, in traced pixels, is drawn as one line down its middle instead of the two edges either side of it, and its neighbours leave out the edges it stands for. Width is measured between the subpixel-refined edges, so anti-aliasing does not skew it. 0 turns it off. |
shape_stacking | cutouts | cutouts · stacked | cutouts sets each shape into a hole cut in the shapes below it; stacked draws smaller shapes on top of larger ones and omits fully covered holes, for a smaller file. Same picture either way. Not used by stroke_edges. |
group_by | none | none · color · parent · layer | Collects shapes into groups: SVG uses <g>, DXF uses layers. EPS, PDF and AI have no groups, so only the draw order changes. Not used by stroke_edges. |
SVG options
Only read when format is svg. They have no effect on png, even though PNG is rendered from the SVG.
| Param | Default | Values | Notes |
|---|---|---|---|
svg_version | svg_1_1 | svg_1_0 · svg_1_1 · svg_tiny_1_2 | The version declared on the <svg> tag. The body is valid under all three. |
svg_fixed_size | auto | true · false | Writes width and height onto the <svg> tag. Left unset, they are written only when scale, width, height or unit is set; otherwise the tag carries just a viewBox. |
svg_adobe_compatibility | false | true · false | Forces SVG 1.1 and replaces elliptical-arc (A) path commands with Béziers, which Adobe importers need. <circle> and <ellipse> elements are kept. |
DXF options
Only read when format is dxf.
| Param | Default | Values | Notes |
|---|---|---|---|
dxf_units | mm | mm · cm · in · pt | The drawing unit, declared in $INSUNITS. |
dxf_flatness | 0.05 | > 0 | How far, in traced pixels, a flattened curve may sit from the curve it replaces. |
dxf_compatibility | lines_and_arcs | lines_only · lines_and_arcs · lines_arcs_and_splines | Which primitives a HATCH boundary may use. Only lines_arcs_and_splines reproduces a Bézier exactly; the others approximate it. |
Output size
Everything upstream works in traced pixels. These place that drawing in a document of a given size. One traced pixel is one point (1/72 in), so none, px and pt are the same size. Upscaling does not change the output size — a 4× trace still declares the input's size, just with more detail — so to get more PNG pixels, use scale.
| Param | Default | Values | Notes |
|---|---|---|---|
scale | — | > 0 | Uniform multiplier on the traced size. Takes precedence over width and height. |
width | — | > 0 | Page width, in unit. Sent without height, the height follows the aspect ratio. |
height | — | > 0 | Page height, in unit. Sent without width, the width follows the aspect ratio. |
unit | none | none · px · pt · in · cm · mm | The unit width and height are given in. |
aspect_ratio | preserve_inset | preserve_inset · preserve_overflow · stretch | Only used when both width and height are set: preserve_inset fits the whole drawing, preserve_overflow covers the page and crops, stretch distorts to fill. |
align_x | center | left · center · right | Horizontal position of the drawing in the leftover — or overflowing — space. |
align_y | center | top · center · bottom | The same, vertically. |
Input
| Param | Default | Values | Notes |
|---|---|---|---|
max_pixels | 2097252 | ≥ 100 | Largest input area, as width × height; bigger images are downsampled once, before anything else reads them. A fixed 2048 px cap on the longest side also applies and is not configurable. |
alpha_threshold | 128 | 0 – 255 | Alpha at or above this is opaque, below it transparent — alpha is never traced as a gradient. Transparent pixels are left out of the palette and produce no path. |
Background removal
Cuts the background out before tracing, so only the subject gets a palette color and a path. Runs after upscaling and before quantization.
| Param | Default | Values | Notes |
|---|---|---|---|
remove_bg | false | true · false | Makes the background transparent so it is left out of the trace entirely; pixels already transparent in your upload stay transparent. |
Palette and quantization
The image is reduced to a set of flat colors, by k-means in Lab space, before anything is traced. This is where you decide which colors survive.
| Param | Default | Values | Notes |
|---|---|---|---|
n_colors | auto | ≥ 2 | Number of k-means colors. Omit it and the count is chosen automatically, balancing color error against path count, steered by the auto_* fields below. |
max_colors | 0 | ≥ 0 | Ceiling on the number of colors; 0 means unlimited, and 1 behaves like 2. Caps n_colors or the top of the automatic search. |
auto_bias | 0.5 | 0 – 1 | Which way the automatic search leans: lower gives a simpler result, higher a more accurate one. Small changes matter — above about 0.7, photos go straight to auto_max_colors. |
auto_min_colors | 2 | ≥ 2 | Lower bound on the automatic search. Raised to the number of palette entries when palette is set. |
auto_max_colors | 64 | ≥ 2 | Upper bound on the automatic search. |
spread | 0.5 | 0 – 1 | 0 minimises total color error, so the colors cluster on the dominant hue; 1 spreads them evenly across the colors present. |
palette | — | JSON | Your own colors: each detected color snaps to the nearest entry within tolerance. A list of colors, or of {color, remapped, tolerance} objects — see Custom palettes above. |
Tracing
Turning the quantized regions into curves. Each boundary between two regions is fitted once and shared by both, so neighbouring shapes meet without seams.
| Param | Default | Values | Notes |
|---|---|---|---|
turdsize | 8 | ≥ 0 | Minimum region area, in pixels. With region unification on (min_area above 0) it acts as a floor under min_area rather than a separate pass. |
alphamax | 1.0 | 0 – 4/3 | Corner threshold. 0 makes every vertex a corner (polygons); 4/3 or more gives no corners at all. |
opticurve | true | true · false | Joins adjacent curve segments to cut down their number. |
opttolerance | 0.4 | float | How far, in pixels, a joined curve may deviate from the segments it replaces. |
subpixel | true | true · false | Moves boundary vertices off pixel corners and onto the actual color edge before fitting; junctions stay put. The single biggest lever on curve quality. |
sub_max | 0.75 | float | Furthest a vertex may move in that refinement, in pixels. |
Region unification and smoothing
k-means cuts palette boundaries straight through gradients. This absorbs any region smaller than min_area into its closest-in-color neighbour, gated by a contrast test so real detail survives, then optionally majority-filters the result.
| Param | Default | Values | Notes |
|---|---|---|---|
min_area | 1 | ≥ 0 | A region smaller than this, in pixels, merges into its closest-colored neighbour if that neighbour is within the contrast guard. 0 turns unification off. The tracer's own default is 64; the API sends 1 unless you set it, which keeps small detail. |
keep_de | 12.0 | float | Contrast guard at min_area, in CIE76 ΔE — about 2.3 is a just-noticeable difference. Lower it for flatter, poster-like output; raise it to keep more detail. |
keep_gamma | 0.5 | float | How fast the guard rises for smaller regions: guard = keep_de × (min_area / area) ^ keep_gamma. 0 keeps it flat. |
keep_de_max | 60.0 | float | Ceiling on that rising guard — the contrast a single pixel needs to survive on its own. |
merge_de | 0.0 | float | Also merges neighbours this close in ΔE regardless of size. Off by default; keep it small, because merges chain across gradients. |
merge_rounds | 8 | ≥ 0 | Merge-and-recheck passes. |
smooth_radius | 0.0 | ≥ 0 | Sigma, in pixels, of a Gaussian majority filter. Rounds off edges and detail narrower than about this width; 2–4 is a strong effect. 0 is off. |
smooth_iters | 1 | ≥ 0 | How many times that filter runs. |
smooth_de | 20.0 | ≥ 0 | A pixel may only change to a color within this ΔE, so high-contrast edges keep their shape. 0 removes the limit. |
Gradients
Turned on by draw_style=fill_gradients, which fills a region with a linear or radial gradient wherever one fits better than a flat color; every field here is ignored under the other draw styles. SVG, PDF, AI and EPS draw the gradients; DXF falls back to each region's mean color, one layer per region. Gradient ramps bring in colors from outside max_colors or a palette.
| Param | Default | Values | Notes |
|---|---|---|---|
grad_de | 16.0 | float | Largest RMS ΔE between a merged group and the single ramp fitted through it — this is what controls band merging. Around 3 barely merges, 6 fixes banding, 16 collapses a photo into a few dozen ramps. |
grad_min_px | 24 | ≥ 1 | Smallest merged area, in pixels, worth testing for a ramp. |
grad_rounds | 8 | ≥ 0 | Band-merge rounds. n bands collapse in about log₂ n rounds. |
grad_stops | 6 | ≥ 2 | Most stops per gradient. Redundant ones are dropped, so this is a ceiling rather than a count. |
grad_min_de | 2.5 | float | Regions flatter than this, in RMS ΔE, stay flat-filled. |
grad_improve | 0.25 | float | Fraction of the squared color error a gradient has to remove before it is used. |
grad_radial | true | true · false | false allows linear gradients only, and only planar ramps merge. |
Errors
Failures come back as JSON with an error string. The status code is what you should branch on:
| Status | What happened | What to do |
|---|---|---|
400 | No file in the form data, an empty or unreadable file, malformed palette JSON, or a combination of parameters the tracer rejects. | Send multipart/form-data with a part named file. Do not JSON-encode the image, and send palette as a JSON list. |
401 | The Authorization header is missing or not a bearer token, or the key is invalid or revoked. | Copy the key again from your account page. A revoked key never comes back — create a new one. |
402 | The account is out of API credits. | Top up, or turn on automatic renewal next to your key so a low balance renews itself. |
413 | The file is over 20 MB. | Send a smaller file. Images are downsampled to 2048 px on the longest side before tracing anyway. |
422 | A form field fails validation — wrong type, out of range, or an unknown field name. | Check the value and the spelling against the parameter reference below. Every field is optional; omit it to take the default. |
500 | Anything else went wrong during the trace. | Retry; if it persists, contact support. |
502 | The service could not be reached. | Transient. Retry with backoff. |
const res = await fetch("https://api.justvectorize.com/api/v1/vectorize", { method: "POST", headers, body });
if (!res.ok) {
const { error } = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(`${res.status}: ${error}`);
}
const { fileUrl } = await res.json();Limits and billing
- One credit per production image. A preview costs 0.2 credits, a test costs nothing, and so does a request that fails — see Pricing.
- Credits roll over up to 5× your monthly allowance, so a plan sized around an average month absorbs a busy one.
- Integration is free. Build and test against the API with
image_mode=testand no subscription — credits are what you pay for when you start shipping production results. - Input is downsampled to 2048 px on its longest side and to
max_pixelsin area, so an enormous source image costs the same as a sensible one and traces no slower.
Plans and prices are on the pricing page. Anything the docs do not answer, ask us on the contact page or at contact@justvectorize.com.
Trace your first image in a minute.
Create an API keyFree to integrate and test · no card required