Back to Blog
Guide March 2, 2026

PDF generation from HTML without managing servers

Generate PDFs from HTML without wkhtmltopdf or managing infrastructure. REST API with headers/footers, fonts, custom styling.

Your app generates invoices. Estimates. Contracts. Shipping labels. Every document becomes a PDF.

Currently: You run wkhtmltopdf on your server. It works... until it doesn't.

Wednesday 3 PM: wkhtmltopdf process hangs on a malformed HTML file. All PDF generation freezes. Customers can't download invoices. Support tickets pile up. By 5 PM, you're SSH'ing into production servers, killing processes manually.

There's a better way. A REST API that generates PDFs from HTML. No server to manage. No hanging processes. No complexity.

The self-hosted PDF generation trap

Most developers start with wkhtmltopdf or similar CLI tools:

const { execSync } = require('child_process');
const fs = require('fs');

function generatePDF(html, filename) {
  fs.writeFileSync('/tmp/input.html', html);

  execSync(`wkhtmltopdf /tmp/input.html /tmp/${filename}.pdf`, {
    timeout: 30000
  });

  const pdf = fs.readFileSync(`/tmp/${filename}.pdf`);
  fs.unlinkSync('/tmp/input.html');
  fs.unlinkSync(`/tmp/${filename}.pdf`);

  return pdf;
}

Looks simple. In production: nightmare.

Hidden costs

Process management

  • wkhtmltopdf hangs on malformed HTML → timeout → manual restart
  • No error recovery → customers stuck waiting
  • Requires /tmp cleanup (disk space issues)
  • System resource limits (ulimits) need tuning

Reliability

  • Font rendering differs by system setup
  • Timeouts are silent failures
  • Large files OOM the process
  • No graceful degradation

Scaling

  • Each PDF needs a new process spawn (~2 seconds)
  • Can't run 100 PDFs/second without horizontal scaling
  • Vertical scaling hits ceiling

Real cost: $50–100/month infrastructure + 3–5 hours DevOps = ~$700/month effective cost.

Solution: Hosted PDF API

Call an endpoint. Get a PDF back. That's it.

curl -X POST https://pagebolt.dev/api/v1/pdf \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Invoice #12345</h1><p>Total: $99.99</p>",
    "format": "A4",
    "margin": "1in"
  }' \
  --output invoice.pdf

No infrastructure. No process management. No timeouts.

Before and after

Self-hosted approach

const express = require('express');
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

app.post('/invoice/:invoiceId', (req, res) => {
  try {
    const html = req.body.html;
    const filename = `invoice-${req.params.invoiceId}`;
    const tmpInput = path.join('/tmp', `${filename}.html`);
    const tmpOutput = path.join('/tmp', `${filename}.pdf`);

    fs.writeFileSync(tmpInput, html);

    // This can hang, timeout, or fail silently
    execSync(`wkhtmltopdf --margin-top 10 --margin-bottom 10 ${tmpInput} ${tmpOutput}`, {
      timeout: 30000,
      stdio: 'pipe'
    });

    const pdf = fs.readFileSync(tmpOutput);
    fs.unlinkSync(tmpInput);
    fs.unlinkSync(tmpOutput);

    res.set('Content-Type', 'application/pdf');
    res.set('Content-Disposition', `attachment; filename="${filename}.pdf"`);
    res.send(pdf);
  } catch (error) {
    console.error('PDF generation failed:', error);
    res.status(500).send('PDF generation failed');
  }
});

Cost: $50–100/month infrastructure + 3–5 hours DevOps = ~$700/month effective.

Hosted API approach

app.post('/invoice/:invoiceId', async (req, res) => {
  try {
    const response = await fetch('https://pagebolt.dev/api/v1/pdf', {
      method: 'POST',
      headers: {
        'x-api-key': process.env.PAGEBOLT_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        html: req.body.html,
        format: 'A4',
        margin: '1in'
      })
    });

    const pdf = Buffer.from(await response.arrayBuffer());

    res.set('Content-Type', 'application/pdf');
    res.set('Content-Disposition', `attachment; filename="invoice-${req.params.invoiceId}.pdf"`);
    res.send(pdf);
  } catch (error) {
    res.status(500).send('PDF generation failed');
  }
});

Cost: $29/month (Starter, 5,000 PDFs/month), $0 infrastructure, $0 DevOps = $29/month total.

Real example: Invoice generation + email

E-commerce platform: generate invoice PDF, email to customer, archive to S3.

const nodemailer = require('nodemailer');
const AWS = require('aws-sdk');

async function generateAndEmailInvoice(order) {
  const html = `
    <h1>Invoice</h1>
    <p>Order ID: ${order.id}</p>
    <table>
      ${order.items.map(item => `
        <tr>
          <td>${item.name}</td>
          <td>$${item.price}</td>
        </tr>
      `).join('')}
    </table>
    <p>Total: $${order.total}</p>
  `;

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

  // Email to customer
  await nodemailer.createTransport({/* ... */}).sendMail({
    from: 'noreply@mystore.com',
    to: order.customer.email,
    subject: `Invoice for Order #${order.id}`,
    text: 'Your invoice is attached.',
    attachments: [{ filename: `invoice-${order.id}.pdf`, content: pdf }]
  });

  // Archive to S3
  await new AWS.S3().putObject({
    Bucket: 'invoices-archive',
    Key: `${order.id}.pdf`,
    Body: pdf
  }).promise();

  return { success: true, invoiceId: order.id };
}

Simple, reliable, no process management.

Comparison: wkhtmltopdf vs hosted API

FactorwkhtmltopdfHosted API
Setup1–2 hours10 minutes
Infra cost$50–100/month$0
DevOps time3–5 hours/month0 hours
Latency2–5 seconds1–2 seconds
Reliability99% (hangs, timeouts)99.9% SLA
ScalingCapped at serverUnlimited
Font supportSystem-dependentConsistent
Custom stylingLimitedFull CSS3
Per-PDF cost$0.05–0.10 (infra)$0.02–0.05 (API)

When to use hosted PDF API

✅ User-facing invoice/receipt generation
✅ Automated document pipelines
✅ Report generation at scale
✅ Limited DevOps resources
✅ Unpredictable volume

Keep wkhtmltopdf if:

  • You process 10,000+ PDFs/day (break-even at scale)
  • You have dedicated DevOps
  • Documents require extreme customization not achievable via HTML/CSS

For most teams: hosted wins.

Getting started

  1. Sign up at pagebolt.dev (free: 100 requests/month)
  2. Get API key from your dashboard (1 minute)
  3. Make your first PDF (5 minutes)
  4. Evaluate: Does it solve your use case?

No more hanging processes. No more 3 AM PDF generation failures.

Replace wkhtmltopdf in 10 minutes

Free tier: 100 PDFs/month. No credit card. No servers to manage.