Python integration

Generate PDFs from HTML in Python

Call the CheapPDF API with the popular requests library. Send HTML or a URL, decode the response and save the PDF, with timeouts and retries built in.

hello.py
# pip install requests
import base64, os, requests

resp = requests.post(
    "https://www.cheap-pdf.com/generate-pdf-public",
    data={
        "apikey": os.environ["CHEAPPDF_API_KEY"],
        "filename": "hello.pdf",
        "fileorhtmlorurl": "<h1>Hello from Python</h1>",
    },
    timeout=60,
)
result = resp.json()

with open("hello.pdf", "wb") as f:
    f.write(base64.b64decode(result["data"]["base64_pdf"]))

Prerequisites

  • Python 3.8 or later and pip install requests.
  • Your API key in the CHEAPPDF_API_KEY environment variable. Never hard-code it or commit it.

A reusable client

This class authenticates every call, sets separate connect and read timeouts, retries only transient failures, and raises a CheapPdfError that carries the API’s error code and HTTP status.

cheappdf.py
# cheappdf.py
import base64
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

ENDPOINT = "https://www.cheap-pdf.com/generate-pdf-public"


class CheapPdfError(Exception):
    def __init__(self, message, code=None, status=0):
        super().__init__(message)
        self.code = code
        self.status = status


class CheapPdf:
    def __init__(self, api_key=None, timeout=60):
        self.api_key = api_key or os.environ.get("CHEAPPDF_API_KEY", "")
        if not self.api_key:
            raise ValueError("CHEAPPDF_API_KEY is not set")
        self.timeout = timeout

        self.session = requests.Session()
        # Retry connection errors and 5xx only. POST must be allowed explicitly.
        retry = Retry(
            total=2,
            backoff_factor=1,
            status_forcelist=[500, 502, 503, 504],
            allowed_methods=["POST"],
            raise_on_status=False,
        )
        self.session.mount("https://", HTTPAdapter(max_retries=retry))

    def from_html(self, html, filename="document.pdf", **options):
        return self._send(html, filename, options)

    def from_url(self, url, filename="page.pdf", **options):
        return self._send(url, filename, options)

    def _send(self, source, filename, options):
        data = {"apikey": self.api_key, "filename": filename, "fileorhtmlorurl": source, **options}
        try:
            resp = self.session.post(ENDPOINT, data=data, timeout=(10, self.timeout))
        except requests.RequestException as exc:
            raise CheapPdfError(f"Network error: {exc}") from exc

        try:
            result = resp.json()
        except ValueError:
            result = {}

        if resp.status_code != 200 or not result.get("success"):
            raise CheapPdfError(
                result.get("message", f"Conversion failed (HTTP {resp.status_code})"),
                code=result.get("error"),
                status=resp.status_code,
            )

        pdf = base64.b64decode(result["data"]["base64_pdf"])
        if not pdf.startswith(b"%PDF"):
            raise CheapPdfError("Response was not a valid PDF")
        return pdf

Using it

generate.py
from cheappdf import CheapPdf, CheapPdfError

client = CheapPdf()

try:
    pdf = client.from_html(
        "<h1>Invoice #1042</h1><p>Total: $9.00</p>",
        filename="invoice-1042.pdf",
        width=800,
        height=1100,
    )
    with open("invoice-1042.pdf", "wb") as f:
        f.write(pdf)
except CheapPdfError as err:
    if err.code == "free_plan_limit_reached":
        print("Monthly free limit reached. Upgrade to continue.")
    elif err.status == 400:
        print(f"Request rejected [{err.code}]: {err}")  # fix the input, do not retry
    else:
        print(f"Temporary failure, try again later: {err}")

Request parameters and response handling

Required fields: apikey, fileorhtmlorurl and filename. Optional: width and height in pixels, landscape and printBackground (pass "true" or "false"). To upload an .html file (up to 5 MB), use files={"fileorhtmlorurl": open("page.html", "rb")} instead of putting it in data.

On success the JSON has success: true and the PDF in data.base64_pdf. On failure, message explains why and error holds a code such as invalid_api_key.

Batch conversion with limited concurrency

Use a small thread pool and collect failures per document. Request rate limits are not published, so keep the worker count low.

batch.py
from concurrent.futures import ThreadPoolExecutor, as_completed
from cheappdf import CheapPdf, CheapPdfError

client = CheapPdf()
reports = {"q1": "<h1>Q1 report</h1>", "q2": "<h1>Q2 report</h1>", "q3": "<h1>Q3 report</h1>"}


def build(name, html):
    pdf = client.from_html(html, filename=f"{name}.pdf")
    with open(f"{name}.pdf", "wb") as f:
        f.write(pdf)
    return name


failed = {}
with ThreadPoolExecutor(max_workers=3) as pool:  # keep concurrency low
    futures = {pool.submit(build, n, h): n for n, h in reports.items()}
    for future in as_completed(futures):
        name = futures[future]
        try:
            future.result()
        except CheapPdfError as err:
            failed[name] = str(err)

print("Failed:", failed or "none")

Run large batches from a worker or scheduled job (for example Celery or cron), not inside a web request. The free plan allows 10 conversions per day, so batches need the Premium plan.

Python FAQ

Which Python library should I use?

requests is a good default. Any HTTP client works because the API is a single form POST. For async code, httpx or aiohttp can send the same fields.

How do I save the PDF in Python?

Decode data.base64_pdf with base64.b64decode() and write the bytes to a file opened in 'wb' mode.

Should I retry failed requests?

Retry network errors, timeouts and 5xx responses a couple of times with backoff. Do not retry 400 responses, because the same request will fail again.

Can I use it from Django or Flask?

Yes. Render a template to an HTML string, call from_html(), and return the bytes in an HttpResponse or Response with the application/pdf content type.

Add PDF generation to your Python app

Create an API key and run the quick start in minutes.

Subscribe to Our Newsletter for Fast HTML to PDF Updates

Stay updated on new features for our fast, simple, and affordable HTML to PDF API.