One POST request. No binary install. Pay per page.
Generate pixel-perfect PDFs from HTML in any Python application — FastAPI, Django, Flask, Celery workers, Lambda functions, CLI scripts — by calling a REST API. Skip the WeasyPrint dependency hell, the wkhtmltopdf Docker layer, and the headless Chrome memory bill.
import requests
API_KEY = "sk_live_YOUR_KEY"
def render_pdf(html: str) -> bytes:
resp = requests.post(
"https://api.html2dochub.com/v1/render",
headers={"X-API-Key": API_KEY},
json={
"type": "pdf",
"html": html,
"options": {"format": "A4", "margin": "18mm"},
},
timeout=60,
)
resp.raise_for_status()
job = resp.json()
return requests.get(job["download_url"], timeout=60).content
pdf_bytes = render_pdf("<h1>Invoice #INV-0042</h1><p>Total: ₹1,200</p>")
open("invoice.pdf", "wb").write(pdf_bytes)import httpx
async def render_pdf(html: str) -> bytes:
async with httpx.AsyncClient(timeout=60) as client:
resp = await client.post(
"https://api.html2dochub.com/v1/render",
headers={"X-API-Key": API_KEY},
json={
"type": "pdf",
"html": html,
"options": {"format": "A4"},
},
)
resp.raise_for_status()
job = resp.json()
pdf_resp = await client.get(job["download_url"])
return pdf_resp.contentfrom django.http import FileResponse
from django.template.loader import render_to_string
import requests, io
def invoice_pdf(request, invoice_id: int):
html = render_to_string("invoices/detail.html", {"invoice": Invoice.objects.get(pk=invoice_id)})
resp = requests.post(
"https://api.html2dochub.com/v1/render",
headers={"X-API-Key": settings.HTML2DOCHUB_API_KEY},
json={"type": "pdf", "html": html, "options": {"format": "A4"}},
timeout=60,
)
resp.raise_for_status()
pdf = requests.get(resp.json()["download_url"], timeout=60).content
return FileResponse(io.BytesIO(pdf), filename=f"invoice-{invoice_id}.pdf",
content_type="application/pdf")from celery import shared_task
import requests, uuid
@shared_task(bind=True, max_retries=3, default_retry_backoff=True)
def generate_report_pdf(self, report_id: int):
report = Report.objects.get(pk=report_id)
idempotency_key = f"report-{report_id}-v{report.version}"
try:
resp = requests.post(
"https://api.html2dochub.com/v1/render",
headers={"X-API-Key": settings.HTML2DOCHUB_API_KEY},
json={
"type": "pdf",
"mode": "async",
"html": report.render_html(),
"webhook_url": f"{settings.BASE_URL}/webhooks/pdf-ready",
"tag": f"report:{report_id}",
"idempotency_key": idempotency_key,
},
timeout=30,
)
resp.raise_for_status()
except requests.RequestException as exc:
raise self.retry(exc=exc)
report.job_id = resp.json()["id"]
report.save()Pay only for pages rendered. No subscriptions. No minimum monthly fee.
Free account. No credit card required. API ready in minutes.
Get your free API key