typefold-client
Python client for the Typefold API. Needs Python 3.10 or later; its only dependency is
httpx. It has a blocking TypefoldClient and an asyncio AsyncTypefoldClient with the
same methods.
pip install typefold-client
Create one client per API key and reuse it. The key needs render:create, render:read and
template:read, and render:cancel to cancel renders.
from typefold import TypefoldClient
client = TypefoldClient("https://pdf.example.com", api_key)
Close it with client.close() when your application stops, or use it as a context manager.
Render a report
render_report renders a published report or saved template by name or ID and returns the PDF
or XLSX bytes:
from typefold import (
Orientation,
PageSize,
PdfMetadata,
RenderReportRequest,
ReportFormat,
ReportPdfOptions,
TemplateRef,
)
pdf = client.render_report(
RenderReportRequest(
template=TemplateRef.by_name("SoldGoodsForWarehouseReportPdf"),
data={"Data": {"reportRows": report_rows, "summaryRows": summary_rows}},
pdf=ReportPdfOptions(
page_size=PageSize.A4,
orientation=Orientation.LANDSCAPE,
metadata=PdfMetadata(title="Sold goods, week 39"),
),
)
)
xlsx = client.render_report(
RenderReportRequest(
format=ReportFormat.XLSX,
template=TemplateRef.by_name("SoldGoodsForWarehouseReportXlsx"),
data=data,
)
)
data is sent as written: dictionary keys keep their names, and dates, decimals, UUIDs and
dataclasses are written as JSON.
Templates imported from jsreport keep their shortids, so stored shortids still work:
TemplateRef.by_shortid("rkJTnK2ce"). list_reports returns each template's shortid.
A retry with the same idempotency_key returns the render the key already created.
get_report_data_schema returns the JSON Schema of a report's data; list_reports lists
templates and the formats each renders.
Render notification text
render_string renders a template string synchronously, without a PDF, for email bodies,
subjects, SMS and push messages. The Handlebars engine runs #if, #each and your helpers:
from typefold import RenderStringRequest, StringEngine, StringFormat
body = client.render_string(
RenderStringRequest(
template="{{#if (isEqual Status 'paid')}}Paid{{else}}Due{{/if}}: {{Booking.Code}}",
data=model,
engine=StringEngine.HANDLEBARS,
helpers="function isEqual(a, b) { return a === b; }",
format=StringFormat.HTML,
)
)
StringFormat.HTML escapes values; StringFormat.TEXT (the default) writes them as they are.
Without engine, only {{path}} placeholders are replaced.
Render HTML or a template version
render creates a render, waits for it and downloads the PDF:
from typefold import DirectRenderRequest, HtmlDocument, PageSize, RenderSettings, TemplateVersionRenderRequest
pdf = client.render(
DirectRenderRequest(
document=HtmlDocument(html="<h1>Invoice 42</h1>"),
settings=RenderSettings(page_size=PageSize.A4),
)
)
ticket = client.render(
TemplateVersionRenderRequest(template_version_id=version_id, data={"ticket": {"number": "TK-1"}})
)
It gives up after two minutes unless you pass a timeout in seconds. To render in the
background, use create_render, then wait_for_render, download or cancel_render, or the
API's render webhooks.
Upload to a storage destination
To also upload the PDF to one of the workspace's storage destinations, pass destination with
the destination's name. path is the object's key after the destination's prefix;
{renderId} is replaced with the render's ID, and it defaults to {renderId}.pdf (or .png, .jpeg,
.webp for an image):
from typefold import RenderDestination, TemplateVersionRenderRequest
render = client.create_render(
TemplateVersionRenderRequest(
template_version_id=version_id,
data=invoice,
destination=RenderDestination(name="invoices-bucket", path="2026/{renderId}.pdf", keep_copy=False),
)
)
render = client.wait_for_render(render)
key = render.result.destination.key
result.destination.key is the object's full key in the bucket. With keep_copy=False the API
keeps no copy: the result has no download_url or expires_at, download raises ValueError,
and render raises it before creating the render, so use create_render and read the PDF from the bucket. Batch items take destination too. A
failed upload fails the render with the diagnostic code destination_upload_failed.
Render a batch
create_batch creates up to 100 renders in one request; if any item is invalid, none is created.
Items are DirectRenderRequest or TemplateVersionRenderRequest; their wait_ms is not sent.
wait_for_batch polls until no item is queued or processing (10 minutes unless you pass a
timeout), and download_batch returns one PDF of every item in order:
batch = client.create_batch(
[TemplateVersionRenderRequest(template_version_id=version_id, data=ticket) for ticket in tickets],
idempotency_key="tickets-2026-10-12",
)
batch = client.wait_for_batch(batch)
if batch.counts.succeeded == batch.total:
pdf = client.download_batch(batch.id)
A failed item does not stop the others; batch.items gives each render's id and state, to
read, download or cancel it as a single render. get_batch reads a batch again.
Embed the content editor
create_embed_token makes a short-lived token that opens the embedded content editor for one
content set of a published template, so your own customers can edit their content in your app.
Call it on your server each time a customer opens the editor, and send only its url to the
page, which frames it with /embed.js:
from typefold import CreateEmbedTokenRequest
embed = client.create_embed_token(
CreateEmbedTokenRequest(
template_id=template_id,
content_set_id=content_set_id,
expires_in_minutes=30,
label=customer.id,
)
)
# Send embed.url to your page; keep embed.id to revoke the token early.
client.revoke_embed_token(embed.id)
The key needs template:read, template:content and render:create. The workspace must list
your app's origin under Settings → Embedding first.
Async
AsyncTypefoldClient has the same methods as coroutines:
from typefold import AsyncTypefoldClient
async with AsyncTypefoldClient("https://pdf.example.com", api_key) as client:
pdf = await client.render_report(request)
Errors
A request the API refuses with 429 RATE_LIMITED, such as a burst of parallel renders, is sent
again after its Retry-After up to max_rate_limit_retries times (3 by default; pass 0 to turn
this off). Other failures raise TypefoldError. error.code is the API's code, such as
NOT_FOUND, RATE_LIMITED or RENDER_TIMEOUT, and error.retryable says whether a retry can
help. When a render was created but failed or timed out, error.error.render_id names it.
Versions
Use a client no newer than your server: older clients keep working because /v1 only grows,
but a newer client's fields or endpoints answer 400 or 404 on an older server.
httpx client
To share an httpx.Client (or httpx.AsyncClient), pass it as http_client with a timeout
above 95 seconds, the longest a report PDF can take. The API key goes on each request, not the
client, and never to download URLs. A client you pass in is not closed with the
TypefoldClient.
Development
cd clients/python
python -m venv .venv && .venv/bin/pip install -e '.[test]'
.venv/bin/pytest && .venv/bin/mypy src tests && .venv/bin/ruff check . && .venv/bin/ruff format --check .
Metadata
Release files for typefold-client 0.6.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| typefold_client-0.6.3.tar.gz | 23.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| typefold_client-0.6.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.4 kB
Release files / typefold_client-0.6.3.tar.gz
| Download URL | typefold_client-0.6.3.tar.gz |
|---|---|
| Size | 23.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
86d44ee300e9ed8f7f0b17cefae7be24fe9b17831b3474fc3ea9ead0bc122690
|
|
BLAKE2b-256 checksum How to use checksums |
88b201fbb88b361f950ca4cf81ffcb9bf1c69c1c364d5cff779805485d7dbbce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / typefold_client-0.6.3-py3-none-any.whl
| Download URL | typefold_client-0.6.3-py3-none-any.whl |
|---|---|
| Size | 19.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b353e6b2658b7522d7fd7b884635b92b86994d0d69319b546b433d0193bdf667
|
|
BLAKE2b-256 checksum How to use checksums |
edc6a065310646f21160b6857abf0236c4c57574282667dae5d2e08920957c45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for typefold-client 0.6.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for typefold_client-0.6.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.