Skip to main content

Official beliq SDK: generate, validate, parse, and convert EU-compliant e-invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, drift-checked rules.

Project description

beliq

Official Python SDK for the beliq e-invoicing compliance API. Generate, validate, parse, and convert EN 16931 invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, nightly-drift-checked rules.

beliq produces and checks the compliant document. Transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.

Install

pip install beliq

Requires Python >= 3.10.

Quick start

from beliq import Beliq

beliq = Beliq(api_key="blq_...")

# Account, plan, and quota context (no quota cost).
account = beliq.me()

# Generate an XRechnung document from an EN 16931 invoice.
generated = beliq.generate(
    standard="xrechnung",
    verify=True,
    invoice={
        "number": "INV-2026-001",
        "issueDate": "2026-01-15",
        "currencyCode": "EUR",
        "seller": {"name": "Seller GmbH", "address": {"city": "Berlin", "postalCode": "10115", "countryCode": "DE"}},
        "buyer": {"name": "Buyer GmbH", "address": {"city": "Munich", "postalCode": "80331", "countryCode": "DE"}},
        "lines": [
            {"description": "Consulting", "quantity": 10, "unitCode": "HUR", "unitPrice": 100, "lineTotal": 1000, "vatRate": 19, "vatCategoryCode": "S"}
        ],
        "totalNetAmount": 1000,
        "totalTaxAmount": 190,
        "totalGrossAmount": 1190,
    },
)
print(generated.xml, generated.meta.schematron_version)

# Validate any document against authority-pinned rules.
result = beliq.validate(generated.xml, format="auto")
if not result.valid:
    for issue in result.errors:
        print(issue.rule_id, issue.message)

Authentication

Create an API key in the beliq dashboard under API Keys:

Beliq(api_key="blq_...")                    # sends X-API-Key (default)
Beliq(api_key="blq_...", auth="bearer")      # sends Authorization: Bearer
Beliq(api_key="blq_...", base_url="https://staging.beliq.eu")

Async

AsyncBeliq mirrors the sync client with await:

import asyncio
from beliq import AsyncBeliq

async def main():
    async with AsyncBeliq(api_key="blq_...") as beliq:
        result = await beliq.validate(open("invoice.xml", "rb").read(), format="auto")
        print(result.valid)

asyncio.run(main())

API

Method Endpoint Input Returns
me() GET /v1/me none AccountInfo (no quota cost)
generate(...) POST /v1/generate EN 16931 invoice dict GenerateResult
validate(document, ...) POST /v1/validate XML or PDF ValidationResult
parse(document, ...) POST /v1/parse XML or PDF ParseResult
convert(document, ...) POST /v1/convert XML or PDF ConvertResult

document accepts a str, bytes, or bytearray. The content type is sniffed from the bytes (PDF vs XML) unless you pass content_type=. generate and convert return the raw document content (bytes) plus the response-header metadata: meta.schematron_version, meta.pdf_kind, meta.source_format/meta.target_format, meta.lost_elements, meta.conversion_tools, the ruleset fingerprint meta.ruleset_sha256 / meta.ruleset_artifacts, and meta.livemode. For an XML output, generate also decodes xml.

JSON responses are Pydantic models. Any field not explicitly typed (such as the per-country authority versions on a validation result) is preserved and accessible. Errors raise BeliqApiError with a typed .code, HTTP .status, and any .details:

from beliq import BeliqApiError

try:
    beliq.validate("not xml")
except BeliqApiError as err:
    print(err.code, err.status, err.message)

The seal: verify it yourself

Pass seal=True to generate to get the document back as a JSON envelope: the decoded content (bytes) plus its sha256 and the full validation_result. Hashing the returned bytes reproduces the returned hash, so you can prove which ruleset the document passed.

import hashlib

sealed = beliq.generate(standard="xrechnung", verify=True, invoice=invoice, seal=True)
assert hashlib.sha256(sealed.content).hexdigest() == sealed.sha256
print(sealed.validation_result.valid, sealed.meta.ruleset_sha256)

Without seal, generate returns the raw document body (unchanged, the default). The ruleset fingerprint (meta.ruleset_sha256, meta.ruleset_artifacts) is present in both modes.

Sandbox and live keys

A blq_test_ key is a sandbox key; a blq_live_ key is live. The client derives the mode from the key prefix before any request:

beliq = Beliq(api_key="blq_test_...")
beliq.livemode  # False for a sandbox key, True for a live key

Each generate / convert response also carries the authoritative mode from the server as meta.livemode.

Generate presets

LIVE_GENERATE_PRESETS is the curated set of public generate targets (matching beliq.eu's own generator). NLCIUS is a Peppol BIS profile rather than a standalone standard, so it is reachable here:

from beliq import LIVE_GENERATE_PRESETS

nlcius = next(p for p in LIVE_GENERATE_PRESETS if p.id == "nlcius")
beliq.generate(standard=nlcius.standard, profile=nlcius.profile, output=nlcius.output, invoice=invoice)

Development

python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
ruff check src tests
mypy
pytest                                   # unit tests (no network)
BELIQ_API_KEY=blq_xxx pytest tests/test_integration.py   # hits the live API; draws quota

tests/test_spec_contract.py reads the vendored openapi.json and fails if the error-code set, the core validate/seal fields, or the public option lists drift from the spec. Refresh the vendored spec with python scripts/sync_spec.py. A weekly workflow (scripts/check_live_drift.py) flags when the vendored spec falls behind the deployed API.

Publishing

Released to PyPI as beliq. Releases run from .github/workflows/release.yml via PyPI Trusted Publishing (OIDC, with attestations): push a v*.*.* tag to publish. No token is stored in the repo.

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

beliq-0.2.0.tar.gz (32.7 kB view details)

Uploaded Source

Built Distribution

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

beliq-0.2.0-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file beliq-0.2.0.tar.gz.

File metadata

  • Download URL: beliq-0.2.0.tar.gz
  • Upload date:
  • Size: 32.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for beliq-0.2.0.tar.gz
Algorithm Hash digest
SHA256 0c71ce0f23c412a9b5e07154f9653898edbb1e37dde74654ecf5c2621b4da658
MD5 8a1192ea816f4b6af116fd29a8cdc920
BLAKE2b-256 3fc47c42c0d7815b0cf6de369f5482b4da776b2f72f2f38bf03783c9363474c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for beliq-0.2.0.tar.gz:

Publisher: release.yml on beliq-eu/beliq-sdk-python

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

File details

Details for the file beliq-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: beliq-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for beliq-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0cdef701cab1d2a5260c2aa54e0fa0ce51a3fa33ffc957b12772ed34acce7510
MD5 ecd1d8ee23fbde6e9a4b2232331b528a
BLAKE2b-256 1ff8178d723171667b9d66b75a5c14c38907f642452e79b7de97d26fdea3284e

See more details on using hashes here.

Provenance

The following attestation bundles were made for beliq-0.2.0-py3-none-any.whl:

Publisher: release.yml on beliq-eu/beliq-sdk-python

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