---
title: "Stop Wrestling with Puppeteer: Generate PDFs with One API Call"
published: false
description: "How to generate invoices, reports, and documents as PDFs from HTML, Markdown, or JSON data — without managing headless browsers."
tags: webdev, api, node, tutorial
cover_image: # (add a cover image URL)
---

Every developer has the same PDF story. It starts innocently:

> "Can we add a 'Download as PDF' button?"

Three days later, you're debugging Chrome memory leaks in production, your Puppeteer instance is eating 2GB of RAM, and the PDFs look different on every server.

I got tired of this cycle, so I built [DocFast](https://docfast.dev) — a PDF API that takes HTML, Markdown, or structured data and returns a PDF. No headless browser setup, no dependencies.

Here's what I learned, and how you can generate PDFs in about 30 seconds.

## The Simple Version

```bash
curl -X POST https://docfast.dev/v1/convert/html \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello World</h1><p>This is a PDF.</p>"}' \
  -o hello.pdf
```

That's it. JSON in, PDF out.

## Three Ways to Generate PDFs

DocFast has three conversion endpoints, each for a different use case:

### 1. HTML → PDF

You have full control. Pass any HTML with optional CSS:

```javascript
const response = await fetch('https://docfast.dev/v1/convert/html', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    html: `
      <h1 style="color: navy;">Monthly Report</h1>
      <p>Revenue: <strong>$12,345</strong></p>
      <table>
        <tr><td>Users</td><td>1,234</td></tr>
        <tr><td>MRR</td><td>$5,670</td></tr>
      </table>
    `,
    css: 'body { font-family: Georgia, serif; padding: 40px; }',
    format: 'A4'
  })
});

const pdf = await response.arrayBuffer();
fs.writeFileSync('report.pdf', Buffer.from(pdf));
```

### 2. Markdown → PDF

This is my favorite endpoint. Write Markdown, get a styled PDF with syntax highlighting:

```javascript
const markdown = `
# Project Proposal

## Overview
We propose building a **real-time dashboard** for monitoring API usage.

## Timeline
| Phase | Duration | Cost |
|-------|----------|------|
| Design | 2 weeks | $3,000 |
| Build | 4 weeks | $8,000 |
| Test | 1 week | $1,500 |

## Code Sample
\`\`\`javascript
const analytics = new Analytics({ key: 'abc123' });
analytics.track('pdf_generated', { format: 'A4' });
\`\`\`
`;

const response = await fetch('https://docfast.dev/v1/convert/markdown', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ markdown })
});
```

Tables, code blocks, images — it all renders beautifully.

### 3. URL → PDF

Need to snapshot a webpage? Just pass the URL:

```bash
curl -X POST https://docfast.dev/v1/convert/url \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "waitUntil": "networkidle0"}' \
  -o page.pdf
```

It handles JavaScript-rendered pages too (SPAs, dashboards, etc).

## The Killer Feature: Invoice Templates

This is what I wish existed years ago. Instead of building invoice HTML from scratch, you pass JSON data and get a professional invoice:

```javascript
const response = await fetch('https://docfast.dev/v1/templates/invoice/render', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    data: {
      invoiceNumber: 'INV-2026-042',
      date: '2026-02-14',
      dueDate: '2026-03-14',
      from: {
        name: 'Your Company',
        address: '123 Main St',
        email: 'billing@yourco.com'
      },
      to: {
        name: 'Client Corp',
        address: '456 Oak Ave',
        email: 'ap@client.com'
      },
      items: [
        { description: 'Web Development', quantity: 40, unitPrice: 95, taxRate: 20 },
        { description: 'Hosting', quantity: 1, unitPrice: 29 }
      ],
      currency: '€',
      notes: 'Payment due within 30 days.',
      paymentDetails: 'IBAN: AT12 3456 7890 1234 5678'
    }
  })
});
```

No HTML. No CSS. Just data → PDF.

## Integrating Into Your App

Here's a minimal Express.js endpoint that generates and returns an invoice:

```javascript
app.get('/api/invoice/:id', async (req, res) => {
  const invoice = await db.getInvoice(req.params.id);

  const pdfResponse = await fetch('https://docfast.dev/v1/templates/invoice/render', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.DOCFAST_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ data: invoice })
  });

  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename=invoice-${invoice.invoiceNumber}.pdf`);
  pdfResponse.body.pipe(res);
});
```

12 lines. Your users can download invoices.

## Why Not Just Use Puppeteer?

You can! But here's what you're signing up for:

- **Memory:** Each Chrome instance uses 200-500MB RAM
- **Cold starts:** Launching a browser takes 1-3 seconds
- **Stability:** Chrome crashes, zombie processes, OOM kills
- **DevOps:** Managing Chrome versions, fonts, dependencies in Docker
- **Concurrency:** Need multiple PDFs at once? Now you're managing a browser pool

DocFast handles all of this. Persistent browser pool, sub-second generation, no infrastructure to manage.

## Pricing

- **Free:** 100 PDFs/month, all endpoints, all templates
- **Pro:** $9/month for 10,000 PDFs/month

No per-page fees. No credit card for the free tier. [Get your API key here](https://docfast.dev).

---

If you have questions about the API or want to see specific templates added, drop a comment. I'm actively building based on user feedback.

Happy PDF-ing! 📄
