pdfkitt vs Puppeteer: HTML to PDF Without the Headache

Puppeteer is a great library — until it becomes your full-time job. Here is how self-hosting stacks up against an API built for production PDFs.

The problem with self-hosted Puppeteer

Running headless Chromium on your own servers looks simple in a tutorial. In production it is a different story. Each browser instance consumes significant RAM — often hundreds of megabytes per tab — and spikes under concurrent renders. CPU contention slows the rest of your app when invoices, reports, and exports all hit at once.

Crashes and stuck processes are routine: zombie Chromium, out-of-memory kills, and CI breaking when Chrome revisions change. Then there is ongoing maintenance: patching browsers, tuning launch flags, managing fonts and locales, hardening sandboxes, and building retries and timeouts so one bad HTML payload does not take down a worker.

Side-by-side: setup, cost, maintenance, scaling

Self-hosted Puppeteerpdfkitt API
Setup timeHours to days — Chromium images, fonts, flags, sandboxing, CIMinutes — sign up, copy an API key, send a POST request
CostAlways-on RAM/CPU, storage, plus ongoing engineering timePredictable monthly plans; generous free tier (1,000 PDFs/mo)
MaintenanceChrome updates, crash recovery, memory leaks, security patchesFully managed infrastructure and rendering pool
ScalingYou provision workers, queues, retries, and rate limitsScales with your plan; rate limits and quotas documented upfront

Code: fifteen lines of plumbing vs three lines to ship

A minimal Puppeteer flow ignores pooling, health checks, and failure modes — but it is already more code than calling an API.

Puppeteer (typical setup)

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  headless: true,
  args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
});
try {
  const page = await browser.newPage();
  await page.setContent(html, { waitUntil: "networkidle0", timeout: 30_000 });
  const pdf = await page.pdf({ format: "A4", printBackground: true });
  // Production: add pooling, logging, retries, and metrics here
  await fs.promises.writeFile("out.pdf", pdf);
} finally {
  await browser.close();
}

pdfkitt (one request)

curl -X POST https://api.pdfkitt.dev/v1/convert \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"html":"<h1>Hello</h1>"}' --output out.pdf

When Puppeteer still makes sense

Self-hosting is the right tool when you need maximum control: heavily customized Chromium builds, exotic DevTools protocol workflows, or features outside PDF generation — for example full interactive browser automation, screenshots in many formats, or air-gapped / on-prem environments where outbound API calls are not allowed.

If your goal is reliable HTML → PDF for invoices, reports, and user exports, an API trades flexibility you do not need for uptime you do.

Try pdfkitt free — 1,000 PDFs/month, no credit card

Get an API key and send your first PDF in minutes.