--- name: energy-data description: Query Elering public dashboard APIs for Estonian electricity prices, production, consumption, forecasts, balances, and cross-border flows. module: elering.mjs execution: get --- # Elering Energy Data Elering is the transmission system operator, and its dashboard API is the public record of what Estonian electricity cost and where it came from. Public JSON GET endpoints at `https://dashboard.elering.ee/api/`, no authentication. ## Retrieve **Use the module below — do not build the query URLs by hand.** One call takes the whole window the question asks about, fetches it, and returns an aggregated series plus totals computed in code. ```js const r = await electricityPrice({ from: "2026-01-01", to: "2026-07-31" }); show(r.totals); // { avgEurMwh, avgCentsPerKwh, min, minAt, max, maxAt, … } show(r.rows.slice(0, 5)); // one row per month, each period named ``` **A multi-year question is ONE call.** The API refuses a window longer than a year (`{"status":"BAD_REQUEST","messages":["Maximum period is 1 year"]}`), so the module splits the window into year-long requests and stitches them, then picks the row grain from the window: hours for a few days, days for a few months, months for a few years, years beyond that. Override with `by: "hour" | "day" | "month" | "year"`. Asking again per year burns steps and invites averaging the averages. Dates are **Estonian** dates: `{ from: "2026-06-01", to: "2026-06-30" }` is the whole of June in Tallinn, not a UTC window that spills three hours into July. ## The 2025-10-01 break — read this before comparing two periods The Nordic/Baltic market moved from **60-minute to 15-minute** periods at 2025-10-01 00:00 CET. On the wire the last hourly point is 2025-09-30T22:00Z and the first quarter-hourly one is 2025-09-30T22:15Z. A window crossing that date holds points standing for different lengths of time, so a plain mean over them overweights everything after the break fourfold. Every average the module returns is **weighted by the period each point covers**, and `resolutions` reports which market time units the window actually contained. When a window crosses the break the `note` says so — pass that on. ## Return - `totals.avgEurMwh` / `avgCentsPerKwh` — the day-ahead price. **Excludes network fee, excise and VAT**, so it is never what a household pays; say so. - `rows` — the series, each period named, in Estonian local time. - `electricitySystem()` — consumption, production and renewable production, integrated from metered MW into GWh. Only the measured `real` series is used; Elering's `plan` forecast is deliberately not carried, so nothing here can report a forecast as a fact. - `crossBorderFlow()` — interconnector flows, where a **negative number is export out of Estonia**. `estlink_1 + estlink_2` reconciles to `finland`. - Cite `source` (the URL fetched) and `page` (the dashboard a citizen can open). ## Limits - The Russian interconnectors read `null` since desynchronisation — a real absence, not a gap in the data. - Prices can be negative; that is a real market outcome, not a parse error. - Gas and the other Elering datasets are not covered here. ## Verify `success: true` and a non-empty series for the requested window. A window entirely in the future returns nothing, and the module throws saying so rather than reporting a zero price. ## Module API electricityPrice({from, to, by, region}) -> {source, page, region, unit, period, by, resolutions, totals, rows, note} electricitySystem({from, to, by}) -> {source, page, unit, period, by, totals, rows, note}