HTML to image API
POST /v1/image renders HTML, Markdown or a public URL in the same
Chromium that makes PDFMint’s PDFs, and returns a PNG or a JPEG instead. Same key, same
placeholders, same strict mode, one credit. This page builds a real social card with it, shows the
one trap that gives every first attempt a white frame, and says plainly what it does not do.
How this page was checked. Every request, error message and
pixel size below was produced on 26 September 2026 by a local build of the PDFMint code that serves
pdf.mintapis.com (commit 83dddcb), on a free account. No timings are quoted.
A social card from a template string #
The most common job: a 1200×630 Open Graph image per blog post, product or invoice,
filled from data. The HTML carries {{placeholders}}; the data object fills
them; strict refuses to render if one is missing, so a card never ships reading
“{{title}}”.
curl -s -X POST https://pdf.mintapis.com/v1/image \
-H "Authorization: Bearer $PDFMINT_API_KEY" \
-H "Content-Type: application/json" \
-o og-card.png \
-d '{
"html": "<style>body{margin:0}</style><div style=\"width:1200px;height:630px;box-sizing:border-box;padding:72px;background:#0b3d2e;color:#fff;font-family:Inter,sans-serif;display:flex;flex-direction:column;justify-content:space-between\"><div style=\"font-size:28px;opacity:.7\">{{site}}</div><div style=\"font-size:68px;font-weight:700;line-height:1.05\">{{title}}</div><div style=\"font-size:26px;opacity:.7\">{{author}} · {{date}}</div></div>",
"data": { "site": "northwind.example/blog",
"title": "Shipping invoices from n8n without a template editor",
"author": "Ada Lovelace", "date": "26 Sep 2026" },
"strict": true,
"type": "png", "width": 1200, "height": 630,
"fullPage": false, "deviceScaleFactor": 1,
"filename": "og-card"
}'The response is 200, Content-Type: image/png,
Content-Disposition: attachment; filename="og-card.png" — the extension is added
because type says PNG — and the image is exactly 1200×630.
With "deviceScaleFactor": 2 (the default) the same request returns
2400×1260: sharper on retina screens, four times the pixels.
The trap: an 8-pixel white frame #
Leave out the <style>body{margin:0}</style> at the start of the HTML
above and the card comes back with a white border on the top and left: the browser’s default
8 px body margin, faithfully rendered, pushing a 1200-pixel box out of a 1200-pixel viewport.
Every screenshot tool built on Chrome does this.
The css field will not fix it for HTML input: it
applies to Markdown only, so the rule has to be inside your HTML. We checked — a request with
"css": "body{margin:0}" and HTML input still has a white pixel at (2, 2).
The options that matter #
| Field | Default | Notes |
|---|---|---|
type | png | png or jpeg. Anything else, WebP included, is refused with invalid_option. |
quality | 85 | JPEG only, 1–100. |
width / height | 1280 / 800 | The viewport. height only bounds the image when fullPage is off. |
fullPage | true | Capture the whole scrollable page. Turn it off for fixed-size cards. |
deviceScaleFactor | 2 | Pixel density. 2 doubles both dimensions. |
omitBackground | false | Transparent instead of white — PNG only, in practice. |
output | binary | binary, url (expiring hosted link) or base64. |
waitFor, timeout, javascript and the url source
behave exactly as on /v1/pdf, so a chart that draws after load can be waited for with a
CSS selector. The full list is in the API reference.
What it does not do #
- No saved templates.
{"template": "invoice", "type": "png"}returnsunsupported_source: “Images can be rendered from "html", "markdown" or "url", not from a saved template.” Send the HTML itself. - No WebP or AVIF.
"type": "webp"returnsinvalid_option: “"type" must be "png" or "jpeg"”. Convert afterwards if you need them. - One engine. It is Chromium. If you need a screenshot as Safari or Firefox would draw it, this is the wrong tool.
- Pages behind a login come back as a picture of the login form, for the reasons set out on the URL to PDF page.
When to do it yourself instead #
If you already run Playwright or Puppeteer, page.screenshot() is a few lines and
free, and nothing on this page beats it. The API earns its credit when you do not want Chromium,
its fonts and its memory in your own container — Inter, JetBrains Mono, Noto CJK and Noto
Color Emoji are installed on ours — or when the image is made inside a workflow tool: the
n8n node has a Generate Image operation that returns the PNG as
binary on the item.
It costs what a PDF costs: one credit per image, 10 a month on the free plan, 5,000 for $9 on Starter. A failed render is refunded.