Guide

JSON to invoice PDF API

POST https://pdfops.dev/api/invoice takes a JSON object with the seller, the buyer and the line items, and returns a complete invoice PDF: header, address blocks, items table, subtotal, tax, total and notes. There is no template to design and no headless browser in the path, and the same JSON always returns the same bytes. It is free to try with no key: 100 requests per IP per month, or 250 with a free key.

The request

One JSON object. from, to and items are required; everything else is optional. Parties are a plain string or { name, lines } with up to 6 address lines.

curl -X POST https://pdfops.dev/api/invoice \
  -H "Content-Type: application/json" \
  -o invoice-INV-0042.pdf \
  -d '{
  "from": { "name": "Acme Studio LLC", "lines": ["100 Main St", "Portland, OR 97201"] },
  "to": { "name": "Globex Corporation", "lines": ["Attn: Accounts Payable", "42 Industrial Way", "Springfield, IL 62701"] },
  "invoice_number": "INV-0042",
  "date": "2026-09-01",
  "due": "2026-10-01",
  "currency": "USD",
  "tax_rate": 8.5,
  "items": [
    { "description": "Design retainer - September", "quantity": 1, "unit_price": 2400 },
    { "description": "Landing page build", "quantity": 3, "unit_price": 650 },
    { "description": "Stock photo licences", "quantity": 12, "unit_price": 9.5 }
  ],
  "notes": "Payment by bank transfer within 30 days. Thank you for your business."
}'

No API key is needed to try it. Add -H "X-API-Key: pdfops_live_..." to use your own quota.

The response

200 OK with Content-Type: application/pdf and the PDF as the body: one US Letter page, 3,311 bytes for this request. The text layer of that file, extracted with pdftotext -layout (spacing condensed to fit):

INVOICE                                                          INV-0042
                                                             Date: 2026-09-01
                                                              Due: 2026-10-01

FROM                                   BILL TO
Acme Studio LLC                        Globex Corporation
100 Main St                            Attn: Accounts Payable
Portland, OR 97201                     42 Industrial Way
                                       Springfield, IL 62701

DESCRIPTION                            QTY          UNIT          AMOUNT
Design retainer - September              1  USD 2,400.00    USD 2,400.00
Landing page build                       3    USD 650.00    USD 1,950.00
Stock photo licences                    12      USD 9.50      USD 114.00

                                              Subtotal      USD 4,464.00
                                            Tax (8.5%)        USD 379.44
                                                 TOTAL      USD 4,843.44

NOTES
Payment by bank transfer within 30 days. Thank you for your business.

Generated with pdfops.dev

Line amounts, subtotal, tax and total are computed for you. Long invoices continue onto further pages with the table header repeated, up to 100 items. The PDF's title metadata is Invoice INV-0042 and its creation date is the invoice date.

Validation errors come back as 400 JSON with a stable code, so a caller or an agent can fix the input and retry:

{"error":"invalid_items","details":"\"items\" must be a non-empty array"}

All codes, field limits and the Stripe webhook example are in the /api/invoice reference.

Same JSON, same bytes

Send the request above twice, or next month, and you get the same file:

$ sha256sum invoice-a.pdf invoice-b.pdf
9c7a206fe5a5bf2cbe22250c965b49e8ab8a55dc8167474afa0864a451e87326  invoice-a.pdf
9c7a206fe5a5bf2cbe22250c965b49e8ab8a55dc8167474afa0864a451e87326  invoice-b.pdf

That hash was measured on 2026-09-24 against both the production API and a local build of the same code. Most HTML-to-PDF renderers stamp the render time into the file, so every run hashes differently. Here there is no wall-clock input, which is what makes a retried billing webhook safe: store the PDF under its invoice number or hash and a duplicate delivery overwrites an identical file. The pattern is worked through in snapshot-testing PDFs with a hash.

From code

Python

from pdfops import PdfOps   # pip install pdfops, zero dependencies

pdf = PdfOps().invoice({
    "from": "Acme Studio LLC",
    "to": {"name": "Globex Corporation", "lines": ["42 Industrial Way"]},
    "invoice_number": "INV-0042",
    "date": "2026-09-01",
    "items": [{"description": "Landing page build", "quantity": 3, "unit_price": 650}],
})
open("invoice-INV-0042.pdf", "wb").write(pdf)

Node.js, Bun, Deno, Workers

const res = await fetch('https://pdfops.dev/api/invoice', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    from: 'Acme Studio LLC',
    to: 'Globex Corporation',
    invoice_number: 'INV-0042',
    date: '2026-09-01',
    items: [{ description: 'Landing page build', quantity: 3, unit_price: 650 }],
  }),
});
if (!res.ok) throw new Error(JSON.stringify(await res.json()));
const pdf = new Uint8Array(await res.arrayBuffer());

The typed client pdfops-sdk wraps the same call as client.invoice(request).

From an AI agent

With pdfops-mcp installed, "Invoice Globex for 3 days of consulting at $650, 8.5% tax, due in 30 days" becomes one pdf_invoice tool call with the same JSON, and the PDF is written to disk rather than passed through the conversation.

The fields

FieldTypeNotes
from, tostring or { name, lines? }Required. Up to 6 address lines each.
itemsarray, 1 to 100Required. Each { description, quantity?, unit_price }; quantity defaults to 1.
invoice_numberstringPrinted in the header and used as the PDF title.
date, duestringPrinted as given. date also pins the metadata dates. Omit it and no date line is printed.
currencyISO 4217 codeDefault USD. Formatting only.
tax_ratenumber, 0 to 100One percentage applied to the subtotal.
notesstring, up to 1,000 charactersPrinted under the totals.

When this is the wrong tool

Prefer a form to a curl call? The free invoice generator runs the same endpoint in the browser and shows the exact request it sent.

Frequently asked

Is there a free JSON to invoice PDF API?

Yes. POST https://pdfops.dev/api/invoice works with no key and no signup for 100 requests per IP per month, and a free key (no card) gives 250 a month. Free and keyless output carries a small "Generated with pdfops.dev" footer line; Indie ($16/month, 4,000 requests) and Pro ($79/month, 25,000) output has none.

Do I need to design a template first?

No. The endpoint ships its own layout: header with number and dates, FROM and BILL TO blocks, an items table, subtotal, tax, total and notes. You send data only. If you need your own design, build it once as a fillable PDF and send values to /api/fill-form instead.

Is the output really byte-identical for the same JSON?

Yes. The PDF's metadata dates are pinned to the invoice date you send (or a fixed epoch when you omit it), so nothing in the file depends on when it was rendered. The request on this page returns sha256 9c7a206f…e87326 on every call. Webhook retries and cron re-runs produce the same file, so dedup by hash works.

Which currencies and languages are supported?

Any ISO 4217 code for currency; amounts print as the code plus a two-decimal number such as EUR 850.00, with no conversion. Text uses a Latin font, so Western European characters (é, ü, ñ, €) print correctly, while characters outside that set, such as Chinese, Japanese, Arabic or Cyrillic, print as question marks. Labels are in English.

Can I add a logo, change the paper size or produce e-invoice XML?

Not today. There is one US Letter layout with no logo slot, and the endpoint returns PDF only, not UBL, Factur-X or ZUGFeRD XML. For those, use a fill-form template you design yourself, or a dedicated e-invoicing service.

Can an AI agent or an n8n workflow call it?

Yes. The pdfops-mcp server exposes it to agents as the pdf_invoice tool, the n8n community node n8n-nodes-pdfops has an invoice operation, and the Python (pip install pdfops) and TypeScript (pdfops-sdk) clients have an invoice() method.