How to Auto-Generate PDF Invoices with an API

Learn how to auto-generate PDF invoices programmatically using an API. Code examples in Python, Node.js, and curl for invoice automation.

By LightningPDF Team · · 6 min read
How to Auto-Generate PDF Invoices with an API
TL;DR: Use LightningPDF's HTML-to-PDF API to auto-generate branded invoices from your app. Template your invoice in HTML, POST it to the API, and get a pixel-perfect PDF back in under 2 seconds.

How to Auto-Generate PDF Invoices with an API

Every business that sells a product or service needs invoices. What starts as a manageable task with a handful of customers quickly becomes a bottleneck when your business scales to hundreds or thousands of transactions per month. Manually creating invoices in a word processor or spreadsheet is error-prone, slow, and impossible to maintain as order volume grows.

Invoice automation through an API is the process of programmatically generating PDF invoices by combining transaction data from your application with an HTML template, then converting the result into a print-ready PDF document via a REST endpoint.

In this guide, you will learn how to build an invoice template in HTML and CSS, connect it to the LightningPDF API, and generate professional PDF invoices on demand in Python, Node.js, and curl. By the end, you will have a working invoice pipeline that generates pixel-perfect PDFs in under two seconds.

Why Automate Invoice Generation?

The Manual Process Is Broken

If you are still creating invoices by hand, you are likely dealing with some combination of these problems:

  • Time drain: A single invoice takes 5-15 minutes to fill out, format, and export. At 200 invoices per month, that is 25-50 hours of labor.
  • Human error: Typos in amounts, wrong tax rates, duplicate invoice numbers, and missing line items are common when data is entered manually.
  • Inconsistent branding: Different team members produce invoices that look different, undermining your professional image.
  • No audit trail: Manually created documents are hard to track, version, and retrieve when a customer disputes a charge.

Many jurisdictions require businesses to issue invoices that contain specific fields: sequential invoice numbers, tax identification numbers, itemized line items with tax breakdowns, and payment terms. Automating invoice generation ensures every document meets these requirements consistently, reducing the risk of regulatory penalties.

API-Driven Invoicing Solves These Problems

When you generate invoices through an API, your application becomes the source of truth. The transaction data flows directly from your database into the invoice template, eliminating manual data entry. Every invoice follows the same template, uses the correct tax calculations, and receives a sequential number automatically.

Building an Invoice Template in HTML and CSS

The foundation of API-driven invoicing is a well-structured HTML template. Unlike drag-and-drop builders, HTML gives you full control over layout, typography, and responsive design. If you can build a web page, you can build an invoice.

Here is a complete invoice template you can use as a starting point:

