Invoice PDF API

Invoice PDF API: branded invoices from your billing data

Send line items as JSON and get a print-ready invoice PDF back, ready to email, store or show in your customer portal. Your billing logic stays yours; we render the PDF.

  • 50 free renders/month
  • No credit card
  • Any currency and locale

What is an invoice PDF API?

An invoice PDF API turns billing data into a finished invoice document. Your application sends the invoice number, customer, line items, taxes and totals as JSON; the API fills them into a branded template, lays out the pages and returns a PDF you can email, store or offer for download. Calculations stay in your billing system.

Invoice PDF API key facts
EndpointPOST https://api.dynamicdocumentapi.com/v1/pdf/from-template (or /v1/pdf/from-html for your own HTML)
InputYour invoice data as JSON, in any shape
LayoutHTML/CSS template with Jinja syntax; repeating table headers, clean page breaks, page numbers
Currencies and datesAny ISO 4217 currency and any locale, formatted with Unicode CLDR data
CodesQR codes (payment links, bank transfer payloads) and 16 barcode types
DeliverySigned URL (1 hour default, up to 7 days), binary for email attachments, or base64
BulkBatches from a JSON list or a CSV file, progress via webhook (paid plans)
PriceFree: 50 renders/month. Paid from €15/month (billed annually) for 3,000 renders, auto top-ups from €6 per 1,000, about €0.005 per invoice

Generate an invoice PDF in one request

Store your invoice layout as a template once, then send each invoice as JSON. The example passes numbers exactly as your billing system calculated them; the template only formats them. Already have an invoice page in HTML? You can convert your existing HTML invoice directly instead.

curl https://api.dynamicdocumentapi.com/v1/pdf/from-template \
  -H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
  -H "Idempotency-Key: INV-2026-0042" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "tpl_invoice",
    "data": {
      "number": "INV-2026-0042",
      "issued": "2026-09-22",
      "due": "2026-10-22",
      "locale": "en-US",
      "currency": "USD",
      "customer": { "name": "Acme Co.", "address": "1 Main St, Austin, TX" },
      "items": [
        { "name": "Pro plan (annual)", "qty": 1, "unit": 1200.00, "total": 1200.00 },
        { "name": "Extra seats",       "qty": 4, "unit": 40.00,   "total": 160.00 }
      ],
      "subtotal": 1360.00, "tax_rate": 0.08, "tax": 108.80, "total": 1468.80,
      "pay_url": "https://pay.example.com/i/INV-2026-0042"
    },
    "filename": "{{ data.number }}.pdf",
    "delivery": "binary"
  }' \
  --output INV-2026-0042.pdf
