HEIC to ICO API
POST a HEIC to /v2/convert/heic/to/ico and get an icon file holding one 32-bit image, stored as PNG inside the ICO. Larger photos are shrunk to fit 256 × 256 with their shape kept: the 400 × 300 harbor becomes a 256 × 192 icon, or 128 × 96 at the sample's ImageWidth=128.
- Endpoint
- POST /v2/
convert/ heic/ to/ ico - Accepts
- heic
- Sample timing
- 26 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/heic/to/ico \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@harbor-dusk.heic \
--form-string ImageWidth=128 \
-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/heic/to/ico",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("harbor-dusk.heic", "rb"))],
data={
"ImageWidth": "128",
},
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 = "harbor-dusk.heic";
form.append("File", new Blob([await readFile(file)]), file);
form.append("ImageWidth", "128");
const url = "https://api.pdfconversionapi.com/v2/convert/heic/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.026,
"Files": [
{
"FileName": "harbor-dusk.ico",
"FileExt": "ico",
"FileSize": 19083,
"FileData": "AAABAAEAgGAAAAAAIAB1SgAA…"
}
]
}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
- harbor-dusk.heic (22 KB)
- Output
- harbor-dusk.ico (19 KB)
- Source
- A harbor at dusk that we drew ourselves, saved as a 400 x 300 HEIC stored sideways with a rotation flag and made-up camera and GPS tags. (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 in pixels, up to the 256-pixel cap. The sample's 128 gives a 128 × 96 icon of 19,083 bytes; 1000 still gave 256 × 192.Values: 1–20000 |
ImageHeightinteger | Icon height in pixels. With ImageWidth, the photo is fitted inside the box unless ScaleProportions=false.Values: 1–20000 |
ScaleProportionsboolean, default true | false squeezes the photo into the exact box: 48 × 48 gave a square 48 px icon of 3,917 bytes. |
ScaleIfLargerboolean, default false | true never enlarges a photo smaller than the box. The 256-pixel cap applies either way. |
ScaleImageboolean, default true | false crops instead of scaling: 300 × 300 cut a square from the top-left corner, then the cap shrank it to 256 × 256. |
ColorSpaceenum, default default | gray gives a gray icon (81,568 bytes at 256 × 192). monochrome is applied again after the shrink to 256 px, so every pixel of the icon is black or white: 2,564 bytes at the default 256 × 192.Values: default, rgb, gray, monochrome |
FileNamestring | Name of the .ico without the extension. |
StoreFileboolean, default false | true stores the icon for 3 hours and returns its URL instead of base64 data. |
What carries over, and what doesn't
Kept
- The photo's proportions: 128 × 96 for the sample and 256 × 192 by default, not padded or stretched to a square
- Lossless pixels inside the icon, since the entry is a PNG at 32 bits per pixel
- The upright view of a HEIC stored sideways with a rotation flag
Lost or not yet supported
- Resolution above 256 pixels: a 12-megapixel 4032 × 3024 HEIC also comes back 256 × 192.
- Extra sizes. The file holds one image; there are no 16, 32 or 48 px versions for Windows to pick from.
- EXIF, XMP and GPS tags of the photo.
- Correct tones for HEIC files with an embedded Display P3 profile: gray 16 became 0 in our test.
Limits
- One HEIC per request; one image per icon, at most 256 × 256.
- Photos up to 100,000,000 pixels are read; a 10,240 × 10,240 HEIC returned 413.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans and 120 seconds per conversion.
- HEIC files saved at the top quality setting of a desktop encoder returned 422 in our tests.
Where teams use it
Profile pictures as favicons
A user's phone photo can become the icon for their page or workspace. Send ImageWidth=64 or 128 and the icon arrives at the size you need, already upright.
Shortcut icons from product shots
Internal tools that build desktop shortcuts or installers need .ico files. A HEIC product photo converts in one call, without a separate resize step.
Questions
Does the icon contain several sizes?
No. The ICO directory has a single entry, a 32-bit PNG. For a multi-size icon, call the route once per size with ImageWidth and combine the entries yourself.
How do I get a square icon from a landscape photo?
Either stretch it with ImageWidth=64, ImageHeight=64 and ScaleProportions=false, or crop a square with ScaleImage=false. The crop always starts at the top-left corner, so a 300 × 300 square of the harbor drops its right 100 pixels and cuts through the sun.
What happens to a full-size phone photo?
It is shrunk to fit 256 × 256 with a Lanczos3 filter. A 4032 × 3024 HEIC came back as a 256 × 192 icon of 47,906 bytes.
Is the icon transparent?
The image has an alpha channel, but a photo with no transparency fills it with 255. HEIC or HEIF images that carry an alpha plane keep it; see HEIF to ICO for a transparent example.
Convert your first HEIC file in a minute.