Dieses Dokument gibt es nur auf Englisch.

smard-cli — Usage

Task-oriented examples for the smard CLI, a client for the open SMARD chart-data API run by the Bundesnetzagentur — German electricity-market generation, consumption / residual load and wholesale prices. All output is JSON, so the examples pipe through jq where it helps.

Install

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

The installed binary is smard. To run from a checkout instead of a global install, replace smard with node apps/cli/dist/src/cli/index.js (after npm run build).

Concepts in one line

A series is addressed by a (filter, region, resolution) triple. filter is a numeric series id (smard filters), region is a grid/bidding-zone code (smard regions), resolution is one of hour | quarterhour | day | week | month | year (smard resolutions). Generation/consumption values are in MWh, wholesale prices in EUR/MWh.


Use cases

1. Discover the available filters (series ids)

Why: you need the numeric filter id before you can request any data.

smard filters                       # full catalogue
smard filters --group consumption   # only one group

--group accepts generation | consumption | price | forecast. Output is an array of { id, label, group }. Grab just the ids for prices:

smard --compact filters --group price | jq '[.[].id]'
# [4169, 5078, 4996, ...]

2. List the valid regions and resolutions

Why: region and resolution are validated against fixed value sets; check them before scripting.

smard regions       # ["DE","AT","LU","DE-LU","DE-AT-LU","50Hertz","Amprion","TenneT","TransnetBW","APG","Creos"]
smard resolutions   # ["hour","quarterhour","day","week","month","year"]

DE-LU is the German-Luxembourg bidding zone; 50Hertz/Amprion/TenneT/ TransnetBW are the four German TSO control areas; APG/Creos are the Austrian/Luxembourg TSOs.

3. Get the newest data in one call

Why: latest reads the timestamp index and fetches the most recent window for you, so you don’t have to discover the timestamp first.

# Newest hourly day-ahead wholesale price for the DE-LU bidding zone (EUR/MWh)
smard latest 4169 DE-LU hour

Output is { meta_data, series }, where series is an array of [epochMillis, value] pairs, each timestamp the start of its hour. The window runs to the end of the week, so .series[-1] is usually [ts, null], and the last non-null point is the last published hour — often tomorrow 23:00, since the next day’s prices come out after the midday day-ahead auction. Show the price for the current hour:

smard --compact latest 4169 DE-LU hour \
  | jq -c '[.series[] | select(.[1] != null and .[0] <= now * 1000)][-1]'
# [1789502400000,199.46]

Since 1 October 2025 the day-ahead market sets quarter-hour prices: each hour value is the mean of the four quarterhour prices (smard latest 4169 DE-LU quarterhour).

4. Latest renewable generation (photovoltaics / wind)

Why: track the most recent realised feed-in by technology, in MWh.

smard latest 4068 DE hour    # Photovoltaik (PV)
smard latest 4067 DE hour    # Wind Onshore
smard latest 1225 DE hour    # Wind Offshore

Sum the latest PV window into a single total:

smard --compact latest 4068 DE hour | jq '[.series[][1] | select(. != null)] | add'

5. Total grid load vs. residual load

Why: compare total consumption (filter 410) against residual load (filter 4359, the load left after subtracting renewables) for the same window.

smard latest 410  DE week    # Stromverbrauch: Gesamt (total grid load)
smard latest 4359 DE week    # Stromverbrauch: Residuallast (residual load)

6. Fetch one explicit window (timestamps → series)

Why: pull a specific historical window rather than the newest one. Take the timestamp from timestamps: only a listed window start works, any other value returns a 404. Old windows stay available (the 4169 DE-LU hour index goes back to 2018), so a timestamp read once from the list keeps working.

smard timestamps 4169 DE-LU hour          # -> [ ..., <valid epoch-ms timestamps> ]

# Pick the last available window and fetch it
TS=$(smard --compact timestamps 4169 DE-LU hour | jq '.[-1]')
smard series 4169 DE-LU hour "$TS"

timestamps returns a plain number[] of epoch-millisecond window starts; series returns the same { meta_data, series } shape as latest.

7. Day-ahead price series for a neighbouring market

Why: each neighbouring bidding zone has its own price filter id; the region for these price series is DE-LU.

smard latest 254 DE-LU hour    # Großhandelspreis: Frankreich
smard latest 256 DE-LU hour    # Großhandelspreis: Niederlande
smard latest 259 DE-LU hour    # Großhandelspreis: Schweiz

Find any price filter id by label without leaving the shell:

smard --compact filters --group price | jq '.[] | select(.label | test("Frankreich"))'

8. Generation forecast for the next windows

Why: the forecast group holds the published prognoses (wind, PV, total).

smard latest 125 DE hour     # Prognose: Photovoltaik
smard latest 122 DE hour     # Prognose: Gesamt (total)

9. Quarter-hour table data

Why: the table command reads the separate table_data endpoint for a single window (3 positional args: filter region timestamp, no resolution).

smard table 410 DE <timestamp>

Note: table_data has no discovery endpoint, and SMARD seems to have stopped publishing it. Checked 2026-09-26: windows from 2021 to October 2023 answer (with the weekly window starts timestamps lists, e.g. smard table 410 DE 1698012000000), every window from December 2024 on returns a 404, for DE and DE-LU alike. For anything recent, use series/latest.

10. Pretty vs. compact, and saving for later

Why: pipe machine-readable output into files or other tools.

smard --compact latest 410 DE day > load.json     # single-line JSON
smard --compact latest 4068 DE hour | jq '.series | length'   # count data points

Global options

These apply to every command and may be placed before or after it (before is recommended), each at most once — a repeated option (also filters --group) is a usage error:

Option Description
-V, --version print the version
--base-url <url> API base URL (default https://www.smard.de; no query ?, fragment #, surrounding whitespace or control character — a tab, line break or ESC inside is written percent-encoded, e.g. %09; a literal % in the userinfo is written %25; userinfo is shown as *** in everything the CLI prints, a rejected value included). A plain http: base URL to a remote host logs one WARN record of smard.http on stderr (… sent unencrypted to <host> (http:, not https:)) before the first request (naming the base URL’s credentials when it carries any, never printing them); loopback hosts (localhost, 127.x, ::1) and the offline catalogue commands don’t warn, and stdout and the exit code are unchanged
--timeout <ms> time limit per request in ms, whole response included (0 = no timeout; default 30000; at most 2147483647)
--user-agent <ua> User-Agent header value (non-blank; no control characters or characters above U+00FF — a usage error before any request)
--max-retries <n> retries for transient 429/503 responses and reset connections (0–10, default 2; each backs off linearly, or waits the server’s Retry-After when longer, up to 30 s; a longer one is not retried). Each retry logs one WARN record of smard.http before it waits (HTTP 503 from host: retry 1 of 3 in 2 s)
--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 [smard.http] …) or jsonl (one JSON object per line: ts, level, topic, msg). stdout is not affected
-h, --help help for the program or a command (smard <command> --help)

Exit codes: 0 success, 4 on a 404 from the API, 1 for any other error (network, timeout, parse, non-404 status), non-zero for usage errors.

Quelle auf GitHub ansehen →