Skip to main content

Python SDK and Tools for Poland's KSeF (Krajowy System e-Faktur) API

Project description

KSeF Toolkit

Python SDK for Poland's KSeF (Krajowy System e-Faktur) v2 API.

API Coverage Unit Test Coverage Python Version Integration Tests
beartype pre-commit Ruff License: MIT

Installation

pip install ksef2

Or with uv:

uv add ksef2

Requires Python 3.12+.

Supported OpenAPI Version

The SDK currently supports KSeF OpenAPI version 2.3.0.

Quick Start

from datetime import datetime, timedelta, timezone
from pathlib import Path

from ksef2 import Client, Environment, FormSchema
from ksef2.domain.models import InvoicesFilter

NIP = "5261040828"
client = Client(Environment.TEST)

# Authenticate (XAdES — TEST environment)
auth = client.authentication.with_test_certificate(nip=NIP)

with auth.online_session(form_code=FormSchema.FA3) as session:
    # Send an invoice
    result = session.send_invoice(invoice_xml=Path("invoice.xml").read_bytes())
    print(result.reference_number)

    # Wait until KSeF finishes processing it
    status = session.wait_for_invoice_ready(
        invoice_reference_number=result.reference_number
    )
    print(status.status.description)

# Export invoices (no session required)
export = auth.invoices.schedule_export(
    filters=InvoicesFilter(
        role="seller",
        date_type="issue_date",
        date_from=datetime.now(tz=timezone.utc) - timedelta(days=1),
        date_to=datetime.now(tz=timezone.utc),
        amount_type="brutto",
    ),
)

# Download the exported package
package = auth.invoices.wait_for_export_package(reference_number=export.reference_number)
for path in auth.invoices.fetch_package(
    package=package,
    export=export,
    target_directory="downloads",
):
    print(f"Downloaded: {path}")

Runnable TEST examples: scripts/examples/quickstart.py and scripts/examples/invoices/send_query_export_download.py

Async Quick Start

Use AsyncClient when your application already runs inside an event loop. The async API mirrors the sync client shape: authenticate through client.authentication, then use the returned authenticated client for sessions, invoices, tokens, permissions, limits, certificates, and batch work.

import asyncio
from datetime import datetime, timedelta, timezone
from pathlib import Path

from ksef2 import AsyncClient, Environment, FormSchema
from ksef2.domain.models import InvoicesFilter

NIP = "5261040828"


async def main() -> None:
    async with AsyncClient(Environment.TEST) as client:
        auth = await client.authentication.with_test_certificate(nip=NIP)

        async with await auth.online_session(form_code=FormSchema.FA3) as session:
            result = await session.send_invoice(
                invoice_xml=Path("invoice.xml").read_bytes()
            )
            status = await session.wait_for_invoice_ready(
                invoice_reference_number=result.reference_number
            )
            print(status.status.description)

        export = await auth.invoices.schedule_export(
            filters=InvoicesFilter(
                role="seller",
                date_type="issue_date",
                date_from=datetime.now(tz=timezone.utc) - timedelta(days=1),
                date_to=datetime.now(tz=timezone.utc),
                amount_type="brutto",
            ),
        )
        package = await auth.invoices.wait_for_export_package(
            reference_number=export.reference_number
        )
        zip_parts = await auth.invoices.fetch_package_bytes(
            package=package,
            export=export,
        )
        print(len(zip_parts))


asyncio.run(main())

See docs/guides/async-client.md for async usage patterns.

Features

  • Typed public API for authentication, sessions, invoices, tokens, permissions, limits, certificates, and PEPPOL
  • Sync and async clients with matching high-level entry points through Client and AsyncClient
  • FA(3) invoice builder exposed through ksef2.fa3 for typed invoice construction and XML rendering
  • XAdES and KSeF token authentication through a single Client.authentication entry point
  • Online and batch sessions with resumable session state for long-running jobs
  • Built-in encryption helpers for invoice sending and export package decryption
  • TEST environment tooling including self-signed certificates and disposable test data contexts
  • Runnable examples and guide docs for the common KSeF workflows

