pennylane-client
Async Python client for the Pennylane API (Company API v2).
uv add pennylane-client
Why it's built this way
The client is generated from Pennylane's own published OpenAPI spec, the same way boondmanager-client is generated from BoondManager's spec:
pennylane_client/_spec/spec.json.gz: vendored bundle with the full operation registry (175 operations) and every JSON request-body schema, extracted from the public OpenAPI file.pennylane_client/api.py: generated surface, one method per operation, named after the spec'soperationId(so the names match Pennylane's docs).- Every JSON request body is validated against the matching schema before
sending: a mismatch raises
PennylaneValidationErrorwith the exact violations, instead of an opaque 4xx from the API.
Usage
from pennylane_client import PennylaneClient
async with PennylaneClient(api_token) as client:
# One page of a list operation: {"items", "has_more", "next_cursor"}
page = await client.api.customer_invoices.get_customer_invoices(limit=100)
# A single object
invoice = await client.api.customer_invoices.get_customer_invoice(42)
# Every item of a cursor-paginated list, across pages
async for invoice in client.paginate(
client.api.customer_invoices.get_customer_invoices,
filter=[{"field": "date", "operator": "gteq", "value": "2026-01-01"}],
):
print(invoice["invoice_number"])
# Or all at once
customers = await client.fetch_all(client.api.customers.get_customers)
# Writes are validated against the spec before sending
await client.api.customers.post_company_customer(
{"name": "Acme", "billing_address": {...}}
)
client.api.<group>.<method>(...): groups are the spec's tags (customer_invoices,customers,supplier_invoices,transactions, ...). Path parameters are positional, query parameters are keyword-only, and every method returns the decoded JSON.filteraccepts a list of dicts, JSON-encoded for you.- Find the right method by grepping
docs/API.mdor withpennylane_client.ENDPOINT_INDEX; each method's docstring lists its query parameters and request body.
Client options
max_concurrent_requests(default 5) caps the requests in flight, or passsemaphoreto share one limit across clients.max_retries(default 3): HTTP 429 (rate limit) responses are retried, waiting for theRetry-Afterheader.validate=Falseon a write method skips the schema check (escape hatch for a payload the published spec does not describe correctly).
Errors
PennylaneValidationError: the request body does not match the schema (raised before sending).PennylaneAPIError: the API returned an HTTP error (status_code,endpoint,detail).PennylaneError: base class, also raised on network errors.
Development
uv sync --dev
uv run ruff format --check . && uv run ruff check .
uv run pytest
Updating the spec
uv run python scripts/fetch_spec.py # download and vendor the OpenAPI file
uv run python scripts/generate_api.py # regenerate api.py and docs/API.md
Review the diff of docs/API.md to see what changed in the API.
Releasing
Bump version in pyproject.toml, merge, then push a tag:
git tag v0.1.0 && git push origin v0.1.0
The publish.yml workflow builds the package and publishes it to PyPI with
trusted publishing (no token needed).
Release files for pennylane-client 0.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 | |
|---|---|---|---|
| pennylane_client-0.1.0.tar.gz | 84.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pennylane_client-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 151.8 kB
Release files / pennylane_client-0.1.0.tar.gz
| Download URL | pennylane_client-0.1.0.tar.gz |
|---|---|
| Size | 84.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
25297fa1d40edde47ad74eac808d72328a27424251adabb30765d9a8b44d627e
|
|
BLAKE2b-256 checksum How to use checksums |
499d0191216c208a1c2b4a5beb1afdb3654887b4a2818a4cd493a24255c1494e
|
| 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 Sep 25, 2026.
Transparency logRelease files / pennylane_client-0.1.0-py3-none-any.whl
| Download URL | pennylane_client-0.1.0-py3-none-any.whl |
|---|---|
| Size | 66.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cc63acb6fbd05606c7cc0ae643f7638cb33da2a04da6309ea1c3162c1f4b050b
|
|
BLAKE2b-256 checksum How to use checksums |
2776699af4e93c1f702aed43fd083c974d0567d979682114fe64f370b4763d8f
|
| 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 Sep 25, 2026.
Transparency log