pdf4me
Python client for the PDF4me API. Convert, optimize, merge, split, stamp, and extract content from documents with async functions and typed results.
Requires Python 3.11 or newer and a PDF4me API key.
Upgrading from 0.8.x? Version 1.0 is a rewrite and nothing from 0.8.x keeps working. See Migrating from 0.8.x.
Installation
python -m pip install pdf4me
To install from a source checkout, run python -m pip install . in this
package directory. The import name is pdf4me.
Quick start
Set your API key in the environment:
export PDF4ME_API_KEY="your-api-key"
Save this as optimize_pdf.py and put an input.pdf in the same working
directory. The example uploads the PDF for optimization and saves the result
as optimized.pdf.
import asyncio
import os
from pathlib import Path
from pdf4me import Pdf4meClient
from pdf4me.optimize import optimize
async def main() -> None:
api_key = os.environ.get("PDF4ME_API_KEY")
if not api_key:
raise SystemExit("Set PDF4ME_API_KEY before running this example.")
source = Path("input.pdf")
async with Pdf4meClient(api_key) as client:
result = await optimize(
client, source.read_bytes(), doc_name=source.name
)
Path("optimized.pdf").write_bytes(result)
print("Saved optimized.pdf")
if __name__ == "__main__":
asyncio.run(main())
Run it with:
python optimize_pdf.py
Using actions
Import actions from their module. Pass the client first, document content next, and action options as keyword arguments. Reuse one client for multiple calls; the async context manager closes its connections when finished.
For example, inside an async function with an open client:
from pdf4me.pdf import get_pdf_metadata
from pdf4me.edit import StampAlignX, stamp
metadata = await get_pdf_metadata(client, data, doc_name="input.pdf")
print(metadata.page_count)
stamped = await stamp(client, data, text="DRAFT", align_x=StampAlignX.Center)
Path("stamped.pdf").write_bytes(stamped)
Here, data contains the PDF bytes, for example Path("input.pdf").read_bytes().
Enums such as OptimizeProfile and StampAlignX are exported by the action's
module.
Source documents accept bytes, base64 strings, pdf4meblobid://<id> references,
or HTTPS URLs. Bytes are encoded automatically. File actions return bytes;
JSON actions return typed models, such as DocMetadata or SplitPdfRes.
Queued jobs are polled automatically until the result is available.
Every action accepts extra, a dictionary of additional API request fields.
Use the API's field names for these values.
Client configuration and errors
async with Pdf4meClient(
api_key,
base_url="https://api.pdf4me.com",
timeout=100,
max_wait=300,
) as client:
result = await optimize(client, data)
Durations are in seconds. timeout controls the HTTP request timeout;
max_wait limits waiting for a queued job.
OCR actions (convert_ocr_pdf, image_extract_text) wait at least 10 minutes
(DEFAULT_OCR_MAX_WAIT), or longer if the client's max_wait is higher. Pass
max_wait= to an OCR call to override it for that call.
API failures raise Pdf4meException, importable from pdf4me, with the status
code, API message, and trace ID. A queued job exceeding max_wait raises
TimeoutError.
Available modules
The client provides 106 actions across 18 modules.
| Module | Docs section | Actions |
|---|---|---|
pdf4me.ai_document_extraction |
AI Document Extraction | 14 |
pdf4me.barcode |
Barcode | 7 |
pdf4me.convert |
Convert | 13 |
pdf4me.edit |
Edit | 7 |
pdf4me.excel |
Excel | 1 |
pdf4me.extract |
Extract | 8 |
pdf4me.find_search |
Find Search | 2 |
pdf4me.forms |
Forms | 2 |
pdf4me.generate |
Generate | 6 |
pdf4me.image |
Image | 13 |
pdf4me.merge_split |
Merge & Split | 5 |
pdf4me.optimize |
Optimize | 1 |
pdf4me.organize |
Organize | 5 |
pdf4me.pdf |
16 | |
pdf4me.pdf4me |
PDF4me | 1 |
pdf4me.security |
Security | 2 |
pdf4me.word |
Word | 2 |
pdf4me.zugferd |
ZUGFeRD | 1 |
Migrating from 0.8.x
Version 1.0 replaces the whole surface of the library. Upgrading will not be a drop-in change; the four things that break every 0.8.x program are:
- Calls are async. Every action is a coroutine and must be awaited from inside an event loop.
- The client classes are gone.
MergeClient,OptimizeClient,SplitClientand the rest are replaced by plain functions, imported from the module for their API section, that take the client as their first argument. config.propertiesis no longer read. Pass the API key toPdf4meClientyourself.jprops,requestsandkeyringare no longer dependencies.- Python 2.7 and 3.6 are no longer supported. The minimum is 3.11.
Files are passed as bytes rather than as the file handles
FileReader().get_file_handler(...) used to return, so Path(...).read_bytes()
replaces it. Actions that produce a document return bytes; actions that
answer with data return a typed model.
| 0.8.x | 1.0 |
|---|---|
Pdf4meClient(token=token) |
Pdf4meClient(api_key) |
MergeClient(c).merge_2_pdfs(f1, f2) |
await merge(client, [a, b]) |
OptimizeClient(c).optimize_by_profile(profile, f) |
await optimize(client, data, profile=...) |
SplitClient(c).split_by_page_nr(n, f) |
await split_pdf(client, data, split_action_number=n) |
ExtractClient(c).extract_pages(page_nrs, f) |
await extract_pages(client, data, page_numbers=...) |
StampClient(c).text_stamp(text, pages, ax, ay, f) |
await stamp(client, data, text=..., pages=...) |
BarcodeClient(c).read_barcodes_by_type(t, f) |
await read_barcodes(client, data, barcode_type=[t]) |
The table covers the common calls; the module table above says where to import everything else from, and each action's docstring links its documentation page.
Contributing
Issues and pull requests are welcome on GitHub.
src/pdf4me/_generated/ is generated from the PDF4me OpenAPI description.
Edits made there are overwritten the next time the client is regenerated, so
please open an issue for anything wrong in that tree - a missing field, a wrong
type - rather than patching it. The client and the action modules around it are
hand-written and are the right place for a pull request.
The tests run against a fake transport, so they need no API key and make no network calls:
uv run pytest
Releasing
Releases are published to PyPI by the release workflow when a version tag is pushed.
-
Bump the version.
uv versionsets it inpyproject.tomland updatesuv.lock. Commit the change onmaster:uv version 1.0.3 git commit -am "chore: release 1.0.3" git push origin master
-
Tag that commit with the same version, prefixed with
v, and push the tag:git tag v1.0.3 git push origin v1.0.3
The workflow runs the tests on Python 3.11 to 3.14 and checks that the tag
matches the pyproject.toml version, stopping before anything ships if they
differ. It then builds the distributions, publishes them to PyPI and creates
the GitHub release with generated notes. Push only the tag; don't create the
GitHub release by hand, because the workflow creates it.
Publishing uses PyPI trusted publishing, so no token is stored in the
repository. This repository, release.yml and the pypi environment must be
registered as a trusted publisher of the pdf4me project on PyPI.
Support
- Documentation: docs.pdf4me.com
- API keys and account: dev.pdf4me.com
- Bugs and feature requests: GitHub issues
- Email: support-dev@pdf4me.com
Quote the trace_id from a Pdf4meException when reporting an API failure.
Looking for the PDF4me online tools rather than the API? See pdf4me.com.
License
Released under the MIT License.
Metadata
Release files for pdf4me 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pdf4me-1.1.0.tar.gz | 143.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pdf4me-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 637.0 kB
Release files / pdf4me-1.1.0.tar.gz
| Download URL | pdf4me-1.1.0.tar.gz |
|---|---|
| Size | 143.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c260e9d5ec2683a37a8e958ffe62ebcb0c82ad0c11a09063b7890560b039f5fb
|
|
BLAKE2b-256 checksum How to use checksums |
2fde760c68c1b3444066d77d1cbe0c496cc6fd9256e4bc3e59af506146e1533a
|
| 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 8, 2026.
Transparency logRelease files / pdf4me-1.1.0-py3-none-any.whl
| Download URL | pdf4me-1.1.0-py3-none-any.whl |
|---|---|
| Size | 494.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ecb6cd7068c7fc1f6b9cb8dd63aeb29d7f1807f063c32ef17555c83e4da185f7
|
|
BLAKE2b-256 checksum How to use checksums |
2637270752b7552d1a7fd0edeb024f3845756b855c38264cd28dab27ee3d8849
|
| 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 8, 2026.
Transparency log