Root Client

Client and AsyncClient expose both authenticated and public entry points:

  • client.authentication for XAdES and KSeF-token authentication
  • client.encryption for public KSeF encryption certificates
  • client.peppol for public PEPPOL provider queries
  • client.testdata for TEST-only data setup and cleanup helpers

Async methods are awaited, and async session/testdata helpers are used with async with. For example, client.authentication.with_token(...) becomes await client.authentication.with_token(...), and auth.online_session(...) becomes async with await auth.online_session(...).

Logging

The SDK exposes structlog loggers via ksef2.logging, but it does not configure global logging on import. Applications can either configure structlog themselves or use the provided helper:

from ksef2.logging import configure_logging, get_logger

configure_logging(level="INFO")

logger = get_logger("my_app")
logger.info("Starting KSeF sync", environment="test")

SDK internals use the same logger factory, so once the application configures structlog, events emitted by ksef2 follow the same handlers and rendering.

XAdES on DEMO / PRODUCTION (MCU certificate)

The TEST environment accepts self-signed certificates generated by the SDK. DEMO and PRODUCTION require a certificate issued by MCU — use the provided helpers to load it:

from ksef2 import Client, Environment
from ksef2.core.xades import (
    load_certificate_and_key_from_p12,
    load_certificate_from_pem,
    load_private_key_from_pem,
)

cert = load_certificate_from_pem("cert.pem")  # downloaded from MCU
key = load_private_key_from_pem("key.pem")

auth = Client(Environment.DEMO).authentication.with_xades(
    nip="5261040828",
    cert=cert,
    private_key=key,
)

cert, key = load_certificate_and_key_from_p12("cert.p12", password=b"secret")

Token Authentication

Use this when you already have a KSeF token issued for the target context:

from ksef2 import Client

client = Client()  # uses production environment by default

auth = client.authentication.with_token(
    ksef_token="your-ksef-token",
    nip="5261040828",
)
print(auth.access_token)

Authenticated Client

After with_xades() or with_token(), you get an AuthenticatedClient. Its main entry points are:

  • auth.online_session() and auth.batch_session() for invoice sessions
  • auth.batch for end-to-end batch package preparation, upload, and status polling
  • auth.invoices for metadata queries, exports, downloads, and package fetches
  • auth.tokens for KSeF authorization token lifecycle management
  • auth.permissions for grant, revoke, and query operations
  • auth.certificates for certificate enrollment, retrieval, query, and revocation
  • auth.sessions for active authentication session management
  • auth.invoice_sessions for historical online and batch invoice sessions
  • auth.limits for TEST-environment limit inspection and overrides

Invoice sending stays on auth.online_session() because it depends on an opened KSeF session and its session-specific encryption keys. Batch ZIP preparation and upload live on auth.batch, while metadata queries, exports, and downloads live on auth.invoices because they only require the authenticated bearer context.

Common examples:

from datetime import datetime, timedelta, timezone

from ksef2.domain.models import InvoicesFilter

token = auth.tokens.generate(
    permissions=["invoice_read", "invoice_write"],
    description="API integration token",
)
print(token.reference_number, token.token)

limits = auth.limits.get_context_limits()
print(limits.online_session.max_invoices)

sessions = auth.sessions.query(page_size=10)
print(len(sessions.items))

metadata = auth.invoices.query_metadata(
    filters=InvoicesFilter(
        role="seller",
        date_type="issue_date",
        date_from=datetime.now(tz=timezone.utc) - timedelta(days=7),
        date_to=datetime.now(tz=timezone.utc),
        amount_type="brutto",
    )
)
print(len(metadata.invoices))

For the full API surface, see the guide docs below.

FA(3) Builder

If you want to generate FA(3) invoices inside the SDK, use the public ksef2.fa3 namespace:

from datetime import date
from decimal import Decimal

from ksef2.fa3 import FA3InvoiceBuilder, VatRate

xml_text = (
    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_date(date(2026, 3, 29))
        .invoice_number("FV/2026/03/0001")
        .rows()
            .add_line(
                name="Consulting service",
                quantity=Decimal("1"),
                unit_of_measure="h",
                unit_price_net=Decimal("100.00"),
                vat_rate=VatRate.VAT_23,
            )
        .done()
    .done()
    .to_xml()
)

See docs/guides/fa3-builder.md for the full builder guide and the runnable examples in scripts/examples/invoices.

To build an invoice and generate a PDF visualization locally:

uv run -m scripts.examples.invoices.build_fa3_invoice

This writes both output/fa3_invoice.xml and output/fa3_invoice.pdf.

Examples

Run examples as modules with uv run -m ...; direct execution by file path is not supported.

Development

just sync          # Install all dependencies (including dev)
just test          # Run unit tests
just test-coverage # Run unit tests with coverage and update test-coverage.json
just release-check # Run the pre-release verification suite and build artifacts
just regenerate-models  # Regenerate OpenAPI models

Other commands

just integration   # Run integration tests (requires KSEF credentials in .env)
just coverage       # Calculate API coverage (updates coverage.json)
just fetch-spec     # Fetch latest OpenAPI spec from KSeF

API Coverage

The SDK covers 73 of 73 KSeF API endpoints (100%). See feature docs for details:

  • Authentication — XAdES, token auth, session management
  • Async Client — async authentication, sessions, exports, batch, and testdata
  • Encryption — public KSeF encryption certificates
  • Invoices — send, download, query, export
  • Sessions — online/batch sessions, resume support
  • Tokens — generate and manage KSeF authorization tokens
  • Permissions — grant/query permissions for persons and entities
  • Certificates — enroll, query, revoke KSeF certificates
  • Limits — query and modify API rate limits
  • PEPPOL — query registered PEPPOL providers
  • Test Data — create test subjects, manage test environment

Stability And Releases

The SDK is still in the pre-1.0.0 stabilization phase.

  • Track release notes in CHANGELOG.md
  • Run just release-check before publishing a release
  • Expect 0.x minor releases to contain public API cleanup when needed

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ksef2-0.13.1.tar.gz (8.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ksef2-0.13.1-py3-none-any.whl (408.0 kB view details)

Uploaded Python 3

File details

Details for the file ksef2-0.13.1.tar.gz.

File metadata

  • Download URL: ksef2-0.13.1.tar.gz
  • Upload date:
  • Size: 8.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ksef2-0.13.1.tar.gz
Algorithm Hash digest
SHA256 6b438f8faa6f6dcc2cb0c96458670c0f984eda5431fb15dc2ebb22bd49550228
MD5 4234633ed9f825afcf10e8338f9a746d
BLAKE2b-256 85d2ea8ec4e11ac1b86190b6f092593bf424f991927e950b8ac2546bbf712c0a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ksef2-0.13.1.tar.gz:

Publisher: publish.yml on artpods56/ksef2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ksef2-0.13.1-py3-none-any.whl.

File metadata

  • Download URL: ksef2-0.13.1-py3-none-any.whl
  • Upload date:
  • Size: 408.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ksef2-0.13.1-py3-none-any.whl
Algorithm Hash digest
SHA256 34826551f2585f0a84b4ed18d809ad4135430dc9b0454efd456103ecfe9942fd
MD5 a758fb3fe7622fcb1d927fafa0cfd6ae
BLAKE2b-256 d5a4927cd78f831ac37dd528a6a973feacadea04bef2d9ff2398dcb46344f230

See more details on using hashes here.

Provenance

The following attestation bundles were made for ksef2-0.13.1-py3-none-any.whl:

Publisher: publish.yml on artpods56/ksef2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page