Django5 min read · June 12, 2026

Generate PDFs in Django Without WeasyPrint System Dependencies

Generate PDFs from Django views using a REST API. Render a template to HTML, call the API with requests, return the bytes. No GTK, no system packages.

WeasyPrint is the most popular HTML-to-PDF library for Python, but it requires GTK and Pango system packages that add complexity to any Docker or cloud deployment. xhtml2pdf is older and has even more limited CSS support.

The HTML to PDF API solves this with a single requests call. No system dependencies, modern CSS rendering, and no extra pip packages beyond requests.

Basic Django View

views.pypython
import os
import requests
from django.template.loader import render_to_string
from django.http import HttpResponse, Http404

API_URL = "https://platform.htmltopdfapi.co/api/v1/pdf/generate"
HEADERS = {
    "Authorization": f"Bearer {os.environ['HTMLTOPDF_API_KEY']}",
    "Content-Type": "application/json",
    "Accept": "application/pdf",
}

def invoice_pdf(request, pk):
    try:
        invoice = Invoice.objects.get(pk=pk, user=request.user)
    except Invoice.DoesNotExist:
        raise Http404

    html = render_to_string("invoices/pdf.html", {"invoice": invoice}, request=request)

    res = requests.post(API_URL, headers=HEADERS, json={"html": html, "paper_size": "a4"})
    res.raise_for_status()

    return HttpResponse(
        res.content,
        content_type="application/pdf",
        headers={"Content-Disposition": f'inline; filename="invoice-{pk}.pdf"'},
    )

Async View (Django 4.1+)

If your project uses async Django, use httpx for a non-blocking call:

async views.pypython
import httpx
from django.http import HttpResponse

async def invoice_pdf_async(request, pk):
    invoice = await Invoice.objects.aget(pk=pk)
    html    = render_to_string("invoices/pdf.html", {"invoice": invoice})

    async with httpx.AsyncClient() as client:
        res = await client.post(
            "https://platform.htmltopdfapi.co/api/v1/pdf/generate",
            headers={"Authorization": f"Bearer {os.environ['HTMLTOPDF_API_KEY']}",
                     "Content-Type": "application/json", "Accept": "application/pdf"},
            json={"html": html, "paper_size": "a4"},
            timeout=30,
        )
        res.raise_for_status()

    return HttpResponse(res.content, content_type="application/pdf",
                        headers={"Content-Disposition": 'inline; filename="invoice.pdf"'})

URL-to-PDF for Public Views

If your PDF view is publicly accessible, pass a URL instead of rendering HTML server-side:

url-based snippetpython
res = requests.post(
    API_URL,
    headers=HEADERS,
    json={"url": request.build_absolute_uri(f"/invoices/{pk}/"), "paper_size": "a4"},
)

What You Remove

Switching to a REST API removes WeasyPrint's GTK and Pango system dependencies entirely. Your Docker image shrinks, your build process simplifies, and CSS rendering keeps up with modern standards automatically.