<!DOCTYPE html>
<html>
<head>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    font-family: 'Helvetica Neue', Arial, sans-serif;
    color: #1a1a2e;
    font-size: 14px;
    line-height: 1.5;
    padding: 40px;
  }
  .invoice-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 40px;
    border-bottom: 3px solid #4F46E5;
    padding-bottom: 20px;
  }
  .company-info h1 {
    font-size: 28px;
    color: #4F46E5;
    margin-bottom: 4px;
  }
  .company-info p { color: #64748b; font-size: 13px; }
  .invoice-meta { text-align: right; }
  .invoice-meta h2 {
    font-size: 32px;
    color: #1a1a2e;
    text-transform: uppercase;
    letter-spacing: 2px;
  }
  .invoice-meta .invoice-number {
    font-size: 16px;
    color: #4F46E5;
    font-weight: 600;
    margin-top: 4px;
  }
  .invoice-meta .invoice-date { color: #64748b; margin-top: 2px; }
  .addresses {
    display: flex;
    justify-content: space-between;
    margin-bottom: 40px;
  }
  .addresses .block h3 {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #94a3b8;
    margin-bottom: 8px;
  }
  .addresses .block p { margin-bottom: 2px; }
  table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 30px;
  }
  thead th {
    background: #4F46E5;
    color: #fff;
    padding: 12px 16px;
    text-align: left;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
  }
  thead th:last-child,
  tbody td:last-child { text-align: right; }
  tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid #e2e8f0;
  }
  tbody tr:nth-child(even) { background: #f8fafc; }
  .totals {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 40px;
  }
  .totals table { width: 300px; }
  .totals td { padding: 8px 16px; border: none; }
  .totals .total-row td {
    font-size: 18px;
    font-weight: 700;
    border-top: 2px solid #1a1a2e;
    padding-top: 12px;
  }
  .footer {
    border-top: 1px solid #e2e8f0;
    padding-top: 20px;
    color: #94a3b8;
    font-size: 12px;
    text-align: center;
  }
</style>
</head>
<body>
  <div class="invoice-header">
    <div class="company-info">
      <h1>Acme Corp</h1>
      <p>123 Business Ave, Suite 400</p>
      <p>San Francisco, CA 94102</p>
      <p>billing@acmecorp.com</p>
    </div>
    <div class="invoice-meta">
      <h2>Invoice</h2>
      <p class="invoice-number">#INV-2026-0042</p>
      <p class="invoice-date">Date: February 23, 2026</p>
      <p class="invoice-date">Due: March 25, 2026</p>
    </div>
  </div>
  <div class="addresses">
    <div class="block">
      <h3>Bill To</h3>
      <p><strong>Widget Industries</strong></p>
      <p>Jane Smith</p>
      <p>456 Client Rd</p>
      <p>New York, NY 10001</p>
    </div>
    <div class="block">
      <h3>Payment Details</h3>
      <p>Bank: First National Bank</p>
      <p>Account: 1234567890</p>
      <p>Routing: 021000021</p>
    </div>
  </div>
  <table>
    <thead>
      <tr>
        <th>Description</th>
        <th>Qty</th>
        <th>Unit Price</th>
        <th>Amount</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Web Development - Phase 1</td>
        <td>40 hrs</td>
        <td>$150.00</td>
        <td>$6,000.00</td>
      </tr>
      <tr>
        <td>UI/UX Design</td>
        <td>20 hrs</td>
        <td>$125.00</td>
        <td>$2,500.00</td>
      </tr>
      <tr>
        <td>Hosting Setup &amp; Configuration</td>
        <td>1</td>
        <td>$500.00</td>
        <td>$500.00</td>
      </tr>
    </tbody>
  </table>
  <div class="totals">
    <table>
      <tr><td>Subtotal</td><td>$9,000.00</td></tr>
      <tr><td>Tax (8.5%)</td><td>$765.00</td></tr>
      <tr class="total-row"><td>Total Due</td><td>$9,765.00</td></tr>
    </table>
  </div>
  <div class="footer">
    <p>Payment is due within 30 days. Late payments are subject to a 1.5% monthly fee.</p>
    <p>Thank you for your business!</p>
  </div>
</body>
</html>

This template uses flexbox for layout, clean typography, and a professional color scheme. Embedded CSS ensures the styles travel with the document, absolute units and fixed widths prevent layout shifts during PDF rendering, and the page-break-inside: avoid approach (covered in our page break guide) keeps table rows from splitting across pages.

Generating Invoices with the LightningPDF API

Now send this HTML to the LightningPDF API. The API accepts your HTML as a JSON payload and returns a base64-encoded PDF. You can get started with a free account that includes 50 PDFs per month.

curl

The simplest way to test is with curl:

curl -X POST https://api.lightningpdf.dev/api/v1/pdf/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<!DOCTYPE html><html><head><style>body{font-family:Helvetica,sans-serif;padding:40px}.header{display:flex;justify-content:space-between;border-bottom:3px solid #4F46E5;padding-bottom:20px;margin-bottom:30px}.header h1{color:#4F46E5}table{width:100%;border-collapse:collapse}th{background:#4F46E5;color:#fff;padding:10px;text-align:left}td{padding:10px;border-bottom:1px solid #e2e8f0}</style></head><body><div class=\"header\"><div><h1>Acme Corp</h1><p>Invoice #INV-2026-0042</p></div><div><p>Date: 2026-02-23</p><p>Due: 2026-03-25</p></div></div><table><thead><tr><th>Description</th><th>Amount</th></tr></thead><tbody><tr><td>Web Development</td><td>$6,000.00</td></tr><tr><td>Design</td><td>$2,500.00</td></tr></tbody></table><p style=\"text-align:right;font-size:20px;font-weight:bold;margin-top:20px\">Total: $8,500.00</p></body></html>",
    "options": {
      "format": "A4",
      "print_background": true
    }
  }'

Python

For production use, Python with the requests library is a common choice. If you want a deeper dive into Python-specific patterns, see our Python PDF generation guide.

import requests
import base64
import json
from datetime import datetime, timedelta

API_URL = "https://api.lightningpdf.dev/api/v1/pdf/generate"
API_KEY = "your-api-key"