// Node.js 18+: render the invoice and return the PDF bytes
export async function renderInvoice(invoice) {
  const res = await fetch("https://api.dynamicdocumentapi.com/v1/pdf/from-template", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.DYNAMIC_DOCUMENT_API_KEY}`,
      "Idempotency-Key": invoice.number, // safe to retry
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      template_id: "tpl_invoice",
      data: invoice,
      filename: `${invoice.number}.pdf`,
      delivery: "binary",
    }),
  });
  if (!res.ok) throw new Error(await res.text());
  return Buffer.from(await res.arrayBuffer());
}
import os
import requests

def render_invoice(invoice: dict) -> bytes:
    res = requests.post(
        "https://api.dynamicdocumentapi.com/v1/pdf/from-template",
        headers={
            "Authorization": f"Bearer {os.environ['DYNAMIC_DOCUMENT_API_KEY']}",
            "Idempotency-Key": invoice["number"],  # safe to retry
        },
        json={
            "template_id": "tpl_invoice",
            "data": invoice,
            "filename": f"{invoice['number']}.pdf",
            "delivery": "binary",
        },
        timeout=60,
    )
    res.raise_for_status()
    return res.content
<?php
function renderInvoice(array $invoice): string
{
    $ch = curl_init('https://api.dynamicdocumentapi.com/v1/pdf/from-template');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . getenv('DYNAMIC_DOCUMENT_API_KEY'),
            'Idempotency-Key: ' . $invoice['number'], // safe to retry
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS     => json_encode([
            'template_id' => 'tpl_invoice',
            'data'        => $invoice,
            'filename'    => $invoice['number'] . '.pdf',
            'delivery'    => 'binary',
        ]),
    ]);
    return curl_exec($ch); // PDF bytes
}

The Idempotency-Key header uses the invoice number. If a network error makes your code retry, the API returns the render it already made for that key instead of creating a second one. Keys are kept for 24 hours.

Everything an invoice template needs

Invoices look simple until the first one runs to three pages or goes to a customer in another country. These are the parts that usually take the longest to get right, and how the template handles them. To change the layout itself, you can customize the invoice template in HTML and CSS.

Line items that break cleanly across pages

A long order is a normal HTML table. The <thead> repeats on each page, rows are never cut in half, and the totals block stays together.

tr, .totals { break-inside: avoid; }
thead       { display: table-header-group; }

Taxes, discounts and totals from your system

Sales tax, VAT, reverse charge, discounts and rounding follow rules that belong in your billing code, where they are tested and audited. Send the calculated values; the template shows them. Nothing on the PDF can drift from your ledger.

Multi-currency and locale formatting

One template serves every market. Currency symbols, decimal separators, minor units and date formats follow Unicode CLDR for the locale you pass.

{{ total | format_currency(currency, locale) }}
USD en-US → $1,468.80   EUR de → 1.468,80 €
JPY ja    → ¥1,469
{{ issued | format_date("long", locale) }}

Logo, brand colors and legal footer

Your logo, fonts and colors are part of the template's HTML and CSS. The running footer carries your legal details and "Page 1 of 2" on every page.

"footer": { "enabled": true,
  "left": "Acme Inc. · EIN 12-3456789",
  "right": "Page {{page}} of {{pages}}" }

Payment QR codes

Put a scannable code next to the total: a link to your payment page, or a bank transfer payload such as an EPC QR code for SEPA payments. You build the payload; the template draws a sharp vector QR code.

{{ pay_url | qrcode(size="28mm", ecc="M") }}

Amounts in words

Some markets and payment slips expect the amount spelled out. number_to_words does it in English, German, French, Spanish and Dutch.

{{ 1468 | number_to_words("en") }}
→ one thousand, four hundred and sixty-eight

Automate invoices from Stripe, Shopify or your own backend

Most teams create an invoice when a payment event arrives. The flow is the same for any payment provider:

  1. Payment event

    Your provider calls your webhook, for example Stripe's invoice.paid or a Shopify order webhook.

  2. Render

    Your handler maps the event to invoice JSON and calls the API with the invoice number as idempotency key.

  3. Deliver

    Attach the PDF to your receipt email, store it, or show the signed link in the customer portal.

Payment webhooks are retried by the provider, so the same event can arrive twice. The idempotency key makes that harmless: the second call returns the first PDF. For month-end runs, send one batch with all invoices, from a JSON list or a CSV export, and receive batch.completed when every PDF is ready. Invoices are one use case of our PDF generation API.

Deliver and store invoices

  • Email attachment. "delivery": "binary" returns the PDF bytes, ready for your email provider's attachment field.
  • Download link. The default response contains a signed URL. It expires after one hour, or after up to seven days with expires_in.
  • Retention you control. Keep files for as many days as you set with retention_days, up to your plan's maximum, and delete them any time. Keep your legally required archive in your own system.
  • Your own bucket. On Enterprise plans, invoices can be written straight to your own S3-compatible storage.
  • Notifications. Signed webhooks tell your system when a render succeeded or failed. A webhook your server does not accept is tried up to eight times over about 28 hours.

What an invoice costs to generate

Free

€0

50 invoices per month. Enough to build and test your template end to end.

Starter

€15 / month

3,000 renders per month, billed annually (€19 billed monthly). About €0.005 per invoice.

At 15,000 invoices a month the Growth plan costs €47 billed annually, about €0.0031 per invoice — and a busy month never stops: paid plans top up automatically. See the cost per invoice on every plan.

FAQ

Invoice PDF API: frequently asked questions

Does the API calculate taxes or invoice numbers?

No, and that is deliberate. Invoice numbers, tax rates, discounts and totals come from your billing system, which is the system of record. The API formats and lays out what you send, so the PDF always matches your books. For convenience, templates can still add up columns with sum_by.

Can an invoice span multiple pages?

Yes. Line items are a normal HTML table: the header row repeats on every page, rows are never split in half, and a footer can show "Page 2 of 3". Totals can be kept together with the last rows using break-inside: avoid.

Can I generate invoices in several languages and currencies?

Yes. Pass the customer's locale and currency with the data. Currency symbols, decimal separators and date formats follow Unicode CLDR rules, for example $1,234.50 in the US, 1.234,50 € in Germany and ¥1,235 in Japan. Text labels come from your template, so one template can hold several languages.

Can I show a "Paid" stamp or a different layout for credit notes?

Yes. Templates use conditions, so one template can show a "Paid" stamp, a discount line, a second address block or a credit-note title depending on the data you send. Separate templates for invoices and credit notes work just as well.

How long are generated invoices stored?

As long as you choose per request with retention_days, up to your plan's maximum, or not at all if you take the file as binary. You can delete stored files at any time. Legal retention of invoices remains your responsibility, so keep your own archive copy, for example in your own storage bucket.

Can the API email invoices to my customers?

The API creates the PDF; sending stays with your email provider. Request "delivery": "binary" to attach the file directly, or send a signed link that expires after a time you choose, up to seven days.

Generate your first invoice free

50 renders a month on the free plan, no credit card, auto top-ups on paid plans.

Start free