GIF to BMP API
POST a GIF to /v2/convert/gif/to/bmp to get frame 1 as an uncompressed BMP. A GIF with transparent pixels, like the sample, becomes 32-bit with a version 4 header and alpha 0 where it was clear; a fully opaque one becomes 24-bit. Colors are copied exactly.
- Endpoint
- POST /v2/
convert/ gif/ to/ bmp - Accepts
- gif
- 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/gif/to/bmp \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@parcel-tracker.gif \
--form-string ImageResolution=96 \
-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/gif/to/bmp",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("parcel-tracker.gif", "rb"))],
data={
"ImageResolution": "96",
},
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 = "parcel-tracker.gif";
form.append("File", new Blob([await readFile(file)]), file);
form.append("ImageResolution", "96");
const url = "https://api.pdfconversionapi.com/v2/convert/gif/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": "parcel-tracker.bmp",
"FileExt": "bmp",
"FileSize": 256122,
"FileData": "Qk166AMAAAAAAHoAAABsAAAA…"
}
]
}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
- parcel-tracker.gif (8.7 KB)
- Output
- parcel-tracker.bmp (250 KB)
- Source
- A four-frame animated parcel-tracking card with a transparent outside and a QR code, drawn by us for this page. (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 |
|---|---|
ImageResolutioninteger | DPI to write into the BMP header as pixels per meter. The sample sends 96, stored as 3,780. Without it both fields are 0.Values: 10–800 |
ImageWidthinteger | Width in pixels. A BMP grows with every pixel: the 200 × 80 version is 64,122 bytes and an 800 × 320 one is 1,024,122.Values: 1–20000 |
ImageHeightinteger | Height in pixels; 100 made a 250 × 100 bitmap. With both values the GIF fits inside the box.Values: 1–20000 |
ScaleProportionsboolean, default true | false stretches to both values exactly. |
ScaleIfLargerboolean, default false | true keeps small GIFs at their own size instead of enlarging them. |
ScaleImageboolean, default true | false cuts the top-left ImageWidth × ImageHeight area out without scaling. |
ColorSpaceenum, default default | gray and monochrome change the pixel values, but the sample stays a 32-bit BMP of the same size (256,122 bytes), since its corners are still clear.Values: default, rgb, gray, monochrome |
FileNamestring | Base name for the .bmp file. |
StoreFileboolean, default false | Link to the file for 3 hours instead of inline data. |
What carries over, and what doesn't
Kept
- Every pixel of frame 1, compared channel by channel
- Transparency, in a real alpha channel: the header carries a mask for it (0xFF000000)
- Readability: the image tools built into macOS opened the 32-bit sample and decoded every pixel exactly
Lost or not yet supported
- All frames after the first, and the frame timing.
- Compactness: no compression is applied, so 8,956 bytes of GIF became 256,122 bytes, 4 bytes for each of the 64,000 pixels plus a 122-byte header.
- Predictable display of the clear areas. The clear corners keep their stored color, here magenta (255, 0, 255); a program that ignores the BMP alpha channel shows magenta corners.
- A resolution, unless you send
ImageResolution.
Limits
- One GIF per request.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
- 413 for images above 100,000,000 pixels, checked before decoding.
- Resize values from 1 to 20,000.
- Each conversion may take up to 120 seconds; after that, 504.
Where teams use it
Programs that read only bitmaps
Firmware tools, some Windows utilities and embedded display toolchains take BMP input. Converting a GIF logo or icon gives them a plain uncompressed bitmap.
Raw pixels for your own code
An uncompressed BMP is easy to parse by hand: 3 bytes per pixel for an opaque GIF or 4 with alpha, rows of fixed length, no decompression step.
Questions
Which BMP variant does the API write?
For a GIF with transparent pixels, like the sample: a 32-bit bottom-up BMP with a 108-byte version 4 header, BI_BITFIELDS layout, an alpha mask and 0 in its color space field. A GIF with no transparent pixels, even one that declares a transparent color no pixel uses, gets a 24-bit BMP with the 40-byte header: 300,054 bytes for a 400 × 250 one. Neither is palette-based.
Why do the transparent corners look colored in some viewers?
GIF stores a real color under its transparent index; ours is magenta. The BMP keeps that color with alpha 0. Viewers that honor the alpha mask show nothing there, viewers that ignore it show magenta. If your target ignores alpha, flatten without loss: /v2/convert/gif/to/pnm with AlphaColor fills the clear pixels, and /v2/convert/pnm/to/bmp turns that file into a 24-bit BMP, 192,054 bytes for the sample, with no pixel different from frame 1 on that fill.
Can I choose 24-bit or 8-bit output?
Not by option. The depth follows the frame: 24-bit when every pixel is opaque, 32-bit when any is clear, never 8-bit. To get 24-bit from a transparent GIF, flatten it first as described above.
Is any parameter refused?
ConvertAPI documents no options for GIF to BMP, so options this route doesn't read are ignored rather than refused: ImageQuality=50 or TransparentColor returned the same file as no options.
Convert your first GIF file in a minute.