KRA to SVG API
POST a .kra to /v2/convert/kra/to/svg and our converter first merges the visible layers, then traces the result into filled SVG paths. Fully transparent canvas gets no paths, so the outline of a sticker comes out as a cutout shape.
- Endpoint
- POST /v2/
convert/ kra/ to/ svg - Accepts
- kra
- Sample timing
- 32 ms 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/kra/to/svg \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@rocket-sticker.kra \
-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/kra/to/svg",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("rocket-sticker.kra", "rb"))],
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 = "rocket-sticker.kra";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/kra/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.032,
"Files": [
{
"FileName": "rocket-sticker.svg",
"FileExt": "svg",
"FileSize": 34664,
"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
- rocket-sticker.kra (108 KB)
- Output
- rocket-sticker.svg (34 KB)
- Source
- A cartoon rocket sticker on a transparent canvas that we drew and saved as a six-layer KRA file (one layer hidden, one faded by a transparency mask). (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 | Scales the merged image before tracing. More pixels give smoother outlines and bigger files: 800 px traced into 374 paths (264,062 bytes); the square sticker can go up to 3,464 px.Values: 1–20000, at most 12,000,000 pixels after scaling |
ImageHeightinteger | Scales by height, or sets a box together with ImageWidth.Values: 1–20000 |
ScaleProportionsboolean, default true | false traces a stretched copy filling the exact box. |
ScaleIfLargerboolean, default false | true stops an enlargement before tracing. |
ScaleImageboolean, default true | false traces only a top-left crop of the given size. |
ColorModeenum, default color | bw traces a single-color silhouette: 2 paths and 3,627 bytes for the rocket, a usable cut line.Values: color, bw |
Presetenum, default none | Starting settings. poster raised the sticker to 226 paths; photo gave 197.Values: none, bw, poster, photo |
FilterSpeckleinteger, default 4 | Drops patches whose area is below this value squared, so the default 4 removes patches under 16 pixels. 0 exploded the sticker to 2,213 paths and 165,480 bytes; 16 gave 196 paths.Values: 0–128 |
ColorPrecisioninteger, default 6 | Significant bits per color channel; 3 merged similar shades into 193 paths.Values: 1–8 |
GradientStepinteger, default 16 | How different two layers of color must be to stay separate; 40 gave 201 paths. ConvertAPI documents 10 for PNG to SVG.Values: 0–128 |
Layeringenum, default stacked | cutout makes shapes that don't overlap (202 paths, 26,314 bytes).Values: stacked, cutout |
CurveModeenum, default spline | polygon writes straight segments (19,925 bytes); pixel follows pixel edges exactly (54,836 bytes).Values: spline, polygon, pixel |
CornerThresholdinteger, default 60 | Angle below which a turn is kept as a corner. ConvertAPI documents 55 for PNG to SVG.Values: 0–180 (degrees) |
SegmentLengthnumber, default 4 | Target segment length for curve fitting; 10 shortened the file to 27,013 bytes.Values: 3.5–10 |
SpliceThresholdinteger, default 45 | Angle at which a spline is split. ConvertAPI documents 35 for PNG to SVG.Values: 0–180 (degrees) |
FileNamestring | Name of the .svg returned. |
StoreFileboolean, default false | Stores the SVG for 3 hours behind a link. |
What carries over, and what doesn't
Kept
- The shapes and flat colors of the merged layers: 213 paths in 107 fill colors for the sample
- The transparent canvas, which gets no paths, so the sticker's outline is the edge of the drawing
- The canvas size as the SVG's width and height (320 × 320)
Lost or not yet supported
- Partial transparency. Pixels that are not fully clear are traced as solid color: the shadow, painted at 35% opacity, became a solid dark navy band.
- Gradients and soft shading, which become steps of flat color.
- Krita's own vector layers and text. They are not drawn at all, so they can't be traced either.
- A viewBox. The root element carries only width and height, so scaling the SVG in a page needs one added.
- Any layer structure: paths follow regions of color, not Krita layers.
Limits
- One .kra per request; canvas up to 100,000,000 pixels, sides up to 300,000 px.
- Tracing is limited to 12,000,000 pixels after resizing: 3,464 px wide worked for the sticker, 3,465 returned 413.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
- 120 seconds per conversion, then 504.
Where teams use it
Cut lines for stickers
A cutting plotter needs a vector outline of the sticker. ColorMode=bw on art painted in Krita gives a silhouette of 2 paths that follows the edge of the transparent canvas.
Vector versions of flat-color art
Logos, badges and cartoon art with large flat areas trace into compact paths: the 320 px rocket became 213 paths in 34,664 bytes.
Questions
Does this keep Krita's vector layers as vectors?
No. The route traces pixels. Krita vector layers, including text, are not rendered at all yet, so they are missing from the trace. Only paint layers, groups and masks feed the merged image.
Why is the soft shadow a hard dark shape in the SVG?
Tracing looks at color, and treats only fully transparent pixels as empty. The shadow's pixels have an alpha of 89 out of 255, so they are traced with their stored dark navy at full strength. Remove or merge soft layers in Krita before converting if that matters.
How do I get a cleaner trace?
Raise ImageWidth so outlines have more pixels to follow, and raise FilterSpeckle to drop tiny specks. On the rocket, 800 px gave smoother curves at 264,062 bytes, and FilterSpeckle=16 removed 17 small paths.
Convert your first KRA file in a minute.