Usage

entgeltatlas <command> [options]. Every command prints JSON (pretty by default, single-line with --compact). The API needs an X-API-Key — see README — except codes, which is offline.

Global options

Flag Meaning
--api-key <key> X-API-Key (env ENTGELTATLAS_API_KEY); a blank value is a usage error, not “no key”; an error never repeats the key
--base-url <url> API base (default https://rest.arbeitsagentur.de); http:/https: only, a path prefix is fine, a query (?), fragment (#), surrounding whitespace or a % in the password that isn’t an escape (write %25) is a usage error. A user:password@ part is sent as Basic auth but shown as ***@ in everything the CLI prints, usage errors included. The key and the userinfo go to the base URL’s origin only: a redirect to another host, port or scheme (http→https included) drops them, and a 401/403 then says so. A plain http: base URL other than loopback gets one warning on stderr per run, a WARN record of entgeltatlas.http (… unencrypted to <host> (http:, not https:)), naming the API key or the base URL’s credentials when they travel (never their value)
--timeout <ms> · --max-response-bytes <n> time limit for a whole request (default 30 s, 0 = none) · cap on a response body (default 100 MiB, 0 = none)
--max-retries <n> retries for a transient 429/503 or a reset connection, 0–10 (default 2); each waits 200 ms × attempt, or the server’s Retry-After (seconds or HTTP-date) when that is longer — never less, so Retry-After: 0 can’t cause a burst. A Retry-After above 30 s is not retried: the error names the requested wait and says to try again later. Each retry logs one WARN record of entgeltatlas.http before it waits (HTTP 503 from host: retry 1 of 3 in 2 s)
--user-agent <ua> User-Agent header
--compact single-line JSON
--log-format <format> How errors, warnings and notes are written to stderr: text (default; log4j style, 2026-10-09T14:03:12.481Z WARN [entgeltatlas.http] …) or jsonl (one JSON object per line: ts, level, topic, msg). stdout is not affected

--api-key and --user-agent (and ENTGELTATLAS_API_KEY) must be sendable as an HTTP header: a blank value, a control character (CR/LF, DEL; tab is fine) or a character above U+00FF is a usage error (exit 2) before any request. The key, from the flag or the env var, is trimmed first, as the library does, so a trailing CR/LF from a key file is dropped rather than rejected. A rejected value is never echoed raw: the key is shown as ***, and control characters in any echoed value as \uXXXX (\n for a line break). A rejected numeric value (the KldB code, a dimension code, --timeout, …) is shown only when it reads like a number, an unknown command only when it reads like a command name, and surplus arguments not at all, so a key typed in the wrong place — a stored one too — is not repeated.

config — the key in a credentials file

entgeltatlas config set api-key                         # typed at a prompt, without echo
entgeltatlas obtain-key | entgeltatlas config set api-key   # or piped in
entgeltatlas config get api-key [--reveal]
entgeltatlas config list
entgeltatlas config unset api-key

Precedence is --api-key > ENTGELTATLAS_API_KEY > the credentials file > none. config keeps the key in $XDG_CONFIG_HOME/entgeltatlas/credentials (else ~/.config/entgeltatlas/credentials), mode 0600, written atomically. config set reads the value from a prompt without echo or from stdin (at most 64 KiB), never from the command line (an extra argument, or a key given with --api-key, is a usage error, exit 2, and is not repeated; so is a mistyped name or a surplus argument to any config command — the error names the valid names, never what was typed); a blank value, or one with whitespace or a character no header can carry, is refused (exit 2). config get shows it masked (abcd…wxyz; **** below 20 characters, so for the published key; --reveal prints it whole); config list prints the masked values on stdout and the file’s path on stderr; config unset removes it. A missing name, a link, another user’s file or one others can read (chmod 600 fixes it) exits 1, and so does a hand-edited value config set would refuse (naming the file; config get and config list refuse it too, so config get api-key exits 0 only for a usable key) — and the file is read only when neither the flag nor the env var gave a key, so a problem with it never stops such a run.

entgelte — salary statistics

entgeltatlas entgelte <kldb> [-l <n>] [-r <n>] [-g <n>] [-a <n>] [-b <n>]

<kldb> is the numeric KldB-2010 code (3–5 digits, e.g. 84304) — not an occupation name. Each dimension flag narrows the slice. Code 1 is Gesamt only for -g, -a and -b; for -l it is Helfer and for -r Deutschland. An omitted flag sends no parameter, and the API answers one row per value of that dimension (all four age bands for an omitted -a; recorded live on 2026-10-06), so pass the dimensions you mean. Every row is checked against the flags given: if the API answers another slice (a Deutschland row for -r 11), nothing is printed and the exit code is 1 (“The API answered another slice than the one requested”). Run entgeltatlas codes to see all the numbers.

Flag Dimension Values
-l, --level <n> Anforderungsniveau 1 Helfer · 2 Fachkraft · 3 Spezialist · 4 Experte
-r, --region <n> Region 1 Deutschland … 30 (irregular; see codes/regionen)
-g, --gender <n> Geschlecht 1 Gesamt · 2 Männer · 3 Frauen
-a, --age <n> Alter 1 Gesamt · 2 <25 · 3 25–<55 · 4 ≥55
-b, --branch <n> Branche 1 Gesamt … 11

A code outside these tables (the ones codes prints) is a usage error (exit 2) before any request: what the API does with an unknown code is not live-verified, and if it ignored the parameter you would silently get the unfiltered slice. Each flag takes one value: giving one twice (-g 2 -g 3) is a usage error too — run one call per slice.

entgeltatlas entgelte 84304 -l 4 -r 11 -g 2      # Experte, Baden-Württemberg, Männer

Returns a JSON array of observations, each with entgelt (median), entgeltQ25/entgeltQ75 (quartiles), besetzung (headcount), and the labelled dimensions. A suppressed cell has entgelt: null (or an empty array) — not 0.

Reference lists (live)

entgeltatlas regionen       # region codes (r)
entgeltatlas geschlechter   # gender codes (g)
entgeltatlas alter          # age-band codes (a)
entgeltatlas branchen       # branch codes (b)

Each returns an array of { id, bezeichnung }. These are not in the official spec — if one is unavailable, use codes instead.

codes — offline dimension tables

entgeltatlas codes          # all of l/r/g/a/b, no API key, no network

Prints the static code tables (including l, which has no live endpoint). Use it to look up the numbers for the entgelte flags.

Exit codes

Code Meaning
0 success (help/version/bare invocation included)
1 other API or parse error (incl. a 2xx answer without the documented shape — not an array of salary rows or of {id, bezeichnung} codes, an empty reference list — or an empty body)
2 usage error (bad KldB code, bad flag)
3 401/403 — no key sent, a wrong or stale key (re-run obtain-key), or a WAF/IP block (see README)
4 404 — not found
6 network/transport failure (timeout, size cap)

A reader that stops early (| head, | jq exiting on the first match) ends the run quietly with 0. If stderr’s reader goes away, a failed run still exits with its own code.

obtain-key talks only to the key source (the BA’s Entgeltatlas web app, https://web.arbeitsagentur.de/entgeltatlas/), never to the API: it exits 4 when the source answers 404, 6 on a network failure, and 1 for any other failure (another status, no key stated, conflicting keys) — never 3. Library users get an EntgeltatlasKeySourceError (an EntgeltatlasApiError with status/url).

Gotchas

View the source on GitHub →