TIFF to PNG API
POST a TIFF to /v2/convert/tiff/to/png and get a lossless PNG of its first page. 8-bit and 16-bit pages keep their depth, ImageResolution adds a pHYs DPI chunk, and TransparentColor can key out a background.
- Endpoint
- POST /v2/
convert/ tiff/ to/ png - Accepts
- tiff
- Sample timing
- 2 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/tiff/to/png \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@bin-label-c14.tiff \
--form-string ImageResolution=200 \
-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/tiff/to/png",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("bin-label-c14.tiff", "rb"))],
data={
"ImageResolution": "200",
},
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 = "bin-label-c14.tiff";
form.append("File", new Blob([await readFile(file)]), file);
form.append("ImageResolution", "200");
const url = "https://api.pdfconversionapi.com/v2/convert/tiff/to/png";
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.002,
"Files": [
{
"FileName": "bin-label-c14.png",
"FileExt": "png",
"FileSize": 31887,
"FileData": "iVBORw0KGgoAAAANSUhEUgAA…"
}
]
}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
- bin-label-c14.tiff (42 KB)
- Output
- bin-label-c14.png (31 KB)
- Source
- A two-page warehouse bin label and cycle-count sheet we drew for this page, saved as an LZW-compressed TIFF at 200 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 |
|---|---|
ImageResolutioninteger | Written as a pHYs chunk. The sample sends 200, stored as 7,874 pixels per metre; without it the PNG has no DPI at all.Values: 10–800 |
TransparentColorstring | Exact color to make transparent. #FCFBF7 turned the label's paper into 51,575 transparent pixels and the PNG into RGBA (34,348 bytes).Values: Color name, #RRGGBB or R,G,B |
ColorSpaceenum, default default | gray writes an 8-bit grayscale PNG (12,526 bytes for the label), monochrome one with two values (4,337 bytes). cmyk returns 400.Values: default, rgb, gray, monochrome |
ImageWidthinteger | Width of the PNG; 800 doubled the label to 800 × 600 and 208,005 bytes.Values: 1–20000 |
ImageHeightinteger | Height of the PNG; combine with ImageWidth for a bounding box.Values: 1–20000 |
ScaleProportionsboolean, default true | false distorts the page to the exact width and height. |
ScaleIfLargerboolean, default false | true only shrinks pages that are bigger than the requested size. |
ScaleImageboolean, default true | false cuts the requested size out of the top-left corner. |
ImageInterpolationboolean, default false | Accepted as true or false; scaling always uses Lanczos. |
FileNamestring | Name for the PNG; the TIFF's own name by default. |
StoreFileboolean, default false | true returns a link in Files[].Url that works for 3 hours. |
What carries over, and what doesn't
Kept
- Every pixel of page 1: the sample PNG equals the TIFF page exactly.
- Bit depth: an 8-bit RGB page gave an 8-bit RGB PNG, and a 16-bit gray TIFF a 16-bit gray PNG.
- The DPI you send: the sample's pHYs chunk reads 200 dpi.
- The Orientation tag, applied to the pixels: TIFFs tagged 2, 3, 6 and 8 all matched their upright views, and a 300 × 200 page tagged 6 came out 200 × 300.
- Every compression we tried in the input: LZW, Deflate, PackBits, JPEG-in-TIFF and CCITT Group 4 fax.
Lost or not yet supported
- Page 2 and any later page; the sample's count sheet is not in the response.
- The DPI when you don't send it: the default PNG has no pHYs chunk at all.
- CMYK: such TIFFs become RGB by inverting each channel, with no color profile.
- Metadata: no ICC profile or TIFF text tags are copied into the PNG.
Limits
- One TIFF per request, first page only.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
- 100,000,000 pixels per page, and per resized page; above that the API returns 413.
- Indexed-color TIFFs, and the gray-plus-alpha TIFF we made, are refused with 422.
- Conversions are cut off at 120 seconds with a 504.
Where teams use it
Show scans in a browser without losing detail
For a web page or an app view, PNG is the lossless choice: it keeps every pixel of a scanned drawing or form, so zoomed-in text and thin lines look as they did in the scanner's file.
High bit-depth images
Scientific and medical TIFFs can hold 16 bits per channel. A 16-bit gray test TIFF came back as a 16-bit gray PNG, so the extra levels survive.
Print-ready files with their DPI
Send the scan's resolution as ImageResolution and the PNG records it, so layout tools place the image at its real size.
Questions
Is TIFF to PNG lossless?
Yes for the first page. The sample PNG decodes to the same 400 × 300 pixels as the LZW TIFF, with no difference in any channel.
Does a 16-bit TIFF stay 16-bit?
Yes. A 16-bit grayscale test TIFF came back as a 16-bit grayscale PNG. Other outputs, such as JPEG or GIF, reduce it to 8 bits.
How do I keep the scan's DPI?
Send it as ImageResolution. With ImageResolution=200 the PNG gets a pHYs chunk of 7,874 pixels per metre. Without it no DPI is written, because the TIFF's own value is not copied.
What about multi-page TIFFs?
The first page is converted and the others are skipped without an error. Our sample has two pages and returns one PNG. Split the TIFF before upload to convert each page.
Which TIFFs fail?
Indexed-color (palette) TIFFs return 422 with a message saying RGBPalette is unsupported, and so did a gray-plus-alpha TIFF made with a common imaging library. Save those as RGB first.
Convert your first TIFF file in a minute.