A modern, fully-typed Python client for the Gotenberg PDF generation API, with sync and async support.
pip install "gotenberg-client[httpx]"from gotenberg_client import GotenbergClient
from pathlib import Path
with GotenbergClient("http://localhost:3000") as client:
with client.chromium.html_to_pdf() as route:
response = route.index(Path("my-index.html")).run()
response.to_file(Path("my-index.pdf"))Four lines (excluding imports) to convert HTML to PDF.
Gotenberg is a powerful, Docker-based API for PDF generation and manipulation using Chromium and LibreOffice under the hood. This client gives you a clean, Pythonic interface to all of its capabilities, so you can skip the multipart form-data boilerplate and focus on your documents.
- Fully typed with concrete return types and full
py.typedsupport - Sync and async APIs with identical interfaces
- Pluggable HTTP backends -- httpx (with HTTP/2), httpx2 (with HTTP/2), niquests, or requests — install whichever you prefer
- Pathlib-native -- pass
Pathobjects directly, no manual file handling - Thoroughly tested against a real Gotenberg server across multiple Python versions
- Broad route coverage including Chromium, LibreOffice, PDF merge/convert/split, health checks, and more
- If there's a route you need, just ask!
An HTTP backend is required. Pick the one that suits your project:
pip install "gotenberg-client[httpx]" # recommended — HTTP/2 and async support
pip install "gotenberg-client[httpx2]" # alternative — HTTP/2 and async support
pip install "gotenberg-client[niquests]" # alternative — HTTP/2 and async support
pip install "gotenberg-client[requests]" # sync-onlyIf you need MIME-type detection for automatic content-type headers, add the magic extra:
pip install "gotenberg-client[httpx,magic]"On platforms where libmagic is unavailable or unreliable (such as Windows), use the magika extra instead, which needs no native library:
pip install "gotenberg-client[httpx,magika]"If both are installed, magic is preferred. With neither, detection falls back to the standard library's mimetypes.
from gotenberg_client import GotenbergClient
from pathlib import Path
with GotenbergClient("http://localhost:3000") as client:
with client.chromium.html_to_pdf() as route:
response = route.index(Path("my-index.html")).run()
response.to_file(Path("my-index.pdf"))from gotenberg_client import AsyncGotenbergClient
from pathlib import Path
async with AsyncGotenbergClient("http://localhost:3000") as client:
async with client.chromium.html_to_pdf() as route:
response = await route.index(Path("my-index.html")).run()
response.to_file(Path("my-index.pdf"))from gotenberg_client import GotenbergClient
from pathlib import Path
with GotenbergClient("http://localhost:3000") as client:
with client.chromium.html_to_pdf() as route:
response = route.index(Path("my-index.html")).resource("image.png").resource("style.css").run()
response.to_file(Path("my-index.pdf"))from gotenberg_client import GotenbergClient
from gotenberg_client.options import PageOrientation
with GotenbergClient("http://localhost:3000") as client:
with client.chromium.url_to_pdf() as route:
response = route.url("https://hello.world").orient(PageOrientation.Landscape).run()
response.to_file(Path("my-world.pdf"))from gotenberg_client import GotenbergClient
from gotenberg_client.options import PdfAFormat
from pathlib import Path
with GotenbergClient("http://localhost:3000") as client:
with client.chromium.html_to_pdf() as route:
response = (
route.index(Path("my-index.html")).resources(["image.png", "style.css"]).pdf_format(PdfAFormat.A2b).run()
)
response.to_file(Path("my-index.pdf"))from gotenberg_client import GotenbergClient
from datetime import datetime
with GotenbergClient("http://localhost:3000") as client:
with client.chromium.html_to_pdf() as route:
response = (
route.index("my-index.html")
.metadata(
title="My Document",
author="John Doe",
subject="Example PDF",
keywords=["sample", "document", "test"],
creation_date=datetime.now(),
)
.run()
)
response.to_file(Path("my-index.pdf"))Install the backend you want, then select it explicitly (or rely on auto-detection):
from gotenberg_client import GotenbergClient
# httpx — pip install "gotenberg-client[httpx]"
with GotenbergClient("http://localhost:3000", backend="httpx") as client:
...
# httpx2 — pip install "gotenberg-client[httpx2]"
with GotenbergClient("http://localhost:3000", backend="httpx2") as client:
...
# niquests — pip install "gotenberg-client[niquests]"
with GotenbergClient("http://localhost:3000", backend="niquests") as client:
...
# requests (sync-only) — pip install "gotenberg-client[requests]"
with GotenbergClient("http://localhost:3000", backend="requests") as client:
...
# auto — tries httpx first, then niquests, then httpx2 (default)
with GotenbergClient("http://localhost:3000") as client:
...Pass a (username, password) tuple for HTTP Basic Authentication with any backend,
or omit auth / pass None for the backend's default behavior:
from gotenberg_client import GotenbergClient
with GotenbergClient("http://localhost:3000", auth=("user", "secret")) as client:
with client.chromium.html_to_pdf() as route:
response = route.index(Path("my-index.html")).run()
response.to_file(Path("my-index.pdf"))You can also pass native authentication supported by your selected backend:
import httpx
with GotenbergClient(
"https://gotenberg.example",
backend="httpx",
auth=httpx.DigestAuth("user", "secret"), # Or your own httpx.Auth subclass
) as client:
...HTTPX and HTTPX2 accept their own Auth instances for sync and async clients. Requests
accepts requests.auth.AuthBase instances for sync clients. Niquests accepts its own
AuthBase instances, plus AsyncAuthBase instances with an async client where supported
by the installed Niquests version. Custom authentication should subclass the selected
backend's auth base class; bare callables are not accepted.
With backend="auto", native authentication selects its backend, including for custom
subclasses. Tuples and None retain the default backend preference. You can set backend=
explicitly; an incompatible auth object raises TypeError during client construction.
Custom HTTPX auth can access the complete multipart upload body for signing by
setting requires_request_body = True. See the authentication guide
for details and async usage.
All routes follow the same pattern:
- Add the file(s) you want to process
- Configure options the route supports
- Call
.run()and receive your result
Responses are either a SingleFileResponse or ZipFileResponse, each providing:
to_file(path)-- write the result to diskextract_to(directory)-- extract a ZIP result into a directory (ZipFileResponse only)- Access to
headers,status_code, andcontentfrom the underlying response
Both the client and routes should be used as context managers for proper cleanup.
If that isn't possible, call .close() explicitly:
from gotenberg_client import GotenbergClient
from pathlib import Path
client = GotenbergClient("http://localhost:3000")
try:
route = client.merge.merge()
try:
response = route.merge([Path("myfile.pdf"), Path("otherfile.pdf")]).run()
response.to_file(Path("merged.pdf"))
finally:
route.close()
finally:
client.close()For the full API reference and more examples, see the documentation.
gotenberg-client is distributed under the terms of the MPL 2.0 license.