DEV Community

Boehner
Boehner

Posted on

Generate a PDF from Any URL in Node.js — No Puppeteer, No Headless Browser

Generating PDFs from web pages is one of those tasks that looks simple until you're knee-deep in Puppeteer configuration.

You install the package. You launch a browser. You call page.goto(), wait for networkidle2, call page.pdf(), handle the timeout, close the browser, write the file. And that's the happy path — before you've dealt with the Docker sandbox flags, the 300MB Chromium binary in CI, or the memory leak that shows up on page three.

There's a cleaner way.

The SnapAPI approach

SnapAPI's /v1/pdf endpoint converts any URL to a PDF on our infrastructure. One HTTP call. No browser to manage.

// npm install snapapi-sdk
const snap = require('snapapi-sdk');
const fs   = require('fs');

const api = new snap.SnapAPI('YOUR_KEY'); // or set SNAPAPI_KEY env var

async function generatePDF(url, outputPath) {
  const pdf = await api.pdf({
    url,
    format:           'A4',
    print_background: true,  // include CSS background colors/images
    margin_top:       20,
    margin_bottom:    20,
    margin_left:      20,
    margin_right:     20,
  });
  fs.writeFileSync(outputPath, pdf);
  console.log(`Saved: ${outputPath} (${(pdf.length / 1024).toFixed(1)} KB)`);
}

generatePDF('https://github.com', 'github.pdf');
Enter fullscreen mode Exit fullscreen mode

That's it. The PDF renders from a real Chromium browser on our side — full CSS support, JavaScript execution, web fonts, the works.

Real use cases

Invoice generation — render your invoice HTML template to a URL, call the PDF endpoint, attach the result to a transactional email. No PDF library, no layout math, no font embedding — just HTML you already know how to write.

Report exports — convert any dashboard or analytics page to PDF on demand. Charts rendered by JavaScript frameworks (Chart.js, D3, Recharts) are captured correctly because Chromium waits for the page to fully settle before generating the PDF.

Web archiving — preserve live pages as PDFs for compliance, audit trails, or research. Timestamped captures that reflect exactly what a visitor would have seen.

Documentation — auto-generate PDF versions of your docs on every deploy. Link them from your site for users who prefer offline reading.

Full working script with error handling

const snap = require('snapapi-sdk');
const fs   = require('fs');
const path = require('path');

const api = new snap.SnapAPI(process.env.SNAPAPI_KEY);

async function urlToPDF(url, options = {}) {
  const defaults = {
    format:           'A4',      // A4, Letter, A3, A5, Legal, Tabloid
    landscape:        false,
    print_background: true,
    margin_top:       20,
    margin_bottom:    20,
    margin_left:      20,
    margin_right:     20,
    scale:            1.0,       // 0.1–2.0 — use 0.8 to shrink dense content
  };

  const pdf = await api.pdf({ url, ...defaults, ...options });

  const filename = new URL(url).hostname + '-' + Date.now() + '.pdf';
  const filepath = path.join(process.cwd(), filename);
  fs.writeFileSync(filepath, pdf);

  return { path: filepath, size: pdf.length };
}

// Usage
async function main() {
  // Basic A4
  let result = await urlToPDF('https://github.com');
  console.log(`A4 PDF: ${result.path} (${(result.size / 1024).toFixed(1)} KB)`);

  // Landscape Letter — good for wide tables or dashboards
  result = await urlToPDF('https://example.com', { format: 'Letter', landscape: true });
  console.log(`Landscape PDF: ${result.path}`);

  // Minimal margins — maximize content area
  result = await urlToPDF('https://example.com', { margin_top: 0, margin_bottom: 0, margin_left: 0, margin_right: 0 });
  console.log(`No-margin PDF: ${result.path}`);
}

main().catch(console.error);
Enter fullscreen mode Exit fullscreen mode

Three tips for better PDFs

1. Use print_background: true for branded output. The default in most browsers is to skip background colors and images when printing. If your page uses a dark header, a colored sidebar, or background-image for layout — you need print_background: true or the PDF will look broken.

2. Add delay for pages with lazy-loaded content. If your page loads charts or images after the initial render, add a delay parameter (delay: 2000 for 2 seconds) to give the browser time to finish rendering before the PDF is captured.

3. Use scale to fit dense content. If a page has wide tables or tight layouts that clip at the margins, scale: 0.85 shrinks everything proportionally to fit the page width. Much cleaner than fighting with print media queries.

Free API key

100 PDF generations/month, no credit card, active in 30 seconds.

snapapi.tech/start

Top comments (0)