NUMBERS to PNG API
Send a .numbers file to /v2/convert/numbers/to/png for a PNG of each printed page. With FitToPage=true at 120 DPI, the sample's six-column statement fits one A4 image, 992 × 1403 pixels.
- Endpoint
- POST /v2/
convert/ numbers/ to/ png - Accepts
- numbers
- Sample timing
- 0.946 s 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/numbers/to/png \
-H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
-F File=@account-statement.numbers \
--form-string FitToPage=true \
--form-string ImageResolution=120 \
-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/numbers/to/png",
headers={"Authorization": f"Bearer {key}"},
files=[("File", open("account-statement.numbers", "rb"))],
data={
"FitToPage": "true",
"ImageResolution": "120",
},
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 = "account-statement.numbers";
form.append("File", new Blob([await readFile(file)]), file);
form.append("FitToPage", "true");
form.append("ImageResolution", "120");
const url = "https://api.pdfconversionapi.com/v2/convert/numbers/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.946,
"Files": [
{
"FileName": "account-statement-1.png",
"FileExt": "png",
"FileSize": 56577,
"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
- account-statement.numbers (102 KB)
- Output
- account-statement-1.png (55 KB)
- Source
- A three-row account statement table saved by Numbers 12, taken from the MIT-licensed test files of an open-source Numbers reader; not made by us (MIT). 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 |
|---|---|
PageOrientationenum, default default | landscape also gets the six columns onto one page, with larger text than FitToPage.Values: default, portrait, landscape |
FitToPageboolean, default false | Fits the table to one page across. The sample uses it: without it the last three columns are drawn on a second PNG. |
PageSizeenum, default default | Paper to draw on; A4 unless you choose.Values: letter, legal, tabloid, ledger, statement, executive, lettersmall, a3, a4, a4small, a5, b4, b5 |
Scaleinteger | Fixed zoom in percent instead of fitting.Values: 10–300 |
ImageResolutioninteger, default 150 | Pixels per inch: A4 is 992 × 1403 at 120 and 1240 × 1753 at 150. Stored in the PNG when sent.Values: 36–600 |
PageRangestring | Only these pages.Values: e.g. 1 |
ImageWidthinteger | Width of each PNG in pixels.Values: 1–20000 |
ImageHeightinteger | Height of each PNG in pixels.Values: 1–20000 |
TransparentColorstring | Exact color to make transparent; the table's gray bands are kept.Values: a color name, #RRGGBB or R,G,B |
BackgroundColorstring, default white | Color behind the page.Values: a color name, #RRGGBB or R,G,B |
ColorSpaceenum, default default | Gray or black and white instead of color. rgb gives the same file as the default: three channels, no alpha.Values: rgb, gray, monochrome |
FileNamestring | Stem for the PNG names. |
StoreFileboolean, default false | Links valid for 3 hours. |
Not supported yet
Setting one of these to anything but its default returns 400.
Refused when set: AutoFitColumns, ThousandsSeparator, DecimalSeparator, DateFormat.
What carries over, and what doesn't
Kept
- All six columns on one A4 page, scaled down by FitToPage
- The gray header band and the shaded every-other row
- Dates as May 1, 2020 and amounts with their GBP code
- Lossless pixels, every one opaque, with 120 DPI recorded
Lost or not yet supported
- Cents on currency cells: 2021.5 is drawn as 2022GBP, 2.99 as 3GBP.
- Numbers' own fonts, drawn with a substitute sans-serif.
- Text size: FitToPage shrinks the table, so letters are smaller than at 100% scale.
- The page header shows the joined sheet and table name, Account_Statement.
Limits
- One Numbers file per request.
- Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together.
- Up to 2,000 page images per request.
- A page image can't exceed 16,000 pixels per side or 100,000,000 pixels (413).
- 120 seconds per conversion, then 504.
Where teams use it
Previews for Numbers uploads
A web app that accepts spreadsheets can show what a .numbers file contains, even though browsers and most servers can't open the format.
Screenshots for support
When a customer sends a Numbers file to support, attach a PNG of it to the ticket so every agent can see the table.
Pictures for reports and slides
Drop a lossless PNG of a small table into a slide or a report where a live spreadsheet isn't needed.
Questions
How do I keep a wide Numbers table on one image?
Send FitToPage=true, as the sample does, or PageOrientation=landscape. With neither, the table is laid out on A4 portrait and its right-hand columns move to a second PNG.
Do I need Numbers or a Mac to convert?
No. The file is opened and drawn on our servers; the sample was saved by Numbers 12 and rendered without any Apple software.
Why do the amounts look rounded?
That is how the currency cells come out when the table is printed: whole units followed by the currency code. The data itself is unchanged; the CSV and XLSX routes return the exact values.
Convert your first NUMBERS file in a minute.