DEEN
fakturai blog

Python & Node SDK

Generate German e-invoices with Python and Node.js (API integration)

Short answer: send a compact JSON payload via HTTP POST to the fakturai REST API and receive an EN 16931-compliant ZUGFeRD/Factur-X PDF back — no XML knowledge, no local generator libraries, no manual namespace declarations. The API checks the generated file against a KoSIT-aligned accept gate before returning it.

Why API instead of a local library?

Local XML libraries for ZUGFeRD or XRechnung require deep format knowledge: EN 16931 BT/BG codes, namespace declarations, Schematron rules, profile specifics. The API abstracts all of that:

Python example (httpx / requests)

This synthetic B2B example requires an approved web-app instance, a saved seller profile and its API key. Seller details come from that profile; the server allocates the invoice number. This endpoint has noformat switch for standalone XRechnung XML. Successful generation counts toward usage; do not automatically retry generation.

Set FAKTURAI_BASE_URL to the web app (locally, e.g. http://127.0.0.1:3004; for approved hosted use, https://fakturai.de) and securely set FAKTURAI_API_KEYin your environment, not the source code. Install httpx with python -m pip install httpx, save the block as generate.py, then run python generate.py. Use synthetic data only; the request is sent to the configured instance.

import os, pathlib
import httpx

base = os.environ["FAKTURAI_BASE_URL"].rstrip("/")
key = os.environ["FAKTURAI_API_KEY"]
payload = {
  "invoice_date": "2026-06-16",
  "due_date": "2026-06-30",
  "currency": "EUR",
  "buyer": {
    "name": "Synthetic Client GmbH",
    "address": {
      "line_one": "Beispielweg 2", "postcode": "80331",
      "city": "München", "country": "DE"
    },
    "contact_email": "billing@example.invalid"
  },
  "lines": [{
    "name": "Consulting", "quantity": "2", "unit": "HUR",
    "unit_price": "50.00", "vat_rate": "19.00"
  }]
}
resp = httpx.post(
    base + "/api/v1/invoices",
    json=payload,
    headers={"Authorization": "Bearer " + key},
    timeout=120.0,
)
resp.raise_for_status()
if resp.status_code != 201 or resp.headers.get("content-type", "").split(";")[0] != "application/pdf":
    raise RuntimeError("Expected HTTP 201 application/pdf")
pathlib.Path("invoice.pdf").write_bytes(resp.content)

For requests, replace httpx.post with requests.post — the payload is identical.

Node.js / TypeScript example (fetch)

Use Node.js 22 or newer, save as generate.mjs and run node generate.mjs with the same environment variables. Both examples save invoice.pdf only on201 application/pdf. Error responses are JSON, not a PDF or a passed test.

import fs from "node:fs/promises";

const base = process.env.FAKTURAI_BASE_URL;
const key = process.env.FAKTURAI_API_KEY;
if (!base || !key) throw new Error("Set FAKTURAI_BASE_URL and FAKTURAI_API_KEY");
const payload = {
  invoice_date: "2026-06-16",
  due_date: "2026-06-30",
  currency: "EUR",
  buyer: {
    name: "Synthetic Client GmbH",
    address: {
      line_one: "Beispielweg 2", postcode: "80331",
      city: "München", country: "DE"
    },
    contact_email: "billing@example.invalid"
  },
  lines: [{
    name: "Consulting", quantity: "2", unit: "HUR",
    unit_price: "50.00", vat_rate: "19.00"
  }]
};
const res = await fetch(new URL("/api/v1/invoices", base), {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: "Bearer " + key },
  body: JSON.stringify(payload),
  signal: AbortSignal.timeout(120_000),
});
if (!res.ok) throw new Error("API error " + res.status);
if (res.status !== 201 || res.headers.get("content-type")?.split(";")[0] !== "application/pdf") {
  throw new Error("Expected HTTP 201 application/pdf");
}
await fs.writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));

The payload is the same JSON structure regardless of language. In TypeScript, add a type definition for the payload; the result is a Buffer / ArrayBuffer.

Key mandatory fields

MCP integration for AI agents

Alongside the REST API, a MCP server exposes the same generate-and-check logic as a tool call. AI agents and MCP clients can generate e-invoices without crafting HTTP manually. See create e-invoices per API & MCP.

What fakturai does not replace

fakturai generates and checks the technical format and structural compliance per EN 16931 with a KoSIT-aligned accept gate. The content and tax correctness of the JSON payload, GoBD-compliant archiving and accounting treatment remain your responsibility. fakturai is not tax advice and makes no legal, tax or delivery guarantees.

Next step

Free German e-invoice validator · Create an API key · API docs

Related: API & MCP creation guide · E-invoice example (XML) · XRechnung validation errors

Free validator · Create API key · API docs