Developer quickstart

One base URL, three endpoints, no SDK required. Works without a key (50 documents/month per IP); add X-Api-Key for the paid tiers. Every generated document is validated by the official KoSIT validator before it leaves the server.

EndpointPurposeInputOutput
POST /v1/invoicesgenerateJSON (see model below)XML (XRechnung) or PDF (ZUGFeRD); or JSON with base64 + report
POST /v1/validatevalidateraw XML body, or multipart file (XML/PDF)JSON report: valid, errors[], warnings[]
POST /v1/parsereadraw XML body, or multipart file (XML/PDF)JSON: parties, lines, totals, payment
GET /v1/usagequotaplan, used, limit

1. Validate

# XML
curl -X POST https://xrechnung.dev/v1/validate --data-binary @invoice.xml
# ZUGFeRD / Factur-X PDF (the embedded XML is extracted and checked)
curl -X POST https://xrechnung.dev/v1/validate -F file=@invoice.pdf
{"valid": false, "scenario": "EN16931 XRechnung (CII)", "profile": "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0",
 "errors": [{"code": "BR-DE-15", "message": "Das Element \"Buyer reference\" (BT-10) muss übermittelt werden.", "location": "/rsm:CrossIndustryInvoice[1]/…"}],
 "warnings": [], "counts": {"errors": 1, "warnings": 0, "informations": 0}, "usage": {"plan": "free", "used": 1, "limit": 50}}

2. Generate

curl -X POST https://xrechnung.dev/v1/invoices -H "Content-Type: application/json" -H "X-Api-Key: $KEY" -o RE-2026-0001.pdf -d '{
  "format": "zugferd", "language": "de",
  "invoice": {
    "number": "RE-2026-0001", "issue_date": "2026-09-07", "delivery_date": "2026-09-05",
    "seller": {"name": "Beispiel GmbH", "vat_id": "DE123456789", "email": "rechnung@beispiel.de", "phone": "+49 30 1234567",
               "address": {"street": "Musterstr. 1", "postcode": "10115", "city": "Berlin", "country": "DE"}},
    "buyer":  {"name": "Kunde AG", "vat_id": "DE987654321", "email": "ap@kunde.de",
               "address": {"street": "Kundenweg 9", "postcode": "80331", "city": "München", "country": "DE"}},
    "buyer_reference": "04011000-12345-67",
    "lines": [{"description": "Beratung September", "quantity": "10", "unit": "HUR", "unit_price": "120.00", "vat_rate": "19"},
              {"description": "Fahrtkosten", "quantity": "1", "unit": "C62", "unit_price": "85.50", "vat_rate": "19"}],
    "payment": {"iban": "DE02120300000000202051", "bic": "BYLADEM1001", "due_date": "2026-09-21", "terms": "Zahlbar innerhalb von 14 Tagen ohne Abzug."},
    "notes": ["Vielen Dank für Ihren Auftrag."]
  }}'

Invoice model

FieldNotes
formatxrechnung (CII XML, XRechnung 3.0 profile) or zugferd (PDF/A-3 with EN 16931 XML)
outputbinary (default: the file) or json (base64 + full validation report)
invoice.type_code380 invoice (default), 381 credit note, 384 corrected invoice; set preceding_invoice_number for 381/384
invoice.sellervat_id or tax_number required; email and phone required (XRechnung BR-DE-6/7); contact_name defaults to the company name
invoice.buyer_referenceLeitweg-ID for public-sector buyers; defaults to the buyer name (XRechnung requires BT-10)
lines[].unitUN/ECE Rec. 20: HUR hour, C62 piece, DAY, MON, KGM, MTR, E48 service unit
lines[].vat_categoryS standard (default), Z zero, E exempt, AE reverse charge, K intra-EU, G export, O out of scope — exemption codes/texts are added automatically
invoice.small_businesstrue for § 19 UStG: no VAT, mandatory note added
invoice.paymentiban → SEPA credit transfer (code 58); due_date, terms, reference (defaults to the invoice number)

Money fields are strings or numbers with up to 2 decimals; totals are computed server-side per line with round-half-up, so they always satisfy the EN 16931 arithmetic rules.

3. Parse

curl -X POST https://xrechnung.dev/v1/parse -F file=@incoming.pdf
{"syntax": "CII", "number": "RE-2026-0001", "issue_date": "2026-09-07", "currency": "EUR",
 "seller": {"name": "Beispiel GmbH", "vat_id": "DE123456789", …}, "buyer": {…},
 "lines": [{"id": "1", "description": "Beratung September", "quantity": "10", "unit": "HUR", "unit_price": "120.00", "vat_rate": "19", "net_total": "1200.00"}],
 "taxes": [{"category": "S", "rate": "19", "basis": "1285.50", "tax": "244.25"}],
 "totals": {"net": "1285.50", "tax": "244.25", "gross": "1529.75", "due": "1529.75"},
 "payment": {"iban": "DE02120300000000202051", "due_date": "2026-09-21", "reference": "RE-2026-0001"}}

Python

import requests

API = "https://xrechnung.dev"
H = {"X-Api-Key": "xr_live_..."}          # omit for the free tier

report = requests.post(f"{API}/v1/validate", data=open("invoice.xml", "rb"), headers={**H, "Content-Type": "application/xml"}).json()
assert report["valid"], report["errors"]

pdf = requests.post(f"{API}/v1/invoices", json={"format": "zugferd", "invoice": invoice}, headers=H)
pdf.raise_for_status(); open("RE-2026-0001.pdf", "wb").write(pdf.content)

data = requests.post(f"{API}/v1/parse", files={"file": open("incoming.pdf", "rb")}, headers=H).json()
print(data["totals"]["gross"], data["payment"]["iban"])

PHP

$ch = curl_init("https://xrechnung.dev/v1/invoices");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => json_encode(["format" => "xrechnung", "invoice" => $invoice]),
  CURLOPT_HTTPHEADER => ["Content-Type: application/json", "X-Api-Key: $key"],
  CURLOPT_RETURNTRANSFER => true,
]);
$xml = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) { $err = json_decode($xml, true); /* $err["detail"] */ }
file_put_contents("RE-2026-0001.xml", $xml);

Node

const res = await fetch("https://xrechnung.dev/v1/invoices", {
  method: "POST", headers: {"Content-Type": "application/json", "X-Api-Key": process.env.XR_KEY},
  body: JSON.stringify({format: "zugferd", invoice})});
if (!res.ok) throw new Error(await res.text());
await fs.promises.writeFile("RE-2026-0001.pdf", Buffer.from(await res.arrayBuffer()));

Errors and limits

StatusMeaning
401invalid or inactive API key
422invoice data rejected (message names the field and the rule, e.g. "seller.phone is required (XRechnung BR-DE-6)") — or the document could not be read
413document larger than 5 MB
429monthly quota reached — upgrade at /#pricing

Documents are processed in memory and never stored. Quota resets on the first of each month (UTC). Postman: collection. Full schema: OpenAPI.

Get an API key — €19/month OpenAPI reference