def generate_invoice(invoice_data):
    """Generate a PDF invoice from structured data."""

    # Build line items HTML
    items_html = ""
    subtotal = 0
    for item in invoice_data["items"]:
        amount = item["quantity"] * item["unit_price"]
        subtotal += amount
        items_html += f"""
        <tr>
            <td>{item['description']}</td>
            <td>{item['quantity']}</td>
            <td>${item['unit_price']:,.2f}</td>
            <td>${amount:,.2f}</td>
        </tr>"""

    tax = subtotal * invoice_data.get("tax_rate", 0.085)
    total = subtotal + tax

    # Build full HTML (using the template from above)
    html = f"""<!DOCTYPE html>
    <html><head><style>
      body {{ font-family: Helvetica, sans-serif; padding: 40px; color: #1a1a2e; }}
      .header {{ display: flex; justify-content: space-between;
                 border-bottom: 3px solid #4F46E5; padding-bottom: 20px;
                 margin-bottom: 30px; }}
      .header h1 {{ color: #4F46E5; }}
      .addresses {{ display: flex; justify-content: space-between;
                    margin-bottom: 30px; }}
      .addresses h3 {{ color: #94a3b8; font-size: 11px;
                       text-transform: uppercase; letter-spacing: 1px; }}
      table {{ width: 100%; border-collapse: collapse; margin-bottom: 30px; }}
      th {{ background: #4F46E5; color: #fff; padding: 12px 16px;
           text-align: left; font-size: 12px; text-transform: uppercase; }}
      th:last-child, td:last-child {{ text-align: right; }}
      td {{ padding: 12px 16px; border-bottom: 1px solid #e2e8f0; }}
      tr:nth-child(even) {{ background: #f8fafc; }}
      .totals {{ display: flex; justify-content: flex-end; }}
      .totals table {{ width: 300px; }}
      .totals td {{ border: none; padding: 8px 16px; }}
      .total-row td {{ font-size: 18px; font-weight: 700;
                       border-top: 2px solid #1a1a2e; }}
    </style></head><body>
      <div class="header">
        <div>
          <h1>{invoice_data['company_name']}</h1>
          <p>{invoice_data['company_address']}</p>
        </div>
        <div style="text-align:right">
          <h2 style="font-size:28px;letter-spacing:2px">INVOICE</h2>
          <p style="color:#4F46E5;font-weight:600">#{invoice_data['invoice_number']}</p>
          <p style="color:#64748b">Date: {invoice_data['date']}</p>
          <p style="color:#64748b">Due: {invoice_data['due_date']}</p>
        </div>
      </div>
      <div class="addresses">
        <div>
          <h3>Bill To</h3>
          <p><strong>{invoice_data['client_name']}</strong></p>
          <p>{invoice_data['client_address']}</p>
        </div>
      </div>
      <table>
        <thead><tr>
          <th>Description</th><th>Qty</th>
          <th>Unit Price</th><th>Amount</th>
        </tr></thead>
        <tbody>{items_html}</tbody>
      </table>
      <div class="totals"><table>
        <tr><td>Subtotal</td><td>${subtotal:,.2f}</td></tr>
        <tr><td>Tax ({invoice_data.get('tax_rate', 0.085) * 100:.1f}%)</td>
            <td>${tax:,.2f}</td></tr>
        <tr class="total-row">
          <td>Total Due</td><td>${total:,.2f}</td></tr>
      </table></div>
    </body></html>"""

    response = requests.post(
        API_URL,
        headers={
            "X-API-Key": API_KEY,
            "Content-Type": "application/json"
        },
        json={
            "html": html,
            "options": {
                "format": "A4",
                "print_background": True
            }
        }
    )

    if response.status_code == 200:
        result = response.json()
        pdf_bytes = base64.b64decode(result["data"]["pdf"])
        filename = f"invoice_{invoice_data['invoice_number']}.pdf"
        with open(filename, "wb") as f:
            f.write(pdf_bytes)
        print(f"Generated {filename} in {result['data']['generation_time_ms']}ms")
        return filename
    else:
        raise Exception(f"API error {response.status_code}: {response.text}")


# Usage
invoice = {
    "company_name": "Acme Corp",
    "company_address": "123 Business Ave, San Francisco, CA 94102",
    "invoice_number": "INV-2026-0042",
    "date": "2026-02-23",
    "due_date": "2026-03-25",
    "client_name": "Widget Industries",
    "client_address": "456 Client Rd, New York, NY 10001",
    "tax_rate": 0.085,
    "items": [
        {"description": "Web Development - Phase 1", "quantity": 40, "unit_price": 150.00},
        {"description": "UI/UX Design", "quantity": 20, "unit_price": 125.00},
        {"description": "Hosting Setup", "quantity": 1, "unit_price": 500.00}
    ]
}

