Back to Blog
Guide February 26, 2026

How to generate a PDF from a URL in Node.js

Generate a PDF from any URL in Node.js with one fetch call. No Puppeteer, no Chromium binary, no headless browser process to manage.

How to Generate a PDF from a URL in Node.js

The standard approach to PDF generation in Node.js: spin up Puppeteer, launch a Chromium process, navigate to the URL, call page.pdf(), handle memory leaks, then clean up the browser. That's 50+ lines of lifecycle management for a file conversion.

An API call does the same thing in 10:

import fs from 'fs';

const res = await fetch('https://api.pagebolt.dev/v1/pdf', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.PAGEBOLT_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://example.com/report',
    format: 'A4',
    printBackground: true,
    blockBanners: true
  })
});

const buffer = Buffer.from(await res.arrayBuffer());
fs.writeFileSync('output.pdf', buffer);

No dependencies beyond Node's built-in fetch (Node 18+). No browser process, no memory management.

Common options

{
  url: 'https://example.com',
  format: 'A4',           // A4, Letter, Legal, A3
  printBackground: true,  // include CSS backgrounds
  margin: {
    top: '20mm',
    bottom: '20mm',
    left: '15mm',
    right: '15mm'
  },
  landscape: false,
  blockBanners: true,     // strip cookie consent popups
  blockAds: true
}

Generate from raw HTML

If you're building invoices, reports, or receipts from templates, pass HTML directly instead of a URL:

const html = `
  <html>
    <body style="font-family: sans-serif; padding: 40px">
      <h1>Invoice #1042</h1>
      <p>Amount due: $299.00</p>
    </body>
  </html>
`;

const res = await fetch('https://api.pagebolt.dev/v1/pdf', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.PAGEBOLT_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ html, format: 'A4', printBackground: true })
});

In an Express route

app.get('/invoice/:id', async (req, res) => {
  const invoiceHtml = await buildInvoiceHtml(req.params.id);

  const pdfRes = await fetch('https://api.pagebolt.dev/v1/pdf', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.PAGEBOLT_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ html: invoiceHtml, format: 'A4', printBackground: true })
  });

  const buffer = Buffer.from(await pdfRes.arrayBuffer());

  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="invoice-${req.params.id}.pdf"`);
  res.send(buffer);
});

In AWS Lambda

The same code runs without modification in Lambda — no Chromium layer, no @sparticuz/chromium, no binary compatibility issues. The browser runs on PageBolt's infrastructure.

What you skip vs Puppeteer

Puppeteer PageBolt API
Browser binary Required (~300MB) None
Memory per request 200-500MB Negligible
Cold start 5-10s Milliseconds
Concurrent PDFs Complex pool mgmt Fire-and-forget
Deployment size Large Kilobytes

Try it free — 100 requests/month, no credit card. → pagebolt.dev

Get Started Free

100 requests/month, no credit card

Screenshots, PDFs, video recording, and browser automation — no headless browser to manage.

Get Your Free API Key →