Ink & Order for Developers
Guide

Generate invoice PDFs from HTML

HTML and CSS are the easiest way to design an invoice: style it once, fill it in with a template, and let the Ink & Order API turn it into a clean PDF using headless Chromium. Paid, approved, in order.

1. Get a key

Get a free key (50 files/month), then export it:

export INKORDER_API_KEY=io_live_...
pip install requests

2. The complete script

"""Generate an invoice PDF from an HTML template with the Ink & Order API.

Usage: INKORDER_API_KEY=io_live_... python guide_invoice_pdf.py
"""
import html
import os
from string import Template

import requests

BASE = os.environ.get("INKORDER_API_URL", "https://api.inkandordertools.com")

INVOICE = Template("""<!doctype html><html><head><meta charset="utf-8"><style>
  body { font-family: Helvetica, Arial, sans-serif; margin: 48px; color: #111; }
  .top { display: flex; justify-content: space-between; }
  table { width: 100%; border-collapse: collapse; margin-top: 32px; }
  th, td { padding: 8px; border-bottom: 1px solid #ddd; text-align: left; }
  td.n, th.n { text-align: right; }
  .total { font-size: 20px; font-weight: bold; text-align: right; margin-top: 16px; }
</style></head><body>
  <div class="top"><div><h1>Invoice $number</h1><p>Billed to: $customer</p></div>
  <div><p>Date: $date</p><p>Due: $due</p></div></div>
  <table><tr><th>Item</th><th class="n">Qty</th><th class="n">Unit</th><th class="n">Amount</th></tr>
  $rows</table>
  <p class="total">Total: $$$total</p>
</body></html>""")


def render_invoice(number, customer, date, due, items):
    rows, total = [], 0.0
    for desc, qty, unit in items:
        amount = qty * unit
        total += amount
        rows.append(f"<tr><td>{html.escape(desc)}</td><td class='n'>{qty}</td>"
                    f"<td class='n'>${unit:,.2f}</td><td class='n'>${amount:,.2f}</td></tr>")
    return INVOICE.substitute(number=number, customer=html.escape(customer), date=date,
                              due=due, rows="".join(rows), total=f"{total:,.2f}")


def html_to_pdf(page: str) -> bytes:
    r = requests.post(f"{BASE}/v1/convert",
                      headers={"Authorization": f"Bearer {os.environ['INKORDER_API_KEY']}"},
                      files={"file": ("invoice.html", page.encode(), "text/html")}, timeout=120)
    r.raise_for_status()
    return r.content


page = render_invoice("INV-1001", "Acme Corp", "2026-09-29", "2026-10-29",
                      [("Website redesign", 1, 2400.00), ("Hosting (months)", 12, 25.00)])
with open("invoice-INV-1001.pdf", "wb") as f:
    f.write(html_to_pdf(page))
print("saved invoice-INV-1001.pdf")
python guide_invoice_pdf.py
# saved invoice-INV-1001.pdf

Why this works well

← All guides