Dieses Dokument gibt es nur auf Englisch.

Usage

Real, use-case-driven examples for the luftqualitaet CLI — a client for the open Umweltbundesamt Air Data API (air-quality indices, raw measurements, annual balances, exceedances, and the station/component metadata behind them).

Install

npm i -g @maschinenlesbar.org/luftqualitaet-cli

The installed binary is luftqualitaet. All examples below assume it is on your PATH. (If you are running from a checkout instead, replace luftqualitaet with node apps/cli/dist/src/cli/index.js.)

Every command prints JSON to stdout, so it pipes cleanly into jq. Use the global --compact flag to emit single-line JSON.

Use cases

1. Look up component ids (which pollutant is which?)

Every measurement query needs a numeric component id. List them first so you know that 1 is PM₁₀, 5 is NO₂, 3 is O₃, and so on.

luftqualitaet components --lang en --index code

--index code keys the result by component code (e.g. PM10, NO2) instead of by id. Drop --index for the default id-keyed map. Use --lang de for German names.

2. Look up scope ids (which averaging definition?)

measures needs a scope id and thresholds takes an optional one (the averaging/aggregation definition — daily average, hourly average, 8-hour max, …). List them to pick one.

luftqualitaet scopes --lang en

Typical scopes: 1 = daily average (1TMW), 2 = one-hour average (1SMW), 5 = maximum eight-hour average (8SMW_MAX).

3. Get the air-quality index for a station over a day

Fetch the computed air-quality index for one station across an hour-ending time window. Hours run 1–24, where 24 is the hour ending at midnight.

luftqualitaet airquality \
  --station 143 \
  --date-from 2024-01-01 --time-from 1 \
  --date-to 2024-01-01 --time-to 24

All four window flags (--date-from, --time-from, --date-to, --time-to) plus --station are required.

The hours under .data.<station> are keyed by their start time (CET) and come back in time order: the API sometimes lists the newest hours out of order, and the client sorts them. The newest hour may be incomplete ([2] = 1 in airquality).

4. Discover which time windows a station actually has data for

Before requesting a window, check the available date range per station for air-quality data so you do not ask for gaps.

luftqualitaet airquality-limits | jq '.data | keys | length'

This command takes no options. The response wraps the per-station limits map under .data (alongside request and indices metadata), so index into .data to count or inspect the stations.

5. Fetch raw measurements for a station + component + window

Get the underlying measured values (not the index) for a single station, narrowed to one component and one scope over an hour-ending window. Example: PM₁₀ (component 1) as a daily average (scope 1) for one day.

luftqualitaet measures \
  --station 143 --component 1 --scope 1 \
  --date-from 2024-06-01 --time-from 1 \
  --date-to 2024-06-01 --time-to 24

--component and --scope are required along with the window flags and --station: the response is keyed station → hour, so it holds one series. Left out, the API would pick one for you (station 143 without either returns PM₂,₅ as a daily floating average), not all of them. A component the station doesn’t measure, or an unknown scope, returns "data": {} with exit 0 — check measures-limits.

6. Find the valid date range for a measurement series

measures-limits reports the available date range per scope/component/station — useful for picking a window that returns data.

luftqualitaet measures-limits --compact | jq '.data[] | select(.[0]=="1")' | head

--compact keeps the (large) payload on one line for easier streaming into jq.

7. Read the annual balance for a pollutant

Annual tabulations (Jahresbilanzen) summarise a component over a full year: the annual mean and the exceedance counts of its limit values, evaluated by UBA from the final, checked data (published in June of the following year). For “how many days over the limit in 2023?” this is the figure to use. Example: the PM₁₀ (component 1) balance for 2023, in German.

luftqualitaet annual-balances --component 1 --year 2023 --lang de

--year must be >= 2016. Use --lang en for English labels. --index is accepted but changes nothing here: the rows are arrays led by the station id with either value (only the echoed request differs).

8. List exceedances for a pollutant and year

How often has PM₁₀ exceeded its limit this year so far, month by month? transgressions is UBA’s exceedance table for the running year, from preliminary data. For a completed year it is not updated to the final data — its counts can differ from the annual balance (and from the stations’ own daily means), so use annual-balances there; the CLI logs a note (an INFO record) on stderr saying so.

luftqualitaet transgressions --component 1 --year 2026 --lang en

Same constraints as annual balances: --year >= 2016, plus optional --lang (and --index, which has no effect here either). In practice the data is complete from 2019: for 2016–2018 the API fails with HTTP 500 for some components (NO₂, PM₁₀; O₃ 2018 works), and the CLI adds a hint saying so.

9. Inspect the thresholds behind the air-quality index

See the threshold values used for a given use case. For air-quality index thresholds of ozone (component 3):

luftqualitaet thresholds --use airquality --component 3 --lang en

--use is airquality or measure. --component and --scope further narrow the result.

10. Pull combined metadata to build a measurement query

meta returns components, scopes, networks, stations, etc. in one call — the lookup tables you need to assemble measures / airquality queries. Then slice it with jq.

# Everything needed for measurement queries (German labels)
luftqualitaet meta --use measure --lang de | jq 'keys'

# Air-quality metadata requires a window
luftqualitaet meta --use airquality \
  --date-from 2024-01-01 --date-to 2024-01-01 \
  --time-from 1 --time-to 24

--use accepts measure, airquality, transgression, annualbalance, or map. When --use airquality, --date-from and --date-to are required (the --time-from / --time-to window flags are also accepted). The other uses take the same window optionally, and the API applies it there too: meta --use measure with dates lists only the stations active in that window. Give both dates or neither; the hour flags need the dates. An omitted hour is sent as 1 (start) or 24 (end): the API would otherwise fill it with the current hour, which can turn a one-day window around. A reversed window is rejected before any request.

Global options

These flags apply to every command and go before the subcommand:

Flag Purpose
-V, --version Print the CLI version.
--base-url <url> Override the API host (default https://luftdaten.umweltbundesamt.de); the CLI adds /api/air-data/v3 itself. A plain http: URL to a non-loopback host (not localhost, 127.0.0.0/8, ::1) gets one WARN record of luftqualitaet.http on stderr, … sent unencrypted (http:, not https:), naming the host (and the URL’s credentials, never printed); stdout and the exit code are unchanged.
--timeout <ms> Per-request timeout in milliseconds.
--user-agent <ua> Set the User-Agent header.
--max-retries <n> Retries for transient 429/503 responses and reset connections (0..10, default 2). Each retry backs off linearly (200 ms, 400 ms, …), or waits the server’s Retry-After when that is longer (up to 30 s; a longer one is not retried, and the error says so). Each retry logs one WARN record of luftqualitaet.http before it waits (HTTP 503 from host: retry 1 of 3 in 2 s).
--max-redirects <n> HTTP redirects to follow (0..20; 0 = none; default 5).
--max-response-bytes <n> Cap response body size in bytes (0 = unlimited; default 100 MiB).
--compact Print JSON on a single line instead of pretty-printed.
--log-format <format> How errors, warnings and notes are written to stderr: text (default; log4j style, 2026-10-09T14:03:12.481Z WARN [luftqualitaet.http] …) or jsonl (one JSON object per line: ts, level, topic, msg). stdout is not affected.
-h, --help Show help for the CLI or for any <command> --help.

Example combining a global flag with a command:

luftqualitaet --compact components --lang de

Quelle auf GitHub ansehen →