HTML to PDF, over an API

A URL or raw markup in, a PDF out, in one call. Page ranges, real header and footer templates, document metadata — and the four things about printing from a browser that catch everyone out.

The smallest version

url to pdf
curl -X POST https://api.getsnap.dev/v1/screenshot \
  -H "X-API-Key: sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "format": "pdf" }'

That is the whole thing. A4, portrait, 20px margins, backgrounds printed. Everything below is about the cases where those defaults are wrong.

From markup you already have

Most PDF work is not "turn this public page into a PDF" — it is an invoice, a report or a certificate rendered from a template you hold. You do not need to host it first. Send the markup instead of a URL, up to 500,000 characters:

html to pdf
{
  "html": "<h1>Invoice 2026-0416</h1><p>Due 14 November</p>",
  "format": "pdf",
  "pdf_page_size": "A4"
}

Stylesheets, images and web fonts referenced by absolute URL are still fetched, so a template that pulls in Inter from a CDN renders with Inter. Relative URLs have nothing to resolve against, so make them absolute.

1. Your backgrounds disappear

Chromium does not print background colours or images by default — it is a browser, and the assumption is that you are about to spend ink. A PDF of a page with a dark header comes back with a white one, and the usual reaction is that the renderer is broken.

This API sets pdf_background to true by default because a PDF is a document rather than a print job. If you are driving Chromium yourself, that is printBackground: true and it is off until you say so.

2. The page looks nothing like your browser

Printing switches the page to its print stylesheet. Every rule inside an @media print block applies, and anything scoped to screen stops. For an invoice that is exactly right. For a marketing page with a nav bar hidden at print time it is a surprise.

media type
{ "url": "...", "format": "pdf", "media_type": "screen" }

"screen" forces the on-screen rendering. "print" forces the print one even where a browser might not. Left empty, the page gets whatever Chromium would normally use when printing.

3. Your header is there but invisible

This one costs people an afternoon. Chromium renders header and footer templates at a font size of zero unless the template sets one itself. The markup is in the document, the element exists, nothing is drawn.

The second half is margins: the header and footer are painted inside the page margin, so a 20px top margin cannot hold a 30px header. Both have to be right before anything appears.

header and footer that actually render
{
  "url": "https://example.com/report",
  "format": "pdf",
  "pdf_show_header": true,
  "pdf_header": "<div style='font-size:10px; width:100%; padding:0 15mm; color:#666'>Quarterly report</div>",
  "pdf_show_footer": true,
  "pdf_footer": "<div style='font-size:10px; width:100%; padding:0 15mm; text-align:right; color:#666'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
  "pdf_margin_top": "25mm",
  "pdf_margin_bottom": "20mm"
}

Chromium fills five special classes for you inside those templates: pageNumber, totalPages, date, title and url. Put an empty <span> with the class where you want the value and it is substituted per page.

4. The document has no title

Open a generated PDF and most viewers show the filename, or nothing, where the title should be. That is because Chromium writes no document metadata at all — no title, author, subject or keywords. It cannot: those live in the PDF trailer, which is written after rendering finishes.

So they have to be applied to the finished document as a second step. That is the reason most screenshot APIs do not offer them, and why this one does:

document metadata
{
  "url": "https://example.com/invoice/2026-0416",
  "format": "pdf",
  "pdf_title": "Invoice 2026-0416",
  "pdf_author": "Example Ltd",
  "pdf_subject": "Invoice for October 2026",
  "pdf_keywords": "invoice, 2026, october",
  "pdf_creator": "billing-service"
}

It matters more than it sounds for anything that gets filed: a document management system, an email client previewing an attachment, and a search index all read the title before they read a byte of the content.

Page ranges

A long report where you only want the summary, or an invoice where the terms and conditions run to four pages nobody needs. Same syntax a print dialog uses:

page ranges
{ "url": "...", "format": "pdf", "pdf_page_range": "1-3, 8, 11-13" }

Paper, orientation, margins, scale

OptionValuesDefault
pdf_page_sizeA0–A6, Letter, Legal, Tabloid, LedgerA4
pdf_orientationportrait, landscapeportrait
pdf_margin_top and the other threeany CSS length — 20mm, 0.5in, 40px20px
pdf_scale0.1 to 21
pdf_backgroundtrue, falsetrue

pdf_scale is the one worth knowing about: at 0.8 a table that was spilling onto a second page often stops doing so, and it is far less work than rewriting the CSS.

What the other APIs do

Checked against each provider's own documentation on 25 October 2026, and written up with the rest in the sourced comparison.

 Page rangesHeader / footerDocument metadata
getSnap.devyesyesyes
Urlboxyesyesno
ScreenshotAPI.netyesnono
ScreenshotOnenonono
ApiFlashno PDF output at all — PNG, JPEG and WebP only

Margins and paper size are universal; everyone has those. The three columns above are where the field thins out, and all three together is the part we have not found anywhere else.

Frequently asked questions

Why is my header or footer invisible?

Two reasons, usually both at once. Chromium renders those templates at font size zero unless the template sets one, and they are drawn inside the page margin, so a margin smaller than the header leaves it nowhere to go. Set an explicit font-size and a margin at least as tall.

Why are my background colours missing?

Chromium does not print backgrounds by default. This API turns it on; if you are driving the browser yourself, that is printBackground: true.

Why does the PDF look different from the browser?

Printing applies the print stylesheet. Set media_type to screen if you want what you see on screen.

Can I build a PDF from HTML I have not published?

Yes — send it in html instead of url, up to 500,000 characters. Reference assets absolutely so they resolve.

How is this billed?

A PDF is one credit, the same as a screenshot, and a cache hit is free. A report you need every Monday can run on a schedule rather than a cron job you maintain. The free plan is 100 a month and every option on this page works on it — we meter credits, not features.

Render one and look at it

100 PDFs a month, free and permanent, no card. The full option list is in the API reference.

Get a free API key