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 #

FieldDefaultNotes
typepngpng or jpeg. Anything else, WebP included, is refused with invalid_option.
quality85JPEG only, 1–100.
width / height1280 / 800The viewport. height only bounds the image when fullPage is off.
fullPagetrueCapture the whole scrollable page. Turn it off for fixed-size cards.
deviceScaleFactor2Pixel density. 2 doubles both dimensions.
omitBackgroundfalseTransparent instead of white — PNG only, in practice.
outputbinarybinary, 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"} returns unsupported_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" returns invalid_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.

Try it #

Get a free API key   Image reference