Rendex
Recipe

Render an HTML Financial Report or Invoice to PNG in Python

Generate the report as HTML in your Python backend, hand it to Rendex, and get back a pixel-perfect PNG — charts, financial tables, certificates, and invoices included. One render_html() call replaces a hosted headless browser.

Last updated 2026-06-02

The Problem

You already produce reports, invoices, and financial tables as HTML on the server — it's the easiest format to template and style. But turning that HTML into a raster image to embed in a generated PDF or deliverable means running headless Chrome (Playwright or Puppeteer) inside your app: a heavyweight binary to install, version, and keep alive, plus the memory leaks and cold-start stalls that come with a long-running browser process. WeasyPrint and wkhtmltopdf drop modern CSS — Flexbox, Grid, web fonts — so your charts and tables don't look the way they do in a browser. None of this is the report logic you actually own.

The Solution

Render the HTML with the Rendex Python SDK instead. Pip-install rendex, call render_html(html, data=..., format="png") with your report markup, and get back PNG bytes from a single HTTP call — no browser to host, no binary to babysit. Rendex renders with a real, modern headless engine, so Flexbox, CSS Grid, web fonts, and print backgrounds all come through. Set device_scale_factor=2 for retina-crisp output and full_page=True so a long table isn't clipped. Write the bytes to disk, embed them in your generated PDF, or push them to storage — all inside the same script, all on the free tier.

How the Workflow Runs

Python script

Build the report HTML server-side (Jinja2, f-strings, or a charting lib's HTML/SVG export).

Rendex

render_html() posts the HTML to /v1/screenshot and returns PNG bytes at 2x (retina) resolution.

Save / embed

Write the PNG to disk, drop it into a generated PDF, or upload it to your report store — all from the same script.

Input → Rendered Output

Left: a Python snippet calling rendex.render_html on a financial table at device_scale_factor 2. Right: the rendered sharp 2x PNG — a 'Revenue by Quarter' report with a brand-orange quarterly bar chart, an FY revenue total, and a green YoY growth figure, ready to embed in a PDF report.

Rendered by Rendex

What You Need

  • A Rendex API key — the free tier gives you 100 renders/month with no card required.
  • Python 3.9+ with the SDK installed: pip install rendex (single dependency, httpx).
  • Your report, invoice, or table as an HTML string — templated however you like (Jinja2, f-strings, a charting library's HTML/SVG export).
  • Somewhere to put the result: a file path, a PDF builder you embed the PNG into, or an upload target.

What This Recipe Uses

HTML to PNG

render_html() turns a raw HTML string into PNG bytes through one /v1/screenshot call — no Playwright or Puppeteer in your deployment.

Retina output

device_scale_factor=2 renders at 2x so financial tables, small type, and chart labels stay crisp when embedded in a PDF or slide.

Full-page capture

full_page=True renders the whole report, so a long itemized table or multi-section statement isn't clipped at the viewport height.

Mustache data{} templating

Pass one reusable HTML template plus a data object. {{company}}, {{period}}, and {{#rows}} loops fill in server-side, per report.

Build It

render_report.py
from rendex import Rendex
from pathlib import Path

rendex = Rendex("YOUR_API_KEY")  # or Rendex(os.environ["RENDEX_API_KEY"])

# Your report HTML — templated however you already do it (Jinja2, f-strings,
# a charting library's HTML/SVG export). Here: a financial summary table.
html = """
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
      body { font-family: system-ui, "Segoe UI", Roboto, sans-serif;
             color: #0f172a; margin: 0; padding: 40px; width: 900px; }
      h1   { color: #ea580c; font-size: 24px; margin: 0 0 4px; }
      .sub { color: #64748b; font-size: 13px; margin: 0 0 28px; }
      table { width: 100%; border-collapse: collapse; }
      thead th { text-align: left; font-size: 11px; text-transform: uppercase;
                 letter-spacing: .04em; color: #64748b;
                 border-bottom: 2px solid #e2e8f0; padding: 10px 12px; }
      tbody td { border-bottom: 1px solid #f1f5f9; padding: 10px 12px; }
      td.amt, th.amt { text-align: right; font-variant-numeric: tabular-nums; }
      tfoot td { font-weight: 700; padding-top: 14px; }
    </style>
  </head>
  <body>
    <h1>{{company}} — Revenue Summary</h1>
    <p class="sub">Period: {{period}}</p>
    <table>
      <thead><tr><th>Line item</th><th class="amt">Amount</th></tr></thead>
      <tbody>
        {{#rows}}<tr><td>{{label}}</td><td class="amt">{{amount}}</td></tr>{{/rows}}
      </tbody>
      <tfoot><tr><td>Total</td><td class="amt">{{total}}</td></tr></tfoot>
    </table>
  </body>
</html>
"""

result = rendex.render_html(
    html,
    data={
        "company": "Acme Robotics, Inc.",
        "period": "Q1 2026",
        "rows": [
            {"label": "Subscriptions", "amount": "€482,100"},
            {"label": "Professional services", "amount": "€91,400"},
            {"label": "Overages", "amount": "€12,750"},
        ],
        "total": "€586,250",
    },
    format="png",
    width=900,
    full_page=True,          # render the whole table, not just the viewport
    device_scale_factor=2,   # 2x retina — crisp when embedded in a PDF/deck
)

# result.image is the raw PNG bytes — save it, or embed it in a generated PDF.
Path("revenue-summary.png").write_bytes(result.image)
print("Rendered", result.metadata)  # width/height/format/credits-remaining
embed_in_pdf.py
# Render the HTML report to a PNG, then drop it into a deliverable PDF.
# (reportlab shown; the PNG bytes work with any PDF library or a docx/pptx.)
from io import BytesIO
from rendex import Rendex
from reportlab.lib.pagesizes import A4
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas

rendex = Rendex("YOUR_API_KEY")

result = rendex.render_html(
    REPORT_HTML,                 # your templated HTML string
    data=report_context,         # the row's data for this report
    format="png",
    width=900,
    full_page=True,
    device_scale_factor=2,
)

# Build the PDF deliverable and place the rendered chart/table image.
pdf = canvas.Canvas("deliverable.pdf", pagesize=A4)
img = ImageReader(BytesIO(result.image))
pdf.drawImage(img, 48, 360, width=499, preserveAspectRatio=True, mask="auto")
pdf.drawString(48, 800, "Quarterly Report — generated automatically")
pdf.showPage()
pdf.save()
render-report.sh
# The same call without the SDK. --output writes the PNG bytes to a file.
curl -X POST https://api.rendex.dev/v1/screenshot \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --output revenue-summary.png \
  -d '{
    "html": "<!doctype html><html><head><style>body{font-family:system-ui,sans-serif;color:#0f172a;padding:40px;width:900px}h1{color:#ea580c;margin:0 0 4px}table{width:100%;border-collapse:collapse}thead th{text-align:left;border-bottom:2px solid #e2e8f0;padding:10px 12px;font-size:11px;text-transform:uppercase;color:#64748b}tbody td{border-bottom:1px solid #f1f5f9;padding:10px 12px}td.amt,th.amt{text-align:right}tfoot td{font-weight:700;padding-top:14px}</style></head><body><h1>{{company}} — Revenue Summary</h1><p>Period: {{period}}</p><table><thead><tr><th>Line item</th><th class=\"amt\">Amount</th></tr></thead><tbody>{{#rows}}<tr><td>{{label}}</td><td class=\"amt\">{{amount}}</td></tr>{{/rows}}</tbody><tfoot><tr><td>Total</td><td class=\"amt\">{{total}}</td></tr></tfoot></table></body></html>",
    "data": {
      "company": "Acme Robotics, Inc.",
      "period": "Q1 2026",
      "rows": [
        { "label": "Subscriptions", "amount": "€482,100" },
        { "label": "Professional services", "amount": "€91,400" },
        { "label": "Overages", "amount": "€12,750" }
      ],
      "total": "€586,250"
    },
    "format": "png",
    "width": 900,
    "fullPage": true,
    "deviceScaleFactor": 2
  }'

100 free API calls/month — no credit card required. Get your API key and start building.

Reach for this pattern when your backend already speaks HTML and you need an image, not a webpage — a financial summary embedded in a board deck, an invoice rasterized into a PDF deliverable, a certificate generated per customer, or a chart baked into a nightly report. It is the right fit for the dev who renders dozens or thousands of these from a server script and does not want a headless browser in the deployment. Because render_html() takes raw HTML plus an optional Mustache data object, you keep templating where it belongs — Jinja2, a charting library's HTML export, or plain f-strings — and Rendex only handles the pixels. You get a real rendering engine, so the PNG matches what you'd see in Chrome: modern CSS, web fonts, and print backgrounds intact. Use device_scale_factor=2 for retina output and full_page=True so a tall report isn't cut off at the viewport. The SDK returns the bytes directly, so saving to disk, embedding into a PDF, or uploading to your report store is one more line. It runs on Rendex's free 100 renders a month with no card, which is enough to prove the pipeline before you scale the volume.

Frequently Asked Questions

Related Resources