Tutorial

How to Convert HTML to PDF in Python

|8 min read

Generating PDFs from HTML is one of the most common backend tasks — invoices, reports, certificates, data exports. Python has several options, from local libraries to cloud APIs. This guide compares the three most popular approaches with code examples.

Common use cases

HTML to PDF conversion is everywhere in production applications:

1

Invoices & receipts

Generate branded invoices from HTML templates with dynamic data. Send as email attachments or store for accounting.

2

Reports & dashboards

Export data visualizations, analytics dashboards, and business reports as shareable PDF documents.

3

Certificates & diplomas

Generate personalized certificates at scale for courses, events, and achievements.

4

Legal & compliance

Archive web pages, terms of service, and compliance documents as tamper-evident PDFs.

Method 1: WeasyPrint

WeasyPrint is a pure-Python library that renders HTML/CSS to PDF. Install with pip install weasyprint. It requires system-level dependencies (cairo, pango, gdk-pixbuf) which can be tricky to install on some platforms.

Convert a URL

weasyprint_url.py
#E8A0BF">from weasyprint #E8A0BF">import HTML

# Convert a URL to PDF
HTML(#A8D4A0">'https://example.com/invoice/12345').#87CEEB">write_pdf(#A8D4A0">'invoice.pdf')
#E8A0BF">print(#A8D4A0">'PDF saved to invoice.pdf')

Convert raw HTML

weasyprint_html.py
#E8A0BF">from weasyprint #E8A0BF">import HTML

html_string = #A8D4A0">""#A8D4A0">"
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; padding: 40px; }
    .header { border-bottom: 2px solid #333; padding-bottom: 20px; }
    .total { font-size: 24px; font-weight: bold; color: #FF4B33; }
  </style>
</head>
<body>
  <div class="header#A8D4A0">">
    <h1>Invoice #12345</h1>
    <p>Date: April 12, 2026</p>
  </div>
  <table>
    <tr><td>API requests (1,500)</td><td>$9.00</td></tr>
    <tr><td>FlareSolverr add-on</td><td>$0.00</td></tr>
  </table>
  <p class="total#A8D4A0">">Total: $9.00</p>
</body>
</html>
"#A8D4A0">""

HTML(string=html_string).#87CEEB">write_pdf(#A8D4A0">'invoice.pdf')
#E8A0BF">print(#A8D4A0">'PDF saved to invoice.pdf')

Pros

  • + Pure Python — no external process
  • + Good CSS2 support
  • + Active development
  • + Free and open source

Cons

  • - No JavaScript execution
  • - No Flexbox or CSS Grid support
  • - System dependencies (cairo, pango)
  • - Slow for complex pages

Method 2: pdfkit (wkhtmltopdf)

pdfkit is a Python wrapper around wkhtmltopdf, which uses an older Qt WebKit engine. Install with pip install pdfkit and download the wkhtmltopdf binary from their website.

pdfkit_example.py
#E8A0BF">import pdfkit

# Convert a URL to PDF
pdfkit.#87CEEB">from_url(#A8D4A0">'https://example.com/invoice/12345', #A8D4A0">'invoice.pdf')

# Convert an HTML string to PDF
html = #A8D4A0">'<h1>Invoice #12345</h1><p>Total: $9.00</p>'
pdfkit.#87CEEB">from_string(html, #A8D4A0">'invoice.pdf')

# Convert an HTML file to PDF
pdfkit.from_file(#A8D4A0">'invoice.html', #A8D4A0">'invoice.pdf')

#E8A0BF">print(#A8D4A0">'PDF saved to invoice.pdf')

Deprecated: wkhtmltopdf is no longer maintained

wkhtmltopdf was archived in 2023 and is no longer receiving updates. It uses an older Qt WebKit engine that does not support modern CSS or JavaScript. While it still works for simple HTML, it should not be used for new projects.

Pros

  • + Simple three-function API
  • + Handles URL, string, and file input
  • + Good for basic layouts

Cons

  • - Deprecated and unmaintained
  • - Requires wkhtmltopdf binary
  • - Outdated WebKit engine
  • - Poor modern CSS support
  • - No JavaScript execution

Method 3: SnapRender API

SnapRender uses a real Chromium browser to render HTML to PDF. Full CSS3 support (Flexbox, Grid, custom fonts), JavaScript execution, and no local dependencies. Four lines of Python.

Convert a URL to PDF

url_to_pdf.py
#E8A0BF">import requests

# Convert any URL to PDF — 4 lines
resp = requests.#87CEEB">post(
    #A8D4A0">"https://api.snaprender.dev/v1/pdf",
    headers={#A8D4A0">"x-api-key": #A8D4A0">"sr_live_YOUR_KEY"},
    json={#A8D4A0">"url": #A8D4A0">"https://example.com/invoice/12345"}
)
#E8A0BF">with #E8A0BF">open(#A8D4A0">"invoice.pdf", #A8D4A0">"wb") #E8A0BF">as f:
    f.#87CEEB">write(resp.#87CEEB">content)

Convert raw HTML to PDF (invoice example)

Pass your HTML string in the html field instead of url. SnapRender renders it in Chromium and returns the PDF binary.

invoice.py
#E8A0BF">import requests

# Convert raw HTML to PDF — perfect #E8A0BF">for invoices
invoice_html = #A8D4A0">""#A8D4A0">"
<html>
<head>
  <style>
    body { font-family: #A8D4A0">'Helvetica Neue', sans-serif; padding: 60px; }
    .header { display: flex; justify-content: space-between; border-bottom: 3px solid #FF4B33; padding-bottom: 30px; }
    .logo { font-size: 28px; font-weight: bold; }
    .line-item { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #eee; }
    .total { font-size: 24px; font-weight: bold; color: #FF4B33; margin-top: 20px; text-align: right; }
  </style>
</head>
<body>
  <div class="header#A8D4A0">">
    <div class="logo#A8D4A0">">SnapRender</div>
    <div>Invoice #SR-2026-0412<br/>April 12, 2026</div>
  </div>
  <div class="line-item#A8D4A0">"><span>Pro Plan (1,500 requests)</span><span>$9.00</span></div>
  <div class="line-item#A8D4A0">"><span>FlareSolverr (included)</span><span>$0.00</span></div>
  <div class="total#A8D4A0">">Total: $9.00</div>
</body>
</html>
"#A8D4A0">""

resp = requests.#87CEEB">post(
    #A8D4A0">"https://api.snaprender.dev/v1/pdf",
    headers={#A8D4A0">"x-api-key": #A8D4A0">"sr_live_YOUR_KEY"},
    json={#A8D4A0">"html": invoice_html}
)
#E8A0BF">with #E8A0BF">open(#A8D4A0">"invoice.pdf", #A8D4A0">"wb") #E8A0BF">as f:
    f.#87CEEB">write(resp.#87CEEB">content)

Comparison: WeasyPrint vs pdfkit vs SnapRender

FeatureWeasyPrintpdfkitSnapRender
CSS Flexbox/GridNoNoYes
JavaScript supportNoNoYes
Custom web fontsPartialPartialYes
Raw HTML inputYesYesYes
URL inputYesYesYes
Local dependenciescairo, pangowkhtmltopdfNone
Actively maintainedYesNo (archived)Yes
Speed (simple page)~2s~1.5s~1.5s
Speed (complex page)~5-10s~3-5s~2-3s
CostFreeFreeFree tier + paid

For simple HTML without modern CSS or JavaScript, WeasyPrint works well and is completely free. For production use with complex layouts, custom fonts, or JavaScript-rendered content, SnapRender delivers the most reliable results with zero local dependencies.

Start free — 100 PDFs/month

Get your API key in 30 seconds. Convert HTML to pixel-perfect PDFs with four lines of Python. No local dependencies, no binary installs, no CSS limitations.

Get Your API Key

Frequently asked questions

It depends on your needs. WeasyPrint is best for simple HTML/CSS without JavaScript. pdfkit (wkhtmltopdf) handles more complex layouts but is deprecated. For production use with full CSS3/JS support and no local dependencies, SnapRender's API is the most reliable option — it uses a real Chromium browser for pixel-perfect rendering.

Yes. Both WeasyPrint and SnapRender support raw HTML input. With SnapRender, pass your HTML in the "html" field instead of "url" — it will render it in a Chromium browser and return the PDF. This is perfect for generating invoices, reports, and certificates from templates.

WeasyPrint supports most CSS2 and some CSS3 properties but not Flexbox or Grid. pdfkit/wkhtmltopdf uses an older WebKit engine with limited CSS3 support. SnapRender uses a full Chromium browser, so it supports all modern CSS including Flexbox, Grid, custom fonts, and media queries.

SnapRender starts free with 100 requests/month. Paid plans begin at $9/month for 1,500 requests. Each PDF conversion counts as one request. There are no credit multipliers — a PDF costs the same as a screenshot or scraping request.