JPG to ICO API
POST a JPEG to /v2/convert/jpg/to/ico and get a .ico file with one icon image inside, PNG-compressed at 32 bits per pixel. Pictures larger than 256 px are fitted into 256 × 256 with their shape kept, so the 400 × 300 sample became a 256 × 192 icon.
- Endpoint
- POST /v2/
convert/ jpg/ to/ ico - Accepts
- jpg
- Sample timing
- 6 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/jpg/to/ico \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@trailhead-sign.jpg \
-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/jpg/to/ico",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("trailhead-sign.jpg", "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 = "trailhead-sign.jpg";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/jpg/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.006,
"Files": [
{
"FileName": "trailhead-sign.ico",
"FileExt": "ico",
"FileSize": 71020,
"FileData": "AAABAAEAAMAAAAAAIABWFQEA…"
}
]
}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
- trailhead-sign.jpg (25 KB)
- Output
- trailhead-sign.ico (69 KB)
- Source
- A drawn mountain trailhead with a wooden sign and a QR code, made by us for these pages and saved at JPEG quality 92 with an EXIF block, an sRGB color profile and 300 dpi. (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 before the 256 cap. ImageWidth=64 gave a 64 × 48 icon of 6,142 bytes; 1000 still ends at 256 × 192.Values: 1–20000 (the icon is capped at 256) |
ImageHeightinteger | Icon height. 48 × 48 as a box gives 48 × 36, since the photo is 4:3.Values: 1–20000 (capped at 256) |
ScaleProportionsboolean, default true | false with both sizes gives a square icon by stretching: 256 × 256 from the 4:3 sign (90,252 bytes). |
ScaleIfLargerboolean, default false | true leaves a small JPEG at its own size when the requested size is larger; the 256 cap still applies. |
ScaleImageboolean, default true | false crops instead of scaling. ImageWidth=300, ImageHeight=300 cuts a 300 × 300 square from the left of the sign, which is then fitted to 256 × 256: a square icon without distortion. |
ColorSpaceenum, default default | gray gives a gray icon (63,743 bytes for the sample). monochrome leaves only pure black and white pixels, also after the fit to 256 px (4,365 bytes).Values: default, rgb, gray, monochrome; cmyk refused |
FileNamestring | Name of the .ico; trailhead-sign.ico by default. |
StoreFileboolean, default false | true swaps the base64 data for a link that works for 3 hours. |
What carries over, and what doesn't
Kept
- The picture's proportions: 4:3 in, 4:3 out (256 × 192), unless you crop or stretch
- The upright view of phone photos: a 300 × 200 test photo tagged EXIF Orientation 6 came back as a 171 × 256 icon, turned as the tag says before the fit
- Color at 8 bits per channel, stored as a PNG inside the icon
- Your chosen size up to 256 px, applied before the cap
Lost or not yet supported
- Pixels beyond 256 on a side; larger pictures are scaled down to fit.
- Squareness. A 4:3 photo gives a 256 × 192 icon; crop or stretch if the place you use it expects a square.
- Transparency, since a JPEG has none: every alpha value in the icon is 255, and
TransparentColoris not applied to icons. - Extra icon sizes. The file always holds one image; there is no 16/32/48 set.
- EXIF, color profile and DPI.
Limits
- One JPEG per request, one image per .ico.
- Icon images up to 256 × 256.
- Uploads up to 10 MB on the Free plan or 100 MB on paid plans.
- 100,000,000 decoded pixels at most.
- 120 seconds per request.
Where teams use it
A favicon from a photo or a JPEG logo
When the only copy of a logo is a JPEG, crop it square with ScaleImage=false and the size you want, and use the .ico as a favicon or a shortcut icon.
Icons for user-uploaded profile or project images
Turn an uploaded picture into a small Windows icon for a desktop shortcut or a generated installer, with ImageWidth set to the size you ship.
Questions
Why is my icon 256 × 192 and not square?
The picture is fitted inside 256 × 256 without changing its shape. For a square icon, crop with ScaleImage=false and equal ImageWidth and ImageHeight, or stretch with ScaleProportions=false.
Can one .ico hold 16, 32 and 48 px versions?
No. The file always contains one icon image. Make one call per size; combining them into a single .ico needs another tool.
How is the icon stored inside the file?
As a 32-bit PNG. The directory entry for the sample says 256 × 192, 32 bits per pixel, and the image data starts with the PNG signature.
Can the icon have a transparent background?
Not from a JPEG. The format has no alpha, and the color knock-out option isn't applied to icons. Start from a transparent PNG and use png-to-ico.
Convert your first JPG file in a minute.