Python SDK and Tools for Poland's KSeF (Krajowy System e-Faktur) API
Project description
ksef2 SDK
Typed Python SDK for automating KSeF 2.0 invoicing workflows.
Built against the published KSeF OpenAPI specification and checked daily so
the SDK stays aligned with API changes.
100% endpoint coverage, sync and async clients, FA(3) invoice building, and
tools for authentication, sessions, exports, tokens, permissions, and
certificates.
What is ksef2?
ksef2 is a community-maintained Python SDK for Poland's KSeF v2 API. It is
designed for developers building custom integrations, automations, back-office
tools, and invoice-processing pipelines around KSeF without hand-writing HTTP
requests, polling loops, encryption handling, or FA(3) XML.
This project is not published, endorsed, or supported by Poland's Ministry of Finance. Official KSeF documentation remains the source of truth for API behavior.
The SDK currently targets KSeF OpenAPI version 2.6.1.
Install
# standard pip installation
pip install ksef2
# install with uv inside an application project
uv add ksef2
Requires Python 3.12 or newer.
Optional extras:
pip install "ksef2[pdf]" # local invoice PDF rendering
pip install "ksef2[runtime-checks]" # optional beartype runtime checks
Runtime checks are disabled unless KSEF2_RUNTIME_CHECKS=1 is set.
The CLI is distributed separately under stacking-hq/ksef2-cli.
Install it when you want terminal workflows,
scriptable commands, or local profiles:
uv tool install ksef2-cli
# or
pipx install ksef2-cli
Authenticate
Use the authentication method that matches the environment you are working with.
from ksef2 import Client, Environment
from ksef2.core.xades import (
load_certificate_from_pem,
load_private_key_from_pem
)
client = Client(Environment.TEST)
# local TEST workflows can use an SDK-generated certificate.
test = client.authentication.with_test_certificate(nip="5261040828")
# token authentication works when you already have a KSeF token.
token = client.authentication.with_token(
ksef_token="your-ksef-token",
nip="5261040828",
)
# DEMO and PRODUCTION can authenticate with an MCU-issued XAdES certificate.
cert = load_certificate_from_pem("company.pem")
key = load_private_key_from_pem("company.key")
xades = Client(Environment.DEMO).authentication.with_xades(
nip="5261040828",
cert=cert,
private_key=key,
)
# you can also use CLI profiles to avoid handling certificates and tokens directly in your code
profile = client.authentication.with_profile("test-company")
ksef2-cli profiles
The separate ksef2-cli package
provides local profiles for repeated CLI work. Profiles store non-secret
defaults such as environment, NIP, authentication method, certificate paths, and
the environment variable that contains a secret.
CLI profile setup:
ksef2 profile create prod-token \
--env production \
--nip 5261040828 \
--token-env KSEF2_TOKEN
# profile create activates the new profile by default, use this to switch between contexts
ksef2 profile use prod-token
# example usage of the cli
ksef2 --profile prod-token invoices metadata \
--role seller \
--date-from 2026-01-01T00:00:00Z
These commands add a profile to the local ksef2-cli configuration at
~/.config/ksef2-cli/config.toml:
# ksef2-cli local profiles
# CLI options override the selected profile for one invocation.
# Store token and password secrets in environment variables.
active_profile = "prod-token"
[profiles.prod-token]
environment = "production"
nip = "5261040828"
[profiles.prod-token.auth]
type = "token"
token_env = "KSEF2_TOKEN"
Use defined profiles in the SDK:
from ksef2 import Client, Environment
from ksef2.profiles import Profile, ProfileStore, TokenProfileAuth
store = ProfileStore.default()
store.save(
"prod-token",
Profile(
environment=Environment.PRODUCTION,
nip="5261040828",
auth=TokenProfileAuth(token_env="KSEF2_TOKEN"),
),
activate=True,
overwrite=True,
)
# match the profile and client environments.
client = Client(Environment.PRODUCTION)
# defaults to the currently active profile in the CLI configuration.
active = client.authentication.with_profile()
# or specify which profile to use explicitly.
seller = client.authentication.with_profile("prod-token")
Send and download an invoice
from pathlib import Path
from ksef2 import Client, Environment, FormSchema
client = Client(Environment.TEST)
auth = client.authentication.with_test_certificate(nip="5261040828")
with auth.online_session(form_code=FormSchema.FA3) as session:
status = session.send_invoice_and_wait(
invoice_xml=Path("invoice.xml").read_bytes(),
timeout=60.0,
)
invoice_xml = auth.invoices.wait_for_invoice_download(
ksef_number=status.ksef_number,
timeout=120.0,
)
Path("downloads").mkdir(exist_ok=True)
Path("downloads/invoice.xml").write_bytes(invoice_xml)
print(status.ksef_number)
Use auth.invoices for metadata queries, exports, package downloads, and direct
invoice downloads after KSeF assigns invoice numbers.
Build valid FA(3) invoices
Use ksef2.fa3 when you want to generate FA(3) XML from typed Python builders
instead of composing XML by hand.
from datetime import date
from decimal import Decimal
from ksef2 import FormSchema
from ksef2.fa3 import FA3InvoiceBuilder, VatRate
builder = (
FA3InvoiceBuilder()
.header(system_info="my app")
.seller(
name="ACME S.A.",
tax_id="1234567890",
country_code="PL",
address_line_1="ul. Przykladowa 123",
)
.buyer(
name="XYZ GmbH",
country_code="DE",
address_line_1="Unter den Linden 1",
)
.standard()
.issue_place("Warszawa")
.issue_date(date(2026, 3, 29))
.invoice_number("FV/2026/03/0001")
.rows()
.add_line(
name="Consulting service",
supply_date=date(2026, 3, 29),
unit_of_measure="h",
quantity=Decimal("10"),
unit_price_net=Decimal("100.00"),
vat_rate=VatRate.VAT_23,
)
.done()
.done()
)
invoice_xml = builder.to_xml()
with auth.online_session(form_code=FormSchema.FA3) as session:
status = session.send_invoice_and_wait(
invoice_xml=invoice_xml.encode("utf-8"),
)
print(status.ksef_number)
The builder can also return a domain model with build() or the generated
FA(3) schema model with to_spec().
Documentation
- Online docs: https://docs.stacking.me/ksef2/sdk/intro/
- Quickstart: https://docs.stacking.me/ksef2/sdk/getting-started/quickstart/
- Workflow guides: https://docs.stacking.me/ksef2/sdk/workflows/overview/
- API reference: https://docs.stacking.me/ksef2/sdk/reference/api-signatures/
- Source docs:
docs/enanddocs/pl - Runnable examples:
scripts/examples
Development
just sync
just test
just release-check
Additional development tasks live in the justfile, including integration
tests, API coverage checks, OpenAPI model regeneration, and release tooling.
Contributing
Issues and pull requests are welcome. Before opening a PR, run the focused test or docs build that covers your change, and update both source docs and examples when behavior changes.
For SDK docs, edit the source catalog under docs/en and docs/pl. The public
documentation site syncs from those files.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ksef2-0.18.1.tar.gz.
File metadata
- Download URL: ksef2-0.18.1.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78db66a88af06f667d0a2cad662d9c742c0343e89a0f3b784e860d161320ea56
|
|
| MD5 |
8eedd2f4f1ddd6a7b4327eb1e682e27b
|
|
| BLAKE2b-256 |
62632a289337d012fc06762f32d16dd8fcb9af817a2d4383acb3a13512b9375a
|
Provenance
The following attestation bundles were made for ksef2-0.18.1.tar.gz:
Publisher:
publish.yml on stacking-hq/ksef2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ksef2-0.18.1.tar.gz -
Subject digest:
78db66a88af06f667d0a2cad662d9c742c0343e89a0f3b784e860d161320ea56 - Sigstore transparency entry: 1910498622
- Sigstore integration time:
-
Permalink:
stacking-hq/ksef2@0782f6cd293493f453832c7a3a6d28bcc28a9627 -
Branch / Tag:
refs/tags/v0.18.1 - Owner: https://github.com/stacking-hq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0782f6cd293493f453832c7a3a6d28bcc28a9627 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ksef2-0.18.1-py3-none-any.whl.
File metadata
- Download URL: ksef2-0.18.1-py3-none-any.whl
- Upload date:
- Size: 449.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85fdba378fbc7692b26a7b8b16751418457bca362d7dbb5a8f9fa6a47de64f8e
|
|
| MD5 |
7133f4762e5e4114ddc9d9c06b55ed7d
|
|
| BLAKE2b-256 |
39bbb32621d5c6ad67825a9828e58a1021e0aeb441099c58f63108e7ab7741c9
|
Provenance
The following attestation bundles were made for ksef2-0.18.1-py3-none-any.whl:
Publisher:
publish.yml on stacking-hq/ksef2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ksef2-0.18.1-py3-none-any.whl -
Subject digest:
85fdba378fbc7692b26a7b8b16751418457bca362d7dbb5a8f9fa6a47de64f8e - Sigstore transparency entry: 1910498804
- Sigstore integration time:
-
Permalink:
stacking-hq/ksef2@0782f6cd293493f453832c7a3a6d28bcc28a9627 -
Branch / Tag:
refs/tags/v0.18.1 - Owner: https://github.com/stacking-hq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0782f6cd293493f453832c7a3a6d28bcc28a9627 -
Trigger Event:
push
-
Statement type: