HDR to SVG API
POST a .hdr to /v2/convert/hdr/to/svg and get a traced SVG. The pixels are clipped at 1.0, cut to 8 bits and grouped into color regions; the sample uses Preset=photo, which turned the sunset panorama into 33 paths and 49,649 bytes.
- Endpoint
- POST /v2/
convert/ hdr/ to/ svg - Accepts
- hdr
- Sample timing
- 82 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/hdr/to/svg \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@lakeside-sunset-sky.hdr \
--form-string Preset=photo \
-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/hdr/to/svg",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("lakeside-sunset-sky.hdr", "rb"))],
data={
"Preset": "photo",
},
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 = "lakeside-sunset-sky.hdr";
form.append("File", new Blob([await readFile(file)]), file);
form.append("Preset", "photo");
const url = "https://api.pdfconversionapi.com/v2/convert/hdr/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.082,
"Files": [
{
"FileName": "lakeside-sunset-sky.svg",
"FileExt": "svg",
"FileSize": 49649,
"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
- lakeside-sunset-sky.hdr (90 KB)
- Output
- lakeside-sunset-sky.svg (48 KB)
- Source
- A sunset sky panorama we drew in code and saved as a 400 × 200 Radiance RGBE file with run-length scanlines; the sun disc is 60.0. (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 |
|---|---|
Presetenum, default none | A set of tracing settings. photo keeps the sky in broad bands (33 paths); poster gave 186 paths; bw 4; with none most of the sky became one flat color. The figures for the other options below were measured without a preset, not with the sample's photo.Values: none, bw, poster, photo |
FilterSpeckleinteger, default 4 | Drops patches smaller than this many pixels; 0 kept 861 paths (131,895 bytes).Values: 0–128 |
ColorPrecisioninteger, default 6 | How finely colors are told apart: 3 left 3 paths, 8 gave 186.Values: 1–8 |
GradientStepinteger, default 16 | Minimum color step between layers; 64 gave 47 paths and 0 gave 104.Values: 0–128 |
CurveModeenum, default spline | polygon uses straight lines (11,833 bytes), pixel stair-steps along pixel edges (24,371 bytes).Values: spline, polygon, pixel |
CornerThresholdinteger, default 60 | Joints sharper than this angle stay corners; 90 gave 51,782 bytes.Values: 0–180 |
SegmentLengthnumber, default 4 | Curve segment length; 8 gave 34,392 bytes, and below 3.5 is a 400.Values: 3.5–10 |
SpliceThresholdinteger, default 45 | Angle that splits a spline; 90 gave 40,188 bytes.Values: 0–180 |
ColorModeenum, default color | bw traces only light and dark: 4 paths for the sky, hills and lake.Values: color, bw |
Layeringenum, default stacked | cutout builds non-overlapping shapes, 59 paths here.Values: stacked, cutout |
ImageWidthinteger | Resizes before tracing: ImageWidth=120 gave a 120 × 60 SVG with 57 paths.Values: 1–20000 |
ImageHeightinteger | Height to resize to before tracing; the scale options work as on the raster routes.Values: 1–20000 |
FileNamestring | Name of the SVG. |
StoreFileboolean, default false | 3-hour link instead of the file. |
What carries over, and what doesn't
Kept
- The 400 × 200 canvas, set as
widthandheighton the root element - The broad color zones: night-blue zenith, orange horizon band, dark hills and the sun's streak on the lake
- A fully covered canvas: the drawn SVG has no transparent pixel, as the Radiance image has none
Lost or not yet supported
- Smooth gradients. Even with
photo, the sky becomes bands; with the default settings most of it is a single fill. - Light above 1.0 and every difference finer than 8 bits.
- A
viewBox, so scaling the SVG with CSS needs one added.
Limits
- Tracing is limited to 12,000,000 pixels:
ImageWidth=5000(5,000 × 2,500) on this sky returned 413. - One .hdr per request; up to 10 MB on the Free plan or 100 MB on paid plans.
- 100,000,000-pixel input ceiling.
- 120 seconds per request.
Where teams use it
Poster-style skies
Designers can turn a rendered or captured sky into flat color bands for posters, banners or game backdrops that scale cleanly. Preset=poster keeps more bands than photo.
Silhouettes from a horizon
ColorMode=bw separates the dark hills from the bright band of sky just above them, giving a horizon outline for a logo or a cut file.
Questions
Why did my sky turn into one flat color?
With the default settings, neighbouring sky colors are close enough to merge into one region. Preset=photo or Preset=poster keeps more bands; so does a lower GradientStep.
Is the panorama still 2:1 after tracing?
Yes. The SVG is 400 × 200 units, the same as the source pixels, and nothing is reprojected.
How large can the input be?
The decoded image may have up to 100,000,000 pixels, but tracing stops at 12,000,000. Send ImageWidth to scale a large .hdr down within the same request.
Convert your first HDR file in a minute.