If you've ever tried to "just turn this page into a PDF on the server," you probably spent a Saturday on it.
Headless Chromium works — until it doesn't. CSS backgrounds disappear. Custom fonts come out as squares. The Lambda cold-starts a fresh Chromium each invocation and the first three PDFs of the morning take eleven seconds. By the time the PDF looks right and renders fast, you've shipped half a stack to keep one Chromium process happy.
So we made a smaller version of that problem: POST a URL, get a PDF back.
curl -X POST https://api.numerixlabs.com/screenshot \
-H "Authorization: Bearer $SNAPAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/invoice/42","format":"pdf"}' \
-o invoice-42.pdf
That's the whole API. No render queue, no font upload pipeline, no Chromium process to babysit.
What "publication-quality" means here
It's easy to render a PDF that looks right in a viewer and falls apart when you print it. Three things usually fail:
-
CSS background images and colours drop. Chromium's default
printBackgroundisfalse— so the PDF you generate at midnight will not have your brand colour fills, even though the screenshot endpoint shows them. We turnprintBackgroundon by default, so aformat=pdfresponse now matches the on-screen render byte-for-byte across backgrounds, gradient fills, and tinted table rows. (This was the last regression we shipped a fix for; we hit it ourselves on the invoice path.) -
Web fonts don't load before the page is captured. We wait for
networkidle2(≤2 long-lived connections — the pragmatic stopping point for analytics-heavy pages that never go fully idle) before snapshotting, so@font-faceresolution finishes before the render pass. No squares. -
Default A4 page size, full background fidelity.
format=pdfrenders at A4 withprintBackground: true, so brand colour fills, gradient backgrounds, and tinted table rows survive into the file. Chromium emulates@media printby default during PDF generation — so if your page has a print stylesheet, you'll get the print layout in the PDF. If you've designed the page screen-only, the on-screen layout is what you'll get.
The combination is what we mean by "publication-quality." Not "renders without errors" — looks the same as the browser, every time, including the bits that print media queries traditionally murder.
Common use cases we've seen
- Invoices and receipts. Render your existing HTML invoice page; ship the PDF as the email attachment. Removes the entire "build a separate PDF templating layer" project.
- Reports. Dashboards or analytics views that exist as live HTML — once a month, snapshot them.
- Compliance / audit captures. Snapshot a page state at a specific moment with a stable hash you can refer to later.
- Newsletter archives. Render the issue at send-time and store the PDF.
What the request looks like
The minimum request is a URL and a format. Everything else has sensible defaults.
POST https://api.numerixlabs.com/screenshot
{
"url": "https://example.com/invoice/42",
"format": "pdf"
}
You can override the viewport width, add a post-load delay, clip to a CSS selector, capture full-page or above-fold, and pass custom headers and cookies — the docs cover the full surface. The default behaviour is "do the polite thing": full-page capture at 1280px, wait for networkidle2, no delay.
Why we built it like this
A POST-with-a-URL is the smallest possible API that does the job. We chose it over a "PDF SDK with options for every PDF rendering library" because the same shape solves a lot of nearby problems (PNG, JPEG, webhooks for delivery later) without adding more verbs.
Same shape, same retry policy, same auth — different format.
Try it
Free trial is one POST away — get a key at numerixlabs.com. If you're already on Puppeteer-in-a-Lambda and the cold-start latency is hurting you, that's the migration we'd love to talk about.
If format=pdf doesn't match what you see in your browser, mail us — that's a bug, not a "feature request," and we want to know.
— Built by NumerixLabs.
Top comments (0)