HTML to PDF in Python
The shortest reliable path from HTML to PDF in Python is not a rendering library — it’s an HTTP call to a rendering API. Your app sends HTML, a managed Chromium engine renders it exactly as Chrome would, and a PDF comes back. No browser binaries in your deploy, no native dependencies, no page-break surprises.
Complete example (generate_pdf.py)
Set NIPPYPDF_API_KEY in your environment, then this is the whole
program:
# generate_pdf.py — run with: python generate_pdf.py
import os
import requests
resp = requests.post(
"https://api.nippypdf.com/v1/pdf",
headers={"Authorization": f"Bearer {os.environ['NIPPYPDF_API_KEY']}"},
json={
"html": "<h1>Invoice #1042</h1><p>Total: $2,508.00</p>",
"format": "A4",
"margin": "20mm",
"print_background": True,
},
timeout=30,
)
resp.raise_for_status()
with open("invoice.pdf", "wb") as f:
f.write(resp.content)
print("Wrote invoice.pdf")
All request options
POST https://api.nippypdf.com/v1/pdf accepts JSON; the response body is the
binary PDF. Options mirror Chromium’s print pipeline:
{
"html": "<h1>…</h1>", // or "url": "https://example.com/invoice/1042"
"format": "A4", // "A4" | "Letter" | "Legal" | { "width": "210mm", "height": "297mm" }
"margin": "20mm", // uniform, or { "top": "…", "right": "…", "bottom": "…", "left": "…" }
"landscape": false,
"scale": 1.0, // 0.1 – 2.0
"print_background": true,
"wait_until": "networkidle", // "load" | "domcontentloaded" | "networkidle"
"header_html": "<span></span>", // running header (use sparingly)
"footer_html": "<span class=\"pageNumber\"></span> / <span class=\"totalPages\"></span>"
}
For pagination control — keeping blocks together, repeating table headers, paper sizes — see the page-break CSS guide.
The self-hosted alternatives, honestly
- WeasyPrint: a genuinely good pure-Python renderer, but its own engine — output differs from Chrome, and system dependencies (Pango/Cairo) complicate deploys.
- Playwright / pyppeteer: Chromium output, browser-fleet problems — memory, processes, cold starts. Read more →
If those trade-offs are fine for your workload, they are honest choices. The API exists for teams who want Chrome’s output without operating Chrome.
FAQ
Do I need a browser or PDF library installed in my Python app?
No. Rendering happens server-side on NippyPDF's Chromium fleet; your Python code only needs an HTTP client (this guide uses requests (pip install requests)). No Chromium download, no native dependencies, nothing extra to deploy.
How do page breaks, fonts and CSS behave?
The API renders with a current Chromium engine, so anything that renders in Chrome renders in your PDF: flexbox, grid, webfonts, and the modern break-before/break-inside page-break properties. See our page-break CSS guide for controlling pagination precisely.
Can I convert a URL instead of an HTML string?
Yes — send { "url": "https://…" } instead of { "html": "…" } in the same request body. Use wait_until: "networkidle" for pages that load data client-side.
Is the API live?
NippyPDF is pre-launch: the schema shown here is the launch API, and early-access keys are going out in batches via the waitlist. Early signups lock in pay-as-you-go pricing around $0.003/document with 500 documents/month free.
Try your own markup in the live in-browser demo, or jump to another language: Node.js, PHP, Ruby, C#, Go.
Generate PDFs from Python without the infrastructure
Pre-launch. Join the waitlist and lock in early-access pricing — we email once, when your key is ready. No spam.