Free tier — sign up in the console to get your API key (no credit card).

Billhorse API

Validate, parse, generate and convert e-invoices — XRechnung, ZUGFeRD/Factur-X and EN 16931 UBL/CII. 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

Sign up at console.billhorse.com (free, no credit card) and create a key on the API-keys page.

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, … }
}
5

Generate & convert (preview)

Build EN 16931 XML from a JSON invoice model, or convert an existing invoice between syntaxes. Pick ?format=cii (UN/CEFACT, the syntax embedded in ZUGFeRD/Factur-X), ?format=ubl (OASIS UBL), ?format=peppol(Peppol BIS Billing 3.0, needs electronic addresses to be routable) or?format=xrechnung (XRechnung 3.0 UBL, the German KOSIT CIUS — needs a seller contact, payment means and a buyer reference/Leitweg-ID for BR-DE). Factur-X/PDF-A3 embedding is server-side; you can also try generation live in your browser on the API page.

# Generate XML from a JSON model (format=cii | ubl | peppol | xrechnung)
curl -s -X POST "https://api.billhorse.com/v1/generate?format=cii" \
  -H "Authorization: Bearer $BILLHORSE_API_KEY" \
  -H "Content-Type: application/json" \
  --data @invoice-model.json > invoice.xml

# Generate XRechnung 3.0 (KOSIT CIUS) or Peppol BIS 3.0
curl -s -X POST "https://api.billhorse.com/v1/generate?format=xrechnung" \
  -H "Authorization: Bearer $BILLHORSE_API_KEY" \
  -H "Content-Type: application/json" \
  --data @invoice-model.json > invoice-xrechnung.xml

# Convert an existing invoice (UBL / CII / hybrid PDF) to another syntax
curl -s -X POST "https://api.billhorse.com/v1/convert?format=ubl" \
  -H "Authorization: Bearer $BILLHORSE_API_KEY" \
  --data-binary @invoice.xml > invoice-ubl.xml

Good to know

TopicDetails
AuthenticationBearer API key. Errors come as RFC 9457 application/problem+json.
BodyRaw bytes (XML or PDF) for validate/parse/convert; a JSON invoice model for generate. Max 10 MB, no multipart, no base64.
Generate & convertPreview. /generate takes a JSON model, /convert an existing invoice; both return XML in the target syntax (?format=cii|ubl|peppol|xrechnung). Factur-X/PDF-A3 is embedded server-side.
Rate limitRequests per minute per key, plan-dependent (Free 120/min, Pro 600/min). HTTP 429 with Retry-After.
Quota (request units)The monthly quota is metered in request units: validate and parse count as 1, generate and convert as 2 (more compute, and they return a document). Rejected requests don't count.
LanguagesFinding messages: English by default, ?lang=de / ?lang=fr on /validate and /parse. Rule ids and expected/actual/found are language-neutral.
VersioningEvery report carries an engine version and a calendar-based ruleset version (e.g. 2026.07). /v1/health and the WASM bh_version() expose both. The ruleset is independent of the engine semver and bumps whenever validation rules are added, changed or tightened — log it alongside your result so any outcome stays reproducible.
Data privacyRequests are processed in memory only — invoices are never stored or logged.
SupportEmail hello@billhorse.com — we typically reply within 1–2 business days. No contractually guaranteed SLA yet; custom SLAs and priority support come with the Enterprise plan. See the changelog for what's new.
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), Peppol BIS Billing 3.0, EN 16931 UBL/CII. ZUGFeRD 1.0 detected with migration notice; encrypted PDFs reported explicitly.
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 →

Error handling

problem+json format, status codes, message languages and finding structure. Errors guide →

OpenAPI spec

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

Postman collection

All endpoints with sample invoices, ready to import. Download collection →

Validation rules

Every rule id explained, with fixes — in EN, DE and FR. Rule index →

Try without code

The browser validator uses the same engine. Open validator →