HTML to PDF in Python with requests and the Rendlet API

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

Short answer: requests.post("https://rendlet.com/v1/pdf", headers={"Authorization": "Bearer KEY"}, json={"html": html}) returns the PDF bytes in r.content. Write them to a file or return them from your view. Rendering happens on Rendlet's side with headless Chromium, so there are no system packages to install on your server.

1. Try it with curl

The public sandbox key needs no signup (5 renders a day per network, 1 page, up to 50 KB, faint watermark).

# Works as-is with the public sandbox key. With your own key: export RENDLET_API_KEY=YOUR_API_KEY
curl -X POST https://rendlet.com/v1/pdf \
  -H "Authorization: Bearer ${RENDLET_API_KEY:-sandbox}" \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Hello</h1><p>Rendered by Rendlet.</p>","options":{"format":"A4","margin":"15mm"}}' \
  -o hello.pdf

2. Python: an html_to_pdf() helper

Install requests (pip install requests), save as render.py and run python render.py. It builds a small report table from data and saves report.pdf. Output when we ran it: saved report.pdf (1 page, 17052 bytes); 3 renders left.

# Python 3 + requests (pip install requests). Save as render.py and run: python render.py
# Uses the public sandbox key unless you set RENDLET_API_KEY=YOUR_API_KEY
import os
import requests

API_KEY = os.environ.get("RENDLET_API_KEY", "sandbox")


def html_to_pdf(html, options=None):
    r = requests.post(
        "https://rendlet.com/v1/pdf",
        headers={"Authorization": "Bearer " + API_KEY},
        json={"html": html, "options": options or {"format": "A4", "margin": "15mm"}},
        timeout=60,
    )
    if not r.ok:
        # Errors are JSON, e.g. {"error": "quota_exceeded", ...}
        raise RuntimeError(f"Rendlet {r.status_code}: {r.text}")
    return r.content, r.headers.get("X-Pages"), r.headers.get("X-Quota-Remaining")


report = {"title": "Weekly sales", "rows": [("North", 1200), ("South", 950)]}
rows = "".join(f"<tr><td>{name}</td><td>{amount:,}</td></tr>" for name, amount in report["rows"])
html = f"""<!doctype html>
<html><body style="font-family:sans-serif;padding:24px">
  <h1 style="color:#1f4e79">{report['title']}</h1>
  <table border="1" cellpadding="6" style="border-collapse:collapse">
    <tr><th>Region</th><th>Sales (USD)</th></tr>{rows}
  </table>
</body></html>"""

pdf, pages, remaining = html_to_pdf(html)
with open("report.pdf", "wb") as f:
    f.write(pdf)
print(f"saved report.pdf ({pages} page, {len(pdf)} bytes); {remaining} renders left")

In production, render your HTML with the template engine you already use (Jinja2, Django templates) so values are escaped, then pass the string to html_to_pdf().

3. Options

FieldValues
options.formatA4, Letter, Legal, A3, A5
options.landscapeTrue / False
options.marginCSS length, e.g. "15mm" or "0"
options.printBackgroundTrue / False
filenameName used in the Content-Disposition header
response"binary" (default) or "base64": JSON with a base64 field (decode with base64.b64decode)

For HTML input, JavaScript in the page is not run, and only data: URIs and public https:// assets load. Inline your CSS or link it over https. Simplified Chinese works out of the box (Noto Sans SC is bundled).

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.

Returning the PDF from Flask, Django or FastAPI

Return the bytes with Content-Type: application/pdf (and Content-Disposition: attachment; filename=... if you want a download), or save them to object storage. Keep the API key in an environment variable on the server.

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.

Coming from wkhtmltopdf or pdfkit?

wkhtmltopdf's GitHub repository was archived in January 2023; see Rendlet vs wkhtmltopdf for a migration example. If your pages need JavaScript or a login to render, a self-hosted browser is the better fit: see Rendlet vs Puppeteer.

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