ICO to BMP API
POST an icon to /v2/convert/ico/to/bmp and get an uncompressed Windows bitmap. An icon with transparency, like the sample, takes 4 bytes per pixel and a version 4 header with an alpha mask, so its clear corners and shadow keep their values. A fully opaque icon gives a 24-bit bitmap.
- Endpoint
- POST /v2/
convert/ ico/ to/ bmp - Accepts
- ico
- Sample timing
- 1 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/ico/to/bmp \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@paper-plane-app-icon.ico \
-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/ico/to/bmp",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("paper-plane-app-icon.ico", "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 = "paper-plane-app-icon.ico";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/ico/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.001,
"Files": [
{
"FileName": "paper-plane-app-icon.bmp",
"FileExt": "bmp",
"FileSize": 262266,
"FileData": "Qk16AAQAAAAAAHoAAABsAAAA…"
}
]
}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
- paper-plane-app-icon.ico (34 KB)
- Output
- paper-plane-app-icon.bmp (256 KB)
- Source
- A four-size app icon we drew and assembled ourselves: 16, 32 and 48 px entries stored as 32-bit bitmaps with a 1-bit mask, and a 256 px entry stored as PNG. The paper plane design is fictional. (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 | Pixel width of the bitmap. ImageWidth=128 gave 65,658 bytes: a 122-byte header plus 128 × 128 × 4 bytes of pixels.Values: 1–20000 |
ImageHeightinteger | Pixel height; on its own the width follows. With a width, the icon is fitted inside that box.Values: 1–20000 |
ScaleProportionsboolean, default true | false stretches to the exact box. For a square icon in a square box the result is identical either way. |
ScaleIfLargerboolean, default false | Shrink only. A 640 px request on the 256 px icon then returns the 262,266-byte original size instead of a 1,638,522-byte enlargement. |
ScaleImageboolean, default true | false cuts the requested size out of the top-left corner: 100 × 80 gave a 32,122-byte bitmap. |
ColorSpaceenum, default default | Changes the pixel values only. gray and monochrome still produce a 32-bit BMP of the same 262,266 bytes.Values: default, rgb, gray, monochrome |
ImageResolutioninteger | Written as pixels per meter in the header (96 becomes 3,780). Left out, both fields are 0.Values: 10–800 |
ImageInterpolationboolean, default false | Accepted and ignored: the resize filter is always Lanczos. |
FileNamestring | Name for the .bmp file; the icon's name is reused by default. |
StoreFileboolean, default false | Get a 3-hour download link instead of a base64 body, handy for a 262 KB bitmap. |
What carries over, and what doesn't
Kept
- All color and alpha values of the icon's 256 px entry, checked pixel by pixel against the sample
- Partial transparency: the 12,009 partly transparent pixels of the shadow and the badge's edge keep their 8-bit alpha
- The entry's size, 256 × 256 for the sample
Lost or not yet supported
- Compactness. With no compression the bitmap is 262,266 bytes, 7.6 times the whole four-size icon (34,551 bytes).
- The three smaller entries of the sample icon.
- A 24-bit or palette version of an icon with transparency. Any clear or partly clear pixel keeps the result at 32 bits per pixel; only a fully opaque icon comes out as 24-bit.
Limits
- One .ico per request.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
ImageWidthandImageHeightup to 20,000 px, and results up to 100,000,000 pixels; a large BMP grows by 4 bytes per pixel with transparency, 3 without.- 120 seconds per conversion, then 504.
Where teams use it
Build tools that only take .bmp
If a resource compiler, installer builder or embedded toolkit in your pipeline reads bitmaps but not icons, convert the .ico during the build instead of keeping a hand-exported copy that drifts out of date.
Reading pixels without a decoder
The bitmap needs no decompression. In the sample, the pixels start at byte 122 as rows of blue, green, red and alpha bytes, bottom row first, which is easy to load from C, a shader tool or a test script.
Questions
Does the BMP keep the icon's transparency?
Yes. It is stored as 32 bits per pixel with bit fields, and the header's alpha mask marks the fourth byte as alpha. The sample's alpha values all came back unchanged, and all three image readers we tried opened the file.
Why is the BMP so much larger than the .ico?
The icon stores its 256 px image as a compressed PNG. The bitmap stores every pixel as 4 raw bytes: 256 × 256 × 4 = 262,144 bytes, plus a 122-byte header.
Can I get a 24-bit or 8-bit BMP instead?
24-bit, yes, when no pixel of the icon is see-through: a test icon with a fully opaque 256 px entry came back as a 24-bit BMP with the 40-byte header, 196,662 bytes. An icon with any clear or partly clear pixel, like the sample, stays 32-bit, and there is no 8-bit option. ColorSpace=gray makes the pixels gray but leaves the format, and the size, the same.
Which size from the icon ends up in the bitmap?
The entry with the highest color depth, then the largest. For the sample that is 256 × 256; resize with ImageWidth if your tool expects 32 or 48 px.
Convert your first ICO file in a minute.