Private beta — request an API key at [email protected]

Billhorse API

Validate and parse e-invoices — XRechnung, ZUGFeRD/Factur-X and EN 16931 UBL/CII — through two endpoints. Files are processed in memory and never stored. Also available as a self-hosted container, so invoice data never leaves your infrastructure.

Quickstart — first validation in five minutes

1

Set your API key

You received it by e-mail during the private beta.

export BILLHORSE_API_KEY="bh_live_…"
2

Validate a sample invoice

Use our example file (valid XRechnung 3.0) or your own XML/PDF:

# Download the sample, then validate it
curl -sO https://billhorse.com/docs/examples/xrechnung-valid.xml
curl -s -X POST https://api.billhorse.com/v1/validate \
  -H "Authorization: Bearer $BILLHORSE_API_KEY" \
  --data-binary @xrechnung-valid.xml

Response (abridged):

{
  "valid": true,
  "profile": { "label": "XRechnung 3.0", "en16931": true, "xrechnung": true },
  "meta": { "number": "RE-2026-0815", "grandTotal": "178.50", … },
  "findings": [],
  "counts": { "error": 0, "warning": 0, "info": 0 }
}
3

See what errors look like

The broken sample triggers eight findings. Every finding carries a rule id — each one has a human explanation page:

{
  "valid": false,
  "findings": [
    { "id": "BR-DE-15", "severity": "error",
      "message": "XRechnung: buyer reference (BT-10, …) is mandatory." },
    { "id": "BR-CO-16", "severity": "error",
      "expected": "178.50", "actual": "170.00", … }
  ]
}

billhorse.com/en/rules/br-de-15 explains the rule and the fix, in EN, DE and FR. Messages come in English by default — add ?lang=de or ?lang=fr to show them to your end users in their language.

4

Parse into normalized JSON

/v1/parse extracts the EN 16931 semantic model — the same JSON shape whether the input was UBL, CII or a hybrid PDF:

curl -s -X POST https://api.billhorse.com/v1/parse \
  -H "Authorization: Bearer $BILLHORSE_API_KEY" \
  --data-binary @rechnung.pdf

{
  "invoice": {
    "number": "RE-2026-0815", "docType": "invoice", "currency": "EUR",
    "seller": { "name": "…", "vatId": "DE123456789", "country": "DE" },
    "totals": { "netTotal": "150", "taxTotal": "28.5", "due": "178.5" },
    "lines": [ … ], "vat": [ … ]
  },
  "report": { "valid": true, … }
}

Good to know

TopicDetails
AuthenticationBearer API key. Errors come as RFC 9457 application/problem+json.
BodyRaw bytes (XML or PDF), max 10 MB. No multipart, no base64.
Rate limit120 requests/minute per key. HTTP 429 with Retry-After.
LanguagesFinding messages: English by default, ?lang=de / ?lang=fr on /validate and /parse. Rule ids and expected/actual/found are language-neutral.
Data privacyRequests are processed in memory only — invoices are never stored or logged.
Self-hostedThe identical container runs in your infrastructure (BILLHORSE_NO_AUTH=1 behind your gateway). Ask us.
FormatsXRechnung 1.2–3.0 (UBL & CII), ZUGFeRD 2.x/Factur-X (all profiles, PDF/A-3 & XML), EN 16931 UBL/CII. ZUGFeRD 1.0 detected with migration notice.
Country rulesGermany (mandatory fields incl. Leitweg-ID) and France (SIREN/SIRET & VAT, 2026 reform).

API Reference

Every endpoint, schema and error — rendered from the OpenAPI spec. Open reference →

OpenAPI spec

Machine-readable contract, ready for codegen and testing. openapi.yaml →

Validation rules

All 42 rule ids explained, with fixes. Rule index →

Try without code

The browser validator uses the same engine. Open validator →

© 2026 Billhorse · Legal & privacy · Questions: [email protected]