PSB to BMP API
Send a .psb to /v2/convert/psb/to/bmp and get a BMP back as base64. The 384 × 128 banner is opaque, so it comes back as a plain 24-bit BMP of 147,510 bytes: a 54-byte header, then 3 bytes per pixel with no alpha channel.
- Endpoint
- POST /v2/
convert/ psb/ to/ bmp - Accepts
- psb
- Sample timing
- 88 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/psb/to/bmp \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@ridge-loop-trail-banner.psb \
-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/bmp",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("ridge-loop-trail-banner.psb", "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 = "ridge-loop-trail-banner.psb";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/psb/to/bmp";
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.088,
"Files": [
{
"FileName": "ridge-loop-trail-banner.bmp",
"FileExt": "bmp",
"FileSize": 147510,
"FileData": "Qk02QAIAAAAAADYAAAAoAAAA…"
}
]
}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.bmp (144 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 | Width after flattening. Halving it quarters the file: 192 gave a 192 × 64 BMP of 36,918 bytes.Values: 1–20000 |
ImageHeightinteger | Height after flattening; with a width, the banner is fitted into the box.Values: 1–20000 |
ScaleProportionsboolean, default true | false allows the banner to be stretched to the exact size. |
ScaleIfLargerboolean, default false | true prevents enlarging a document that already fits. |
ScaleImageboolean, default true | false takes a crop from the top left instead of scaling. |
ImageResolutioninteger | Writes the resolution into the header as pixels per meter; 150 restores the banner's 150 ppi.Values: 10–800 |
ColorSpaceenum, default default | gray and monochrome change the pixel values only, making R, G and B equal (monochrome keeps just 0 and 255). The BMP stays 24 bits per pixel and 147,510 bytes.Values: default, rgb, gray, monochrome; cmyk returns 400 |
FileNamestring | Base name of the BMP; otherwise the upload's name. |
StoreFileboolean, default false | true returns the BMP as a 3-hour link. |
What carries over, and what doesn't
Kept
- The merged colors, rounded from 16 to 8 bits per channel and otherwise unchanged
- The visible layers only; the hidden Old route does not appear
- Opacity: the banner has no clear pixels, so the BMP is 24-bit RGB with no alpha channel at all
- Readability: the 40-byte header is the common BMP layout, and all three readers we tried decoded the banner to the same pixels
Lost or not yet supported
- Bit depth over 8 bits per channel.
- Compression. Each pixel takes 3 bytes, so the BMP is 147,510 bytes where the lossless WebP of the same banner is 16,340.
- The 150 ppi setting: the header's resolution fields are 0 unless you send
ImageResolution. - Layers and any embedded color profile.
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 100,000,000 pixels after flattening, or 413.
- Resize targets from 1 to 20,000 px per side.
- 120 seconds per conversion, then 504.
- Untested in PSB files: type layers, smart objects and layer effects.
Where teams use it
Feeding BMP-only software from large artwork
Older Windows programs and some embedded tool chains read BMP only. Scale the PSB with ImageWidth to the target's resolution in the same request.
Comparing renders pixel by pixel
Because nothing is compressed, two BMPs of the same size can be compared byte for byte after the header, which suits regression tests of artwork.
Questions
Does the BMP have an alpha channel?
Not for an opaque document. The banner has no clear pixels, so the route writes a plain 24-bit BMP with the 40-byte header. A document with transparent areas gets a 32-bit BMP with an alpha mask instead: the PSD sticker sent to this route came back that way, with its 10,195 clear pixels.
How big will the BMP be?
For an opaque document, width × 3 bytes per row, padded to a multiple of 4, times the height, plus a 54-byte header. 384 × 128 gives 147,510 bytes; use ImageWidth to shrink very large documents first.
Are the 16-bit colors rounded or cut?
Rounded. Every value in the BMP equals the nearest 8-bit step of the 16-bit value in the PNG made from the same file.
Convert your first PSB file in a minute.