generate_invoice(invoice)

Node.js

For Node.js applications, the pattern is similar. Our Node.js PDF generation guide covers additional patterns like streaming and async workflows.

const https = require('https');
const fs = require('fs');

const API_URL = 'https://api.lightningpdf.dev/api/v1/pdf/generate';
const API_KEY = 'your-api-key';

async function generateInvoice(invoiceData) {
  // Build line items
  let subtotal = 0;
  const itemsHtml = invoiceData.items.map(item => {
    const amount = item.quantity * item.unitPrice;
    subtotal += amount;
    return `<tr>
      <td>${item.description}</td>
      <td>${item.quantity}</td>
      <td>$${item.unitPrice.toLocaleString('en-US', {minimumFractionDigits: 2})}</td>
      <td>$${amount.toLocaleString('en-US', {minimumFractionDigits: 2})}</td>
    </tr>`;
  }).join('');

  const taxRate = invoiceData.taxRate || 0.085;
  const tax = subtotal * taxRate;
  const total = subtotal + tax;

  const html = `<!DOCTYPE html>
  <html><head><style>
    body { font-family: Helvetica, sans-serif; padding: 40px; }
    .header { display: flex; justify-content: space-between;
              border-bottom: 3px solid #4F46E5; padding-bottom: 20px;
              margin-bottom: 30px; }
    .header h1 { color: #4F46E5; }
    table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }
    th { background: #4F46E5; color: #fff; padding: 12px; text-align: left; }
    th:last-child, td:last-child { text-align: right; }
    td { padding: 12px; border-bottom: 1px solid #e2e8f0; }
    .total { text-align: right; font-size: 20px; font-weight: bold; }
  </style></head><body>
    <div class="header">
      <div><h1>${invoiceData.companyName}</h1>
      <p>${invoiceData.companyAddress}</p></div>
      <div style="text-align:right">
        <h2>INVOICE</h2>
        <p style="color:#4F46E5;font-weight:600">#${invoiceData.invoiceNumber}</p>
        <p>Date: ${invoiceData.date}</p>
        <p>Due: ${invoiceData.dueDate}</p>
      </div>
    </div>
    <table>
      <thead><tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Amount</th></tr></thead>
      <tbody>${itemsHtml}</tbody>
    </table>
    <p>Subtotal: $${subtotal.toFixed(2)}</p>
    <p>Tax (${(taxRate * 100).toFixed(1)}%): $${tax.toFixed(2)}</p>
    <p class="total">Total: $${total.toFixed(2)}</p>
  </body></html>`;

  const response = await fetch(API_URL, {
    method: 'POST',
    headers: {
      'X-API-Key': API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      html: html,
      options: { format: 'A4', print_background: true }
    })
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status} ${await response.text()}`);
  }

  const result = await response.json();
  const pdfBuffer = Buffer.from(result.data.pdf, 'base64');

  const filename = `invoice_${invoiceData.invoiceNumber}.pdf`;
  fs.writeFileSync(filename, pdfBuffer);
  console.log(`Generated ${filename} in ${result.data.generation_time_ms}ms`);
  return filename;
}

// Usage
generateInvoice({
  companyName: 'Acme Corp',
  companyAddress: '123 Business Ave, San Francisco, CA 94102',
  invoiceNumber: 'INV-2026-0042',
  date: '2026-02-23',
  dueDate: '2026-03-25',
  taxRate: 0.085,
  items: [
    { description: 'Web Development - Phase 1', quantity: 40, unitPrice: 150.00 },
    { description: 'UI/UX Design', quantity: 20, unitPrice: 125.00 },
    { description: 'Hosting Setup', quantity: 1, unitPrice: 500.00 }
  ]
});

Best Practices for Invoice Automation

Sequential Invoice Numbering

Most tax authorities require invoices to have unique, sequential numbers with no gaps. Use your database to manage this:

# Use a database sequence or atomic counter
import threading

class InvoiceNumberGenerator:
    def __init__(self, prefix="INV", start=1):
        self.prefix = prefix
        self.counter = start
        self.lock = threading.Lock()

    def next(self):
        with self.lock:
            year = datetime.now().year
            number = f"{self.prefix}-{year}-{self.counter:04d}"
            self.counter += 1
            return number

In production, store the counter in your database and use transactions to prevent duplicates. Never rely on timestamps or random strings for invoice numbers.

Tax Calculation Accuracy

Always calculate taxes server-side using decimal arithmetic, not floating-point:

from decimal import Decimal, ROUND_HALF_UP

subtotal = Decimal("9000.00")
tax_rate = Decimal("0.085")
tax = (subtotal * tax_rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
total = subtotal + tax  # Decimal("9765.00")

Storage and Retrieval

Store generated invoices in object storage (S3, GCS, or similar) with metadata for retrieval:

import boto3

s3 = boto3.client("s3")

def store_invoice(pdf_bytes, invoice_number, metadata):
    key = f"invoices/{datetime.now().year}/{invoice_number}.pdf"
    s3.put_object(
        Bucket="your-invoices-bucket",
        Key=key,
        Body=pdf_bytes,
        ContentType="application/pdf",
        Metadata={
            "invoice_number": invoice_number,
            "client": metadata["client_name"],
            "total": str(metadata["total"]),
        }
    )
    return key

Email Delivery

Attach the generated PDF to an email sent automatically after generation. Pair this with a webhook from your payment processor for a fully automated flow: payment received, invoice generated, email sent.

LightningPDF vs Alternatives for Invoicing

Here is how the major options compare for invoice-specific workloads:

Feature LightningPDF Puppeteer (Self-Hosted) DocRaptor CraftMyPDF
Speed <100ms (native engine) 1-3 seconds 2-5 seconds 1-2 seconds
Setup time 10 minutes 4-8 hours 30 minutes 1 hour
Monthly cost (2K invoices) $9 (Starter) ~$200 infra $35 $29
Template marketplace 15+ starter templates None None None
Batch generation Up to 100 per call Manual loops Not available Not available
Full CSS support Flexbox, Grid, modern CSS Full Chrome rendering PrinceXML subset Proprietary format
Maintenance Zero (managed) Ongoing (updates, scaling) Zero Zero
Free tier 50 PDFs/mo Free (infra costs) 5 PDFs/mo 100 PDFs/mo

For a detailed breakdown of how LightningPDF compares to specific alternatives, see our comparison guides: LightningPDF vs Puppeteer, LightningPDF vs DocRaptor, LightningPDF vs CraftMyPDF, and LightningPDF vs PDFShift.

For most invoice automation use cases, an API approach means you don't run headless browsers yourself. You can explore pre-built invoice templates in the LightningPDF marketplace or build your own in the visual designer. Our complete API comparison guide covers all seven major PDF APIs if you want a broader view.

Integrating with Your Application

The code examples above show standalone generation, but in a real application you will want to integrate invoice generation into your existing workflow.

  1. After payment confirmation: Trigger invoice generation when your payment processor (Stripe, PayPal) sends a webhook.
  2. On-demand downloads: Let customers download invoices from their account dashboard.
  3. Scheduled batch runs: Generate all invoices at the end of a billing cycle using a cron job.
  4. ERP synchronization: Push generated invoices into your accounting system (QuickBooks, Xero) via their APIs.

The HTML-to-PDF complete guide covers the architectural patterns for each of these integration approaches in more detail.

Getting Started

  1. Create a free LightningPDF account -- you get 50 PDFs per month at no cost, no credit card required.
  2. Copy your API key from the dashboard.
  3. Use the code examples above to generate your first invoice.
  4. Browse the template marketplace for pre-built invoice designs.
  5. When you are ready to scale, the Starter plan at $9/month covers 2,000 invoices, and the Pro plan at $29/month handles 10,000.

Read the full API documentation for advanced options including custom headers and footers, page margins, landscape orientation, and watermarks.

Frequently Asked Questions

How do I auto-generate PDF invoices from my app?

Send your invoice HTML to the LightningPDF API via a POST request with your transaction data merged into an HTML template. The API returns a base64-encoded PDF in under two seconds. Use Python, Node.js, Go, or any language with HTTP support to integrate invoice generation directly into your billing workflow.

What is the best API for generating PDF invoices?

LightningPDF handles invoice generation well at any scale, offering sub-100-millisecond generation with its native engine, a marketplace of pre-built invoice templates, batch processing for up to 100 invoices per call, and pricing starting at $9 per month for 2,000 documents. It requires zero infrastructure management.

Can I customize the invoice template design?

Yes. LightningPDF accepts any valid HTML and CSS, giving you complete control over layout, colors, fonts, and branding. You can use the visual template designer to build templates without writing code, or start from a marketplace template and modify it. Flexbox, grid, custom fonts, and embedded images are all fully supported.

L

LightningPDF Team

Building fast, reliable PDF generation tools for developers.

Ready to generate PDFs?

Start free with 50 PDFs per month. No credit card required.

Get Started Free