PSB to SVG API
Send a .psb to /v2/convert/psb/to/svg and the merged layers are traced into SVG paths. The sample doubles the banner with ImageWidth=768 before tracing, which kept its label readable: 152 paths, 56,184 bytes.
- Endpoint
- POST /v2/
convert/ psb/ to/ svg - Accepts
- psb
- Sample timing
- 0.129 s median of 5 runsHow we measure
- Request size
- Up to 10 MB on the Free plan or 100 MB on paid plans, all files together
Request and response
Every sample below ran against the API before this page was published. Set PDFCONVERSIONAPI_KEY to your key and run it as is.
curl -sS -X POST \
https://api.pdfconversionapi.com/v2/convert/psb/to/svg \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@ridge-loop-trail-banner.psb \
--form-string ImageWidth=768 \
-o response.json
# FileData is base64. Save the first file in converted/,
# or print the JSON error.
if jq -e '.Files' response.json > /dev/null; then
mkdir -p converted
name=$(jq -r '.Files[0].FileName' response.json)
jq -r '.Files[0].FileData' response.json \
| base64 --decode > "converted/$name"
else
cat response.json
fiimport base64
import os
import requests
key = os.environ["PDFCONVERSIONAPI_KEY"]
response = requests.post(
"https://api.pdfconversionapi.com/v2/convert/psb/to/svg",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("ridge-loop-trail-banner.psb", "rb"))],
data={
"ImageWidth": "768",
},
timeout=180,
)
result = response.json()
if not response.ok:
raise SystemExit(f"{result['Code']}: {result['Message']}")
os.makedirs("converted", exist_ok=True)
for file in result["Files"]:
path = os.path.join("converted", file["FileName"])
with open(path, "wb") as out:
out.write(base64.b64decode(file["FileData"]))
print(path, file["FileSize"], "bytes")
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
const form = new FormData();
const file = "ridge-loop-trail-banner.psb";
form.append("File", new Blob([await readFile(file)]), file);
form.append("ImageWidth", "768");
const url = "https://api.pdfconversionapi.com/v2/convert/psb/to/svg";
const key = process.env.PDFCONVERSIONAPI_KEY;
const response = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body: form,
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.Code}: ${result.Message}`);
}
await mkdir("converted", { recursive: true });
for (const file of result.Files) {
const path = join("converted", file.FileName);
await writeFile(path, Buffer.from(file.FileData, "base64"));
console.log(path, file.FileSize, "bytes");
}
{
"ConversionCost": 1,
"ConversionTime": 0.129,
"Files": [
{
"FileName": "ridge-loop-trail-banner.svg",
"FileExt": "svg",
"FileSize": 56184,
"FileData": "PD94bWwgdmVyc2lvbj0iMS4w…"
}
]
}Response from our test run on 2026-11-03. ConversionTime shows the median of 5 runs. FileData is shortened here; the real response carries the whole file as base64.
The sample, converted
The same file the code sends. Download the input and the output and compare them yourself.

- Input
- ridge-loop-trail-banner.psb (171 KB)
- Output
- ridge-loop-trail-banner.svg (55 KB)
- Source
- A layered trail-map banner we drew for this site: 384 x 128 pixels at 16 bits per channel on an opaque Background layer, with seven layers including a smooth sky gradient and a hidden old route. Written in the large document (PSB) format by our own script from the published file format specification. (CC0-1.0). How it was made
- Made by
- 2026-11-04, API build
9eeeb0640aba
Parameters
Send these as form fields next to the file. Names are case-insensitive. If you set one of the documented options below that we don't support yet, the request fails with a 400 that names it instead of returning a file made without it.
| Parameter | What it does |
|---|---|
ImageWidthinteger | Resizes before tracing. At its own 384 px the label broke up ("R DGE L OP"); at 768 it traced as whole letters. Large documents must be brought under 12,000,000 pixels this way.Values: 1–20000 |
ImageHeightinteger | Resizes by height before tracing; with a width, a box.Values: 1–20000 |
Presetenum, default none | Ready-made settings. At the banner's full 384 px: bw 6 paths, photo 12, poster 99; at the sample's 768 px: 7, 54 and 281. poster keeps more of the sky gradient as bands.Values: none, bw, poster, photo |
ColorPrecisioninteger, default 6 | Color bits per channel when grouping pixels; 8 gave the same file as poster: 99 paths at full size, 281 at 768 px.Values: 1–8 |
GradientStepinteger, default 16 | Minimum difference between stacked color layers. At full size, 4 added only 3 paths (43 against 40), and the sky kept the default's 3 fills down one column.Values: 0–128 |
FilterSpeckleinteger, default 4 | Patches smaller than this many pixels are dropped; 0 made 1,843 paths and 139,375 bytes at full size, 6,645 paths and 576,853 bytes at the sample's 768 px.Values: 0–128 |
ColorModeenum, default color | bw traces black and white only.Values: color, bw |
CurveModeenum, default spline | Curves, straight segments, or edges that follow the pixel grid.Values: spline, polygon, pixel |
CornerThresholdinteger, default 60 | Joint angle, in degrees, kept as a corner.Values: 0–180 |
SegmentLengthnumber, default 4 | Curve segment length; below 3.5 returns 400.Values: 3.5–10 |
SpliceThresholdinteger, default 45 | Angle at which splines are split.Values: 0–180 |
Layeringenum, default stacked | cutout writes shapes with holes rather than stacked shapes.Values: stacked, cutout |
FileNamestring | Base name of the SVG. |
StoreFileboolean, default false | true returns a 3-hour link. |
What carries over, and what doesn't
Kept
- Flat shapes: each ridge is one filled path within a level of its color, and the dashed trail keeps its dashes
- The label as letters once the trace has room: readable at 768 px wide, broken at the native 384
- The visible layers only; the hidden Old route is not traced
- The traced size on the root element (
width="768"andheight="256"for the sample)
Lost or not yet supported
- Gradients. Down one column of the sample, 80 sky rows that differ in the PSB became 2 fills. At full size,
Preset=posterkeeps 7 bands in the middle of the sky where the default keeps 1. - Accuracy: rendered back at 384 px, the sample differs from the PNG by 8.1 levels on average, 96% of it outside the two ridges (sky, label and trail).
- 16-bit color, layers, and any
viewBoxon the root element.
Limits
- One PSB per request.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
- At most 12,000,000 pixels are traced; larger results return 413 with a hint to set
ImageWidth. - The flattened document itself must stay under 100,000,000 pixels, checked before scaling.
- 120 seconds per conversion, then 504.
- Type layers become pixels before tracing; smart objects and layer effects are untested.
Where teams use it
Maps and diagrams drawn as large documents
Trail maps, floor plans and diagrams painted with flat fills in a big PSB can become vector paths for a web page or a cutting machine. Scale to a working size with ImageWidth first.
A vector starting point for redrawing
When the original vector source is lost, a trace of the PSB gives an illustrator editable shapes to clean up rather than a blank canvas.
Questions
Why does my traced SVG lose the gradient?
The tracer groups similar colors into flat shapes, so a smooth sky becomes a few fills. At the banner's full 384 px, Preset=poster (8 bits of color precision) keeps more bands: 7 instead of 1 in the middle of the sky, at 99 paths instead of 40. At the sample's 768 px it gave 4 fills instead of 2 down one column of the sky, at 281 paths instead of 152.
How do I trace a PSB that is larger than 12 megapixels?
Send ImageWidth or ImageHeight so the scaled image is under 12,000,000 pixels; the API names this in its 413 message. The flattened document must still be under 100,000,000 pixels before scaling.
Do options like ImageResolution cause an error here?
No. ConvertAPI documents no options for PSB to SVG, so options this route does not read are ignored: ImageResolution=300, ColorSpace=gray and ImageQuality=50 each returned the default file. On the PSD to SVG route the same values are refused with 400.
Convert your first PSB file in a minute.