PDFPipe

Use case

Invoice PDF API

Generate print-quality invoice PDFs from HTML with a single POST request. No SDK, no browser to run, no template editor to learn.

Your template, your design

Send any HTML: a Handlebars or Jinja2 template you already write for email, a React component rendered to a string, or a static file. The renderer runs a full browser so CSS Grid, Flexbox, custom fonts, and gradients all work exactly as in the browser preview.

One HTTP call, raw bytes back

POST the HTML, get the PDF. No SDK, no webhook to poll, no intermediate format. Every language with an HTTP client works.

Refund on failure

If the render times out or fails, the document is not counted against your quota. Retry safely.

Store and email a link

Add store: true to get a stable download URL instead of bytes. Email it as a link in the invoice notification. Documents are retained based on your plan (1 day free, 30 days Starter, 1 year Growth+).

Password-protected PDFs

Paid plans can add password protection with the options.password field. The PDF requires a password to open.

Accurate page breaks

Rendering runs under print emulation, so break-inside: avoid and @page rules are honored. Long item tables do not split mid-row.

1. Write the HTML template

Any HTML template engine works. Here is a Handlebars template that covers a typical invoice layout: logo, billing address, line items, and total.

templates/invoice.html
<!-- templates/invoice.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <style>
    body { font-family: system-ui, sans-serif; color: #1a1a1a; margin: 0; padding: 40px; }
    .header { display: flex; justify-content: space-between; align-items: start; }
    .logo { font-size: 24px; font-weight: 700; }
    .meta { text-align: right; font-size: 13px; color: #666; }
    .invoice-number { font-size: 28px; font-weight: 700; margin: 32px 0 8px; }
    .bill-to { margin: 24px 0; }
    table { width: 100%; border-collapse: collapse; margin: 32px 0; }
    th { text-align: left; padding: 8px 12px; background: #f5f5f5; font-size: 12px; }
    td { padding: 10px 12px; border-bottom: 1px solid #eee; font-size: 13px; }
    .total-row td { font-weight: 700; border-top: 2px solid #1a1a1a; border-bottom: none; }
    .footer { font-size: 12px; color: #888; margin-top: 48px; }
  </style>
</head>
<body>
  <div class="header">
    <div class="logo">Acme Corp</div>
    <div class="meta">
      <div>Invoice date: {{invoiceDate}}</div>
      <div>Due: {{dueDate}}</div>
    </div>
  </div>
  <div class="invoice-number">Invoice #{{invoiceNumber}}</div>
  <div class="bill-to">
    <div style="font-size:12px;color:#888;margin-bottom:4px">Bill to</div>
    <div>{{customer.name}}</div>
    <div>{{customer.email}}</div>
    <div>{{customer.address}}</div>
  </div>
  <table>
    <thead>
      <tr><th>Description</th><th>Qty</th><th>Unit price</th><th>Amount</th></tr>
    </thead>
    <tbody>
      {{#each items}}
      <tr>
        <td>{{description}}</td>
        <td>{{quantity}}</td>
        <td>${{unitPrice}}</td>
        <td>${{amount}}</td>
      </tr>
      {{/each}}
    </tbody>
    <tfoot>
      <tr class="total-row">
        <td colspan="3">Total due</td>
        <td>${{total}}</td>
      </tr>
    </tfoot>
  </table>
  <div class="footer">Payment terms: net 30. Thank you for your business.</div>
</body>
</html>

2. Compile and POST (Node.js)

invoice.ts
// Node.js: compile template and call the API in one function

import Handlebars from "handlebars";
import { readFileSync } from "fs";

const tmpl = Handlebars.compile(readFileSync("./templates/invoice.html", "utf-8"));

export async function generateInvoicePdf(data: {
  invoiceNumber: string;
  invoiceDate: string;
  dueDate: string;
  customer: { name: string; email: string; address: string };
  items: { description: string; quantity: number; unitPrice: number; amount: number }[];
  total: number;
}): Promise<Uint8Array> {
  const html = tmpl(data);

  const resp = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PDFPIPE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      html,
      options: {
        format: "A4",
        margin: { top: "20mm", bottom: "20mm", left: "20mm", right: "20mm" },
        print_background: true,
      },
    }),
  });

  if (!resp.ok) throw new Error(`PDF generation failed: ${resp.status}`);
  return new Uint8Array(await resp.arrayBuffer());
}

2. Compile and POST (Python)

invoice.py
# Python: Jinja2 template + httpx

import httpx
from jinja2 import Environment, FileSystemLoader

jinja = Environment(loader=FileSystemLoader("templates"))

def generate_invoice_pdf(data: dict) -> bytes:
    html = jinja.get_template("invoice.html").render(**data)

    resp = httpx.post(
        "https://api.pdfpipe.xyz/v1/pdf",
        headers={"Authorization": f"Bearer {settings.PDFPIPE_API_KEY}"},
        json={
            "html": html,
            "options": {
                "format": "A4",
                "margin": {"top": "20mm", "bottom": "20mm", "left": "20mm", "right": "20mm"},
                "print_background": True,
            },
        },
        timeout=60.0,
    )
    resp.raise_for_status()
    return resp.content

Store the PDF and return a download link

Pass store: true to get a stable URL back instead of raw bytes. Useful when you want to include a download link in the invoice email rather than attaching the file directly.

Node.js
// Store the invoice and return a download URL — useful for emailing links

const resp = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PDFPIPE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    html,
    store: true,                        // returns JSON with document_url instead of raw bytes
    filename: `invoice-${invoiceId}.pdf`,
    options: { format: "A4" },
  }),
});

const { document_url, document_id } = await resp.json();
// Send document_url in the invoice email — link works for up to 30 days (Starter plan)

Pricing

PlanPriceInvoices / monthOverage
HobbyFree500Not available
Starter$193,000Not available
Growth$4915,000Not available
Scale$14950,000Not available
Business$499100,000Contact sales

Start generating invoices

500 invoices a month free. No credit card. Key issued instantly after signup.

Related guide

PDF in Node.js →

Related guide

PDF in Python →

Related guide

PDF in Next.js →