XCF to ICO API
POST an .xcf to /v2/convert/xcf/to/ico and get an icon file with one 32-bit image of the merged layers. Canvases larger than 256 px are fitted into 256 × 256; send ImageWidth for a smaller icon.
- Endpoint
- POST /v2/
convert/ xcf/ to/ ico - Accepts
- xcf
- Sample timing
- 45 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/xcf/to/ico \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@gull-rock-lighthouse.xcf \
--form-string ImageWidth=64 \
-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/xcf/to/ico",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("gull-rock-lighthouse.xcf", "rb"))],
data={
"ImageWidth": "64",
},
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 = "gull-rock-lighthouse.xcf";
form.append("File", new Blob([await readFile(file)]), file);
form.append("ImageWidth", "64");
const url = "https://api.pdfconversionapi.com/v2/convert/xcf/to/ico";
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.045,
"Files": [
{
"FileName": "gull-rock-lighthouse.ico",
"FileExt": "ico",
"FileSize": 4395,
"FileData": "AAABAAEAQDAAAAAAIAAVEQAA…"
}
]
}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
- gull-rock-lighthouse.xcf (203 KB)
- Output
- gull-rock-lighthouse.ico (4.3 KB)
- Source
- A 400 x 300 harbor poster we drew and wrote as a layered XCF file ourselves: 11 layers, including a two-layer group, a layer mask, Multiply and Screen modes and a hidden guide layer. (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 | Icon width. The sample sends 64, so the 400 × 300 poster becomes a 64 × 48 icon of 4,395 bytes. Values above 256 end up at 256.Values: 1–20000 |
ImageHeightinteger | Icon height; with ImageWidth, a box the canvas is fitted into.Values: 1–20000 |
ScaleProportionsboolean, default true | false with both sizes gives a square icon from a non-square canvas: 300 × 300 was cut down to a 256 × 256 entry. |
ScaleIfLargerboolean, default false | true keeps a canvas smaller than the requested size as it is. |
ScaleImageboolean, default true | false crops the top-left area instead of scaling the whole canvas. |
ColorSpaceenum, default default | gray (24,665 bytes at 256 × 192) or monochrome (3,017 bytes, with color values only black or white) icon colors; still stored as a 32-bit image.Values: default, rgb, gray, monochrome; cmyk returns 400 |
FileNamestring | Name of the .ico file; favicon gives favicon.ico. |
StoreFileboolean, default false | true returns a 3-hour link in place of base64. |
What carries over, and what doesn't
Kept
- The merged look of the layers, scaled with a Lanczos3 filter
- Transparency: the icon image is 32-bit RGBA, so a GIMP canvas with a transparent background gives an icon with a transparent background
- The canvas proportions: a 4:3 poster gives a 4:3 icon (256 × 192 by default) unless you set both sizes
Lost or not yet supported
- Multiple sizes. The .ico holds a single image, at the size you asked for.
- Square shape for non-square canvases, unless you send both sizes with
ScaleProportions=false. - Detail at small sizes: at 64 × 48 the poster's subtitle is no longer readable.
Limits
- One XCF per request. The canvas may hold up to 67,108,864 pixels (8,192 × 8,192): 16,384 × 4,096 gave a 256 × 64 icon, 8,193 × 8,192 gets a 422.
- 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.
- The icon image is at most 256 × 256 pixels.
Where teams use it
Favicons from a GIMP logo
Site logos are often kept as layered GIMP files. Converting the .xcf with ImageWidth=32 or 48 gives a favicon.ico without an export step, straight from the master.
Application and shortcut icons
Windows shortcuts and installers use .ico files. Draw the icon on a square GIMP canvas and send it with ImageWidth=256, the largest size one ICO entry can hold.
Questions
Which sizes does the .ico contain?
One. The sample request gives a single 64 × 48 entry; with no size sent, the poster gives one 256 × 192 entry. To ship 16, 32 and 48 px versions, make one request per size and combine them with an icon tool.
How is the image stored inside the icon?
As a PNG image inside the ICO container, 8 bits per channel with alpha. The file has no bitmap-style entry, so a program that only reads the older bitmap entries will not show it.
What if my GIMP canvas is not square?
The icon keeps its proportions, so a 400 × 300 canvas gives 256 × 192. For a square icon, make the canvas square in GIMP or send ImageWidth, ImageHeight and ScaleProportions=false to stretch it.
Convert your first XCF file in a minute.