← All comparisons
vs Puppeteer / headless Chromium

Puppeteer vs makesPDF — alternative to headless Chrome PDFs

We don't print HTML. We render structure.

For: Backend teams running headless Chrome just to spit out a PDF.

Puppeteer / headless Chromium

The one-line difference

Puppeteer (and Playwright, and chrome --headless) drive a real Chromium to render an HTML page and then ask the browser to print it to PDF. makesPDF is a hosted REST API with its own deterministic layout engine: you POST markdown or a compact DSL string and get back a PDF that's PDF/A-2A archival + PDF/UA-1 accessible by construction. No Chromium in your container, no fonts to install, no Xvfb, no flaky CI.

Feature matrix

Puppeteer / headless Chrome makesPDF
Shape Library + a 200MB Chromium binary Hosted REST API (/api/v1/*)
Authoring surface HTML + CSS + JS Markdown, builder DSL, JSON
Runtime Your server (Node + Chromium) Cloudflare Workers (we host)
Cold start 500–2000ms (browser launch) <100ms (Worker)
Container size +200MB for Chromium 0 — you call an HTTP endpoint
Memory per render 200–500MB bounded, single Worker request
Tagged PDF (PDF/UA-1) Partial, lossy via "Tagged PDF" pref Yes, by default
PDF/A archival No (third-party post-processor) Yes (PDF/A-2A by default)
veraPDF-validated output No Yes
Determinism Subject to Chromium version drift Byte-stable across versions
Agent-first No Yes — public skill file, MCP server, x402-payable
License Apache-2.0 (browser proprietary) Proprietary hosted; skill file MIT

Same input, two outputs

A "Hello, Ada" report.

Puppeteer — spin up a browser, load HTML, print:

import puppeteer from "puppeteer";

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(`
  <!doctype html>
  <html><body style="font-family: sans-serif; padding: 40px;">
    <h1>Hello, Ada</h1>
    <p>Welcome to the report.</p>
  </body></html>
`);
await page.pdf({ path: "hello.pdf", format: "A4" });
await browser.close();

makesPDF — one HTTP call, no browser:

curl -X POST https://makespdf.com/api/v1/md \
  -H "Authorization: Bearer $MAKESPDF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"markdown": "# Hello, Ada\n\nWelcome to the report."}' \
  -o hello.pdf

The Puppeteer output is a PDF whose structure tree (if you remembered to enable it) is whatever Chromium decided to emit from your DOM — usually a flat run of Span elements with no real heading semantics, plus all your decorative <div>s tagged as if they were content. The makesPDF output is a tagged PDF with proper H1 / P structure elements you can verify by opening Acrobat's Tags pane.

Why people switch

  • Chromium is a liability in your runtime. A PDF endpoint shouldn't ship 200MB of browser, a font cache, a sandbox config, and a memory ceiling that pages OOM under bursty load. Hosted means none of that lives in your image.
  • HTML→PDF is the wrong abstraction for compliance. Chromium's "Tagged PDF" output is a best-effort transform of your DOM. It tags decorative divs, misses heading levels, and still requires manual remediation to pass a PDF/UA-1 audit. We render straight from semantic structure (h1, p, table, figure) into a tagged tree, then validate with veraPDF.
  • Determinism. Same input → same bytes. Chromium silently changes layout between versions; your "stable" PDF endpoint isn't. makesPDF's renderer is a single owned pipeline — output is byte-stable across deploys.
  • Cold start. Launching a browser per request is 500ms+ even pooled. A Worker round-trip is <100ms.
  • Agents need an API, not a browser. Asking an LLM to "generate HTML + CSS that prints to a perfect A4" is the long way around. Asking it to emit markdown or a 30-token DSL string is the short way.

When Puppeteer is still the right call

  • You're rendering an existing web page exactly as users see it (snapshot of a dashboard, a printable receipt that's already a polished web view). HTML→PDF is literally the job.
  • You need arbitrary CSS features we don't support — complex CSS grid layouts, web fonts loaded from CSS @font-face, JS-driven canvases.
  • You're in a regulated environment that forbids egress to a hosted service. Self-hosted Chromium stays in your VPC.

For most server-side or agent-driven document workloads, the hosted endpoint wins on container size, compliance, cold start, and operational surface area.

Migration

Most "Puppeteer that prints HTML to PDF" code paths are rendering a templated HTML string. Those map cleanly to markdown + the /api/v1/md endpoint, or to the builder DSL when you need precise layout (tables, multi-column, headers/footers). See the skill file for the DSL function reference and a recipe per document type.