Read QR codes from TIFF API
Send a TIFF to /v2/convert/tiff/to/qrdata and the API looks for QR codes on its first page and returns their text in a small JSON file, {"codes": [...]}. Several codes in one image come back as several entries.
- Endpoint
- POST /v2/
convert/ tiff/ to/ qrdata - Accepts
- tiff
- Sample timing
- 4 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/qrdata \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@bin-label-c14.tiff \
-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/qrdata",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("bin-label-c14.tiff", "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 = "bin-label-c14.tiff";
form.append("File", new Blob([await readFile(file)]), file);
const url = "https://api.pdfconversionapi.com/v2/convert/tiff/to/qrdata";
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.004,
"Files": [
{
"FileName": "bin-label-c14.json",
"FileExt": "json",
"FileSize": 70,
"FileData": "ewogICJjb2RlcyI6IFsKICAg…"
}
]
}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.
bin-label-c14.json, 5 lines
{ "codes": [ "BIN C-14 / LOT 2026-0412 / 24 x hex bolt M8" ]}
- Input
- bin-label-c14.tiff (42 KB)
- Output
- bin-label-c14.json (70 bytes)
- 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 |
|---|---|
FileNamestring | Name of the JSON result: FileName=c14-scan returned c14-scan.json instead of bin-label-c14.json. |
StoreFileboolean, default false | true stores the JSON for 3 hours and returns its URL in Files[].Url. |
What carries over, and what doesn't
Kept
- The full text of the code: the sample returns BIN C-14 / LOT 2026-0412 / 24 x hex bolt M8, spaces and slashes included.
- Every code it finds: an image with two QR codes returned both strings in one
codesarray. - Codes in other orientations and at smaller sizes: the label rotated 90° and the label scaled to 200 × 150 both decoded.
- TIFF compressions: LZW (the sample) and a 1-bit CCITT Group 4 copy of the label both worked.
Lost or not yet supported
- Any code on page 2 or later. A two-page test TIFF with the QR code only on page 2 returned 422 'no readable QR code was found in the image'.
- Where the code was: the JSON holds the text only, with no position, size or page number.
- Other barcode types. Code 128, Data Matrix and similar symbols are not read here;
/v2/convert/barcode/to/datahandles those.
Limits
- One TIFF per request; only its first page is searched.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
- Images over 100,000,000 pixels return 413 before any decoding.
- Up to 50 code candidates are decoded per image.
- No code found returns 422; palette TIFFs also return 422.
- 120 seconds per request before a 504.
Where teams use it
Warehouse and inventory scans
Bin labels, pallet tags and packing slips can arrive as TIFF scans. Read the QR code from each one to file it under the right bin or lot without typing.
Fax and document intake
Forms that carry a QR code with a case or form number can be sorted as they arrive. A black-and-white Group 4 fax version of the sample label was read as well as the color original.
Checking printed codes
Scan a printed sheet and confirm that each code still decodes to the expected text before the batch ships.
Questions
What does the response look like?
A JSON file in Files[0].FileData, base64-encoded like every other result. Decoded, the sample's file is 70 bytes of indented JSON: {"codes": ["BIN C-14 / LOT 2026-0412 / 24 x hex bolt M8"]}.
What if the TIFF has no QR code?
The API returns 422 with the message 'no readable QR code was found in the image'. Treat that status as an empty result in your code.
Are all pages of a multi-page TIFF searched?
No, only the first. Move the page with the code to the front, or split the TIFF and send pages one at a time.
Do I need to clean up or rotate the scan first?
Not for typical scans. The sample label read correctly at its own size, rotated 90°, scaled to half size and as a black-and-white fax TIFF. A code that is blurred or cut off at the edge can still fail.
Can options such as ImageWidth be used to help detection?
No. This conversion reads only FileName and StoreFile; other options are ignored, and the sample gave the same result with ImageWidth=100.
Convert your first TIFF file in a minute.