HEIF to WebP API
POST a .heif to /v2/convert/heif/to/webp and get a lossless WebP (VP8L) with an alpha channel. Every decoded value survives, soft shadow included, at the cost of size: the sticker grew from 11,269 bytes to 42,336.
- Endpoint
- POST /v2/
convert/ heif/ to/ webp - Accepts
- heif
- Sample timing
- 23 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/heif/to/webp \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@succulent-sticker.heif \
-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/heif/to/webp",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("succulent-sticker.heif", "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 = "succulent-sticker.heif";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/heif/to/webp";
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.023,
"Files": [
{
"FileName": "succulent-sticker.webp",
"FileExt": "webp",
"FileSize": 42336,
"FileData": "UklGRlilAABXRUJQVlA4TEul…"
}
]
}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
- succulent-sticker.heif (11 KB)
- Output
- succulent-sticker.webp (41 KB)
- Source
- A potted succulent sticker that we drew ourselves, with a transparent background and a soft shadow, saved as a 320 x 320 HEIF with an alpha plane. (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 | Scales color and alpha; 200 gave 21,970 bytes and 800 gave 206,226.Values: 1–16384 |
ImageHeightinteger | Target height, or a box with ImageWidth.Values: 1–16384 |
ScaleProportionsboolean, default true | false stretches to both sizes. |
ScaleIfLargerboolean, default false | true never enlarges. |
ScaleImageboolean, default true | false crops the top-left area. |
ColorSpaceenum, default default | gray (22,460 bytes) and monochrome (9,150 bytes) change the colors and keep the alpha.Values: default, rgb, gray, monochrome |
TransparentColorstring | Adds exact-color transparency on top of the alpha plane; #C4623B cleared 5,931 more pixels.Values: #RRGGBB, R,G,B or a basic color name |
FileNamestring | Base name of the .webp. |
StoreFileboolean, default false | true returns a 3-hour link instead of base64. |
What carries over, and what doesn't
Kept
- Every RGBA value: the WebP matched our HEIF to PNG output exactly
- Partial alpha, including the 11,811 partly transparent shadow and edge pixels (alpha 1 to 254)
- The 320 × 320 size
Lost or not yet supported
- Size against the HEIF: lossless WebP is 3.8 times larger here.
- A lossy option.
ImageQualityis accepted on this route but has no effect. - Any metadata: the file has a single VP8L chunk.
- Tones of Display P3-tagged HEIF files, which the decoder stretches.
Limits
- One HEIF per request, up to 100,000,000 pixels.
- Neither side of the WebP may go over 16,384 px. The sticker stretched to 16,385 × 100 returned 413
WebP images can be at most 16,384 pixels on a side, and this one is 16,385×100; lower ImageWidth or ImageHeight; at 16,384 × 100 it converted (494,926 bytes). - Requests up to 10 MB on the Free plan or 100 MB on paid plans; 120 seconds per conversion.
- AV1-coded HEIF files (AVIF) return 422.
Where teams use it
Cut-out images on web pages
Chrome, Edge and Firefox don't display HEIF. A WebP with alpha keeps the sticker's clear background and soft shadow for sites that serve WebP.
A lossless copy smaller than PNG
For the sample, lossless WebP stores the same decoded pixels in 42,336 bytes against 46,906 for PNG.
Questions
Can I make a smaller, lossy WebP?
Not yet. ImageQuality=50 is accepted here and the WebP comes back the same, 42,336 bytes. On /v2/convert/heic/to/webp the same option returns 400, because ConvertAPI documents it for HEIC but not for HEIF.
Is the transparency kept exactly?
Yes, as decoded. Every pixel's color and alpha matched the PNG we decoded from the same file, including partly transparent ones.
Does it accept AVIF files?
No. AVIF uses the same container as HEIF but AV1 pictures, which our decoder doesn't read; an AVIF sent to this route returned 422.
Convert your first HEIF file in a minute.