Responses, formats and CSV
How responses are structured, how numbers and dates are encoded and how to get lists as CSV into Excel or your database.
JSON
#All responses come as JSON in UTF-8, with the header Content-Type: application/json; charset=utf-8. Field names are German, lowercase and underscored, for example mindestlohn_brutto_stunde. The REST API, the SDK, the CLI with --format json and MCP (in the structuredContent field) all return the same fields in the same form.
An example, the public holidays in Hesse (Hessen) for 2027 (the list is shortened to two entries here):
Numbers, amounts and dates
#| Type | Representation | Example |
|---|---|---|
| Monetary amounts | Number in euros with a period as the decimal separator, no currency symbol | 13.9 |
| Percentages | Number in percent, not as a fraction | 14.6 means 14.6 percent |
| Dates | String in the format YYYY-MM-DD | 2027-01-01 |
| Booleans | true or false | true |
| No value | null, when a field does not apply in this case or is still open | null |
| Lists | JSON array, even when it has just one entry | [] |
In your own software, it is best to calculate monetary amounts in cents or with decimal types, not floating-point numbers. The API returns amounts exactly as they appear in the legal source.
Units of the components
Datasets built from components, for example mini-job levies (Minijob-Abgaben) or tax-free amounts, name the field einheit for each value:
| einheit | Meaning |
|---|---|
euro | Euros |
euro_stunde | Euros per hour |
euro_tag | Euros per day |
euro_monat | Euros per month |
euro_jahr | Euros per year |
euro_km | Euros per kilometer |
euro_uebernachtung | Euros per overnight stay |
prozent | Percent |
prozent_listenpreis | Percent of the gross list price |
tage | Days |
anzahl | plain count, no unit |
fahrten | Trips |
arbeitsplaetze | Positions |
km | Kilometers |
Fields in every response
#| Field | Meaning |
|---|---|
datensatz | Id of the dataset, for example mindestlohn |
rechtsgrundlage | Legal provision the value or calculation is based on |
hinweise | List of limitations and assumptions you should show your users |
datenstand | Date and sequence number of the last changelog entry affecting this dataset, for example 2026-09-24.14 |
Table datasets also return gueltig_ab, gueltig_bis, quelle and stand, see Validity in the response. All fields of every dataset are listed in the reference.
datenstand is built for auditors: two responses with the same datenstand rest on the same checked data, whenever you fetched them. The id points straight to the entry in the changelog. The header X-Datenstand carries the same value and, for CSV, is the only place it appears (CSV has no field for it).
CSV
#Lists are also available as a CSV file with format=csv: every history, the public holidays, the public holidays on working days from the working-days calculator, and the components of the contribution rates. There is no CSV for single values; the API then responds with ungueltiger_parameter.
- Built to RFC 4180: first line with column names, CRLF line endings, fields containing the delimiter or quotes wrapped in quotes.
- UTF-8 with a BOM, so Excel displays German umlauts correctly.
- Nested fields become their own columns:
quelle.titelbecomesquelle_titel. - The filename is in the
Content-Dispositionheader, for example feiertage.csv.
CSV in Excel
#Excel with German settings expects semicolons as the delimiter and commas in decimal numbers. With trennzeichen=semikolon you get exactly that, and the file opens correctly with a double click:
IMPORTDATA, and therefore no key. Download the CSV file and import it, or use Apps Script with UrlFetchApp and the Authorization header.Response headers
#| Header | Content |
|---|---|
Content-Type | application/json or text/csv, each with charset=utf-8 |
X-Kontingent-Limit | Requests per month under your plan, on every response made with a key |
X-Kontingent-Verbraucht | Requests used in the current month, including this one |
X-Datenstand | the same value as the field datenstand, also for CSV |
Cache-Control | for dataset responses private, max-age=…, must-revalidate until the next midnight German time, see Saving requests; otherwise no-store |
ETag | built from datenstand and the request, for If-None-Match, see Saving requests |
Access-Control-Allow-Origin | *, calls from the browser are technically possible, see REST API |
WWW-Authenticate | only with status 401 |
Full export and offline data
#GET https://api.quellenkontor.dev/v1/export returns the entire table inventory in one call: for every dataset with a history (15 of 23) the full history as JSON, plus a link to the dataset's own CSV file. Useful for a first import into your own database or for a backup. Plain calculators without a table, for example notice period or company car, are not included: they have no history to export.
The export needs a key and counts as a single request, no matter how many datasets it contains.
Offline data in the JavaScript and Python SDKs
Both SDKs ship the same history that /export returns, built into the package itself. If the API is unreachable after every retry, the SDK works out the matching step for the requested date itself from the bundled data instead of throwing an error. That response then carries offline: true and the package's own datenstand, not the currently official one, and it is missing the computed extra fields such as zitat, vorheriger_wert or naechster_wert. Plain calculators without a history stay a network error without a connection; there is no offline data for them. Details and how to turn it off are under JavaScript SDK and Python SDK.
Something missing or unclear? Write to us and we will extend the docs.