Eine gültige E-Rechnung (XRechnung oder ZUGFeRD/Factur-X) erzeugst du aus Python am robustesten über eine E-Rechnung API statt durch eigenes XML-Bauen: Du sendest strukturierte Rechnungsdaten per requests, der Dienst rechnet die Summen, vergibt die Nummer und rendert das EN-16931-konforme Dokument. Der Flow sind drei Calls — anlegen, ausstellen, XML oder PDF abholen — die sich mit requests in wenigen Zeilen abbilden lassen.
XRechnung ist reines XML nach EN 16931, ZUGFeRD/Factur-X ein PDF mit
eingebettetem XML. In Python kannst du dieses XML selbst zusammenbauen — und Codelisten,
Geschäftsregeln und Profilstände jährlich nachpflegen — oder die Erzeugung an eine
E-Rechnung API delegieren. Dieser Beitrag zeigt den API-Weg mit
requests.
Der Flow: drei Calls
- Anlegen — du schickst Kunde und Positionen, der Server rechnet Summen und USt.
- Ausstellen — die Rechnungsnummer wird atomar vergeben, das XML gerendert und gegen die EN-16931-Geschäftsregeln validiert.
- Abholen — das reine XRechnung-XML (CII) oder das hybride ZUGFeRD/Factur-X-PDF.
Base-URL und Test-Key (sk_test_…) kommen aus der Umgebung, damit sich der Code später
ohne Änderung auf produktiv umstellen lässt — beides bekommst du mit dem Sandbox-Zugang.
Ein kleiner Client
import os
import requests
BASE_URL = os.environ["FORTLAUF_API_URL"]
API_KEY = os.environ["FORTLAUF_API_KEY"]
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {API_KEY}"})
def api(method: str, path: str, **kwargs) -> requests.Response:
res = session.request(method, f"{BASE_URL}{path}", timeout=30, **kwargs)
res.raise_for_status()
return res1) Rechnung anlegen
Beträge und Mengen gehen als DECIMAL-Strings über die Leitung — nie als float,
sonst holt dich die Fließkomma-Rundung ein. Intern rechnest du am besten mit
decimal.Decimal und serialisierst als String. Der Idempotency-Key macht einen
wiederholten Anlege-Call zum Replay statt zum Duplikat.
import uuid
draft = api(
"POST",
"/invoices",
headers={"Idempotency-Key": str(uuid.uuid4())},
json={
"currency": "EUR",
"documentTarget": "b2g",
"buyer": {
"name": "Gemeinde Görrenbach — Bauamt",
"addressLine1": "Rathausgasse 2",
"postalCode": "55483",
"city": "Görrenbach",
"countryCode": "DE",
},
"buyerElectronicAddress": "<LEITWEG-ID>",
"buyerReference": "<LEITWEG-ID>", # Leitweg-ID (BT-10), nur B2G
"invoiceTypeCode": "380",
"lineItems": [
{
"description": "Ingenieurleistung Straßenkataster, Los 2",
"quantity": "42",
"unitCode": "HUR",
"unitPriceNet": "95.00",
"taxRatePercent": "19",
}
],
},
)
invoice_id = draft.json()["id"] # Status "draft", Summen serverseitig berechnet2) Ausstellen
api("PATCH", f"/invoices/{invoice_id}/finalize")
# → Status "issued", Nummer vergeben, XRechnung-XML (CII) gerendert + validiert3) XML oder PDF abholen
# reines XRechnung-XML (CII)
xml = api("GET", f"/invoices/{invoice_id}/xml").content
with open(f"rechnung-{invoice_id}.xml", "wb") as fh:
fh.write(xml)
# oder das hybride ZUGFeRD/Factur-X-PDF (PDF/A-3 mit eingebettetem XML)
pdf = api("GET", f"/invoices/{invoice_id}/pdf").content
with open(f"rechnung-{invoice_id}.pdf", "wb") as fh:
fh.write(pdf)Wie das XML technisch ins PDF kommt, zeigt ZUGFeRD-PDF/A-3 erzeugen.
Worauf es in Python ankommt
- Beträge als Strings, intern
Decimal. Niefloatfür Geld —"1250.00", nicht1250.0. Der Server rechnet in ganzzahligen Minor-Units und rundet einmal; das vermeidet die Summen-Inkonsistenzen, die der Validator sonst anmahnt (KoSIT-Validierung erklärt). - Idempotenz im Task-Worker. In Celery-, RQ- oder Cron-Jobs kann ein Task doppelt
laufen; ein stabiler
Idempotency-Keypro Rechnung hält den Nummernkreis lückenlos. raise_for_status()nicht vergessen. Ein422aus der Validierung soll deinen Job hart stoppen, nicht still durchrutschen — der kleine Client oben erzwingt das.- Kein Format selbst pflegen. Codelisten, Geschäftsregeln und der Übergang zu XRechnung 4.0 liegen hinter der API; deine Python-Integration schickt „Kunde, Positionen, Steuersätze“ und bleibt stabil.
Der gezeigte Code ist illustrativ: Die vollständige Dokumentation steht dir in der API-Referenz zur Verfügung. Base-URL und Test-Key bekommst du mit dem Sandbox-Zugang im Entwickler-Bereich. Ob du die E-Rechnung selbst baust oder einbindest, ordnet E-Rechnung im eigenen SaaS: bauen oder einbinden? ein.
Selbst ausprobieren? Der Zugang ist kostenlos und ohne Karte — Produkt-Einstieg für Entwickler →
Oder den eigenen Abrechnungsfluss konkret durchsprechen: 30 Min, technisch, mit dem Gründer →