Invoice PDF API: from JSON or HTML to PDF

Last updated: . Every code sample on this page was run against the live API with the sandbox key on that date.

Short answer: Rendlet gives you two ways to turn invoice data into a PDF with one HTTP call: (A) send JSON to the built-in invoice template, with no HTML to write, or (B) fill your own HTML invoice template and send the HTML. Both use POST https://rendlet.com/v1/pdf.

Sample invoice PDF rendered by Rendlet: INVOICE INV-1001 from Acme Studio Ltd to Globex Corp, two line items, 10% tax, total USD 2,035.00

The sample above, as a PDF: invoice-sample.pdf. Sandbox renders also carry a faint SANDBOX watermark.

The flow

  1. Your app has the invoice data (order, customer, line items, tax).
  2. Either send that data as JSON to the built-in template (A), or merge it into your HTML template (B).
  3. POST to /v1/pdf; the response body is the PDF (or base64 JSON with "response":"base64").
  4. Store it, email it, or stream it to the customer.

A. JSON in, invoice PDF out (built-in template)

Fields: number, date, due, currency, accent (hex colour), seller {name, address, email}, buyer {name, address}, items [{description, qty, unit_price}], tax_rate (e.g. 0.1) and notes. Rendlet computes line amounts, subtotal, tax and total. GET /v1/templates returns this example and an itinerary template; no key needed. Template mode doesn't launch a browser, and it supports Simplified Chinese text.

Save as invoice.mjs and run node invoice.mjs (Node 18+):

// Node 18+. Invoice from JSON data with the built-in "invoice" template (no HTML needed).
// Save as invoice.mjs and run: node invoice.mjs
import { writeFile } from "node:fs/promises";

const invoice = {
  template: "invoice",
  data: {
    number: "INV-1001",
    date: "2026-10-18",
    due: "2026-11-17",
    currency: "USD",
    accent: "#1f4e79",
    seller: { name: "Acme Studio Ltd", address: "1 Market St, Springfield", email: "billing@acme.example" },
    buyer: { name: "Globex Corp", address: "42 Elm Ave, Shelbyville" },
    items: [
      { description: "Design sprint (3 days)", qty: 1, unit_price: 1800 },
      { description: "Hosting (monthly)", qty: 2, unit_price: 25 },
    ],
    tax_rate: 0.1,
    notes: "Thank you for your business. Payment by bank transfer within 30 days.",
  },
  filename: "INV-1001.pdf",
};

const res = await fetch("https://rendlet.com/v1/pdf", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RENDLET_API_KEY ?? "sandbox"}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(invoice),
});
if (!res.ok) throw new Error(`Rendlet ${res.status}: ${await res.text()}`);
await writeFile("INV-1001.pdf", Buffer.from(await res.arrayBuffer()));
console.log("saved INV-1001.pdf;", res.headers.get("x-quota-remaining"), "renders left");

B. Your own HTML invoice template

If you need your own layout or branding, render your HTML template with your data (your usual template engine), then send the HTML. Our real sample request is at /samples/invoice-sample-request.json: a self-contained HTML invoice with inline CSS, options of {"format":"A4","margin":"0"} (the design has its own padding) and a filename. Render it with curl:

# Download the sample request, then render it (sandbox key works as-is)
curl -sO https://rendlet.com/samples/invoice-sample-request.json
curl -X POST https://rendlet.com/v1/pdf \
  -H "Authorization: Bearer ${RENDLET_API_KEY:-sandbox}" \
  -H "Content-Type: application/json" \
  -d @invoice-sample-request.json \
  -o sample-invoice.pdf

When we ran it the response was 200, content-type: application/pdf, x-pages: 1. The Node and Python versions of this call are in the Node.js guide and the Python guide.

Tips for invoice and report HTML

Limits to know

SandboxFreeStarterProScale
Price$0$0$7/mo$19/mo$59/mo
Documents5 / day / network100 / month1,000 / month5,000 / month25,000 / month
Requests per minute3103060120
Pages per document11050200500
HTML size50 KB1 MB2 MB2 MB2 MB

Every plan has a hard monthly cap: at the cap the API returns 429 with an upgrade_url, and you are never billed for overage. Failed renders don't count. Source: docs.

Errors and response headers

Successful renders return X-Pages, X-Plan, X-Quota-Limit and X-Quota-Remaining. Errors are JSON like {"error":"...","message":"..."}: 400 bad request, 401 missing key, 403 invalid key, 413 HTML too large or too many pages, 429 quota exceeded, 500 render failed.

Try it on your own HTML. The sandbox renders a real PDF with no signup (5 a day per network, 1 page, watermarked). A free key gives 100 documents a month, up to 10 pages each, with no watermark.

Open the sandbox →   Get a free key · Pricing

Related