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
- No name search — you must supply the numeric KldB code. Resolve names via the BERUFENET/DKZ sibling APIs or the KldB catalogue.
entgeltis a median, in EUR/month gross, censored at the social-insurance ceiling — see GLOSSARY.md.- Region numbering is irregular (Bund/Ost/West + 16 states + 11 cities), not
1..16 — check
codes/regionen. - An empty 403 is ambiguous — a wrong or stale key and a WAF block of your network
look the same. The BA changed the key in 2026: the UUID
client_idthe bundesAPI README still prints is refused. Re-runentgeltatlas obtain-key; see the README’s 403 heads-up.