factora-python
Official Python SDK for the Factora E-Invoicing API — create legally compliant German and European electronic invoices (XRechnung and ZUGFeRD, both based on EN 16931) from a single HTTP call.
Installation
pip install factora
Requires Python 3.9+.
Quickstart
An invoice is created with one POST to /api/v1/invoices/atomic/. The SDK wraps
that endpoint in FactoraClient.create_atomic_invoice().
import uuid
from factora import FactoraAPIError, FactoraClient
client = FactoraClient(api_key="fa_live_your_api_key")
payload = {
"api_mode": "atomic_single_post",
"invoice_header": {
"invoice_number": "RE-2026-0001",
"invoice_date": "2026-08-06",
"currency": "EUR",
"invoice_type": "380",
"buyer_reference": "04011000-12345-34", # Leitweg-ID, required by BR-DE
"profile": "xrechnung", # or "en16931"; omit to get "xrechnung"
},
"seller_snapshot": {
"name": "Muster GmbH",
"street": "Musterstrasse 1",
"zip": "10115",
"city": "Berlin",
"country": "DE",
"vat_id": "DE123456789",
"contact": {
"name": "Alex Muster",
"phone": "+49 30 1234567",
"email": "billing@muster-gmbh.example",
},
},
"buyer": {
"name": "Beispiel AG",
"street": "Beispielweg 7",
"zip": "80331",
"city": "Muenchen",
"country": "DE",
"vat_id": "DE987654321", # or buyer["contact"]["email"]
},
"items": [
{
"description": "Consulting services, August 2026",
"quantity": "2",
"unit": "H87", # UN/ECE Rec. 20 code, H87 = piece
"unit_price_net": "750.00",
"vat_rate": "19.00",
}
],
"validation_totals": {
"net_amount": "1500.00",
"vat_amount": "285.00",
"gross_amount": "1785.00",
},
}
try:
result = client.create_atomic_invoice(
payload,
idempotency_key=str(uuid.uuid4()),
)
except FactoraAPIError as exc:
print(f"HTTP {exc.status_code}: {exc}")
print(exc.response_data) # full response envelope, incl. errors[]
else:
print(result["valid"], result["data"])
FactoraClient is also a context manager, which closes the underlying HTTP
session for you:
with FactoraClient(api_key="fa_live_your_api_key") as client:
result = client.create_atomic_invoice(payload)
Idempotency
Pass idempotency_key with a value you generate (a UUID works well). Replaying
the same key returns the original result instead of creating a second invoice —
safe to use for retries after a timeout.
Request essentials
The API validates strictly; a rejected request comes back as HTTP 400 with a list of errors rather than a partially created invoice.
Top level
| Field | Required | Notes |
|---|---|---|
api_mode |
yes | Must be "atomic_single_post" |
invoice_header |
yes | See below |
validation_totals |
yes | net_amount, vat_amount, gross_amount |
seller_snapshot or mandant_id |
yes | Exactly one — sending both or neither returns 400 |
buyer |
yes | See below |
items |
yes | At least one line item |
invoice_header — invoice_number and invoice_date are required;
currency defaults to "EUR" and invoice_type to "380" (commercial
invoice). profile also lives here: "xrechnung" (default) or "en16931".
It is not a top-level field — sent at the top level it is ignored without
an error and the invoice silently falls back to "xrechnung". The resolved
profile is always returned in the response.
seller_snapshot — name is required.
buyer — name is required.
items[] — each line needs description, quantity, unit
(UN/ECE Rec. 20 code, e.g. "H87"), unit_price_net and vat_rate.
Additional rules under invoice_header.profile: "xrechnung"
The German CIUS (BR-DE rules) requires these on top of the base fields:
invoice_header.buyer_reference— the Leitweg-IDseller_snapshot.cityandseller_snapshot.zipseller_snapshot.contact.phoneandseller_snapshot.contact.emailbuyer.cityandbuyer.zip- an electronic address for the buyer — either
buyer.vat_idwith aDEprefix, orbuyer.contact.email
Response envelope
Every call returns the same envelope:
{
"valid": true,
"data": { "...": "created invoice" },
"errors": [],
"meta": { "...": "request metadata" }
}
Error entries always carry code, severity and message, and may
additionally include field, rule, bt (the EN 16931 business term) and
location.
Error handling
Any non-2xx response, and any transport failure, raises FactoraAPIError:
| Attribute | Description |
|---|---|
status_code |
HTTP status code, or None if the request never reached the server |
response_data |
Decoded response envelope, or None when the body was absent or not JSON |
try:
client.create_atomic_invoice(payload)
except FactoraAPIError as exc:
if exc.status_code == 400 and exc.response_data:
for error in exc.response_data.get("errors", []):
print(error["code"], error["severity"], error["message"])
else:
raise
Configuration
FactoraClient(
api_key="fa_live_your_api_key",
base_url="https://console.factora.software", # default
timeout=30, # seconds, default
)
API keys are issued in the Factora Console. Keys prefixed fa_test_ target the
sandbox, fa_live_ target production. Keep them out of source control — read
them from an environment variable or a secrets manager.
Documentation
Full API reference: https://console.factora.software/docs
License
MIT — see LICENSE.
Metadata
Release files for factora 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| factora-0.1.2.tar.gz | 8.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| factora-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.6 kB
Release files / factora-0.1.2.tar.gz
| Download URL | factora-0.1.2.tar.gz |
|---|---|
| Size | 8.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d326cdd58f58c73ab44b762ef17b4cef4bc9979ba8bb3cd61b0b7ab4f613e36f
|
|
BLAKE2b-256 checksum How to use checksums |
8cf65abb87c6d44201a7144b06096c9a797e8271eae0f6c5a4f715eac57b0d34
|
| 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 Aug 6, 2026.
Transparency logRelease files / factora-0.1.2-py3-none-any.whl
| Download URL | factora-0.1.2-py3-none-any.whl |
|---|---|
| Size | 7.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ae05d122b3cc48315a3a531f6883d66df625d2c6dcd3798a50255532e8ce291
|
|
BLAKE2b-256 checksum How to use checksums |
5e598d16e559a316beefb15c39848ad9dabe99fb28950a8c140d55ca18ff4e53
|
| 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 Aug 6, 2026.
Transparency log