Health
as-of view
API keys
Access requests
approve creates a read key (btd+entsoe by default) — you send it to them yourselfCreate a key
the full key is shown once, then never againKeys
edit source access inline; revocation is immediateDownload
Quickstart
Everything is keyed. Pass the key in the X-API-Key header
(or ?api_key= for SSE, where headers aren't possible).
# who am I — identity, entitlements, limits, today's usage
curl -H "X-API-Key: $LFL_KEY" {ORIGIN}/v1/me
# discover series (AI-first metadata; filter with ?q= ?category= ?zone= ?source=)
curl -H "X-API-Key: $LFL_KEY" "{ORIGIN}/v1/catalog?q=imbalance"
# fetch data (UTC ISO8601; timestamps label period START; end is exclusive)
curl -H "X-API-Key: $LFL_KEY" \
"{ORIGIN}/v1/series/imbalance_price/data?zone=EE&start=2026-07-10T00:00:00Z&end=2026-07-11T00:00:00Z"
The machine-readable OpenAPI schema lives at {ORIGIN}/openapi.json —
keyed like everything else, so fetch it with the X-API-Key header.
Concepts
- Bi-temporal storage. Every value carries
valid_time(the delivery interval it describes) andknowledge_time(when it became knowable). Revisions are archived, never overwritten. Passas_of=<ts>to any data query to reconstruct exactly what was knowable at that instant. - Forecast vintages. Forecasts keep every run (
issued_at). Default = best-known value per period;issue_time=latestreturns the newest coherent curve; an exact timestamp returns that vintage. List runs via/v1/series/{id}/runs. - Dimensions.
zone(EE/LV/LT/…) andentity(corridor, station, fuel) are query parameters, never part of the series id. Direction and model family are part of the id (afrr_lmp_up,wind_forecast_ecmwf). - Sources. A series can be carried by several upstreams (e.g. BTD primary, ENTSO-E secondary). You get the first source your key is entitled to.
- Storage shapes. Scalar observations, forecasts, native 4-second ticks
(capped at 2 days/request), and bid stacks (query
/bids, not/data).
REST API
| endpoint | what it returns |
|---|---|
| GET /v1/catalog | Series list with unit, zones, resolution, sources. Filters: q, category, zone, source, status |
| GET /v1/catalog/{id} | Full metadata: description, quirks, publication lag, usage hints, entities |
| GET /v1/catalog/categories | The category taxonomy tree |
| GET /v1/series/{id}/data | Values in [start, end). Params: zone, entity, source, as_of, issue_time, format=json|csv |
| GET /v1/series/{id}/latest | Newest value + staleness for one zone/entity |
| GET /v1/series/{id}/runs | Forecast vintages, newest first |
| GET /v1/export | Bulk file download: series (any number), start/end, zones, tz (IANA), format=csv|xlsx. Excel = one sheet per series, a column per zone |
| GET /v1/series/{id}/bids | Bid-stack rows (price, quantity, direction, merit position) per MTU |
| GET /v1/series/{id}/validity | Per-day integrity audit: stored vs expected counts, revisions, knowledge lag |
| GET /v1/jobs/{id}/runs | Run history of one ingest job (window, rows changed, errors) |
| GET /v1/stream | SSE push of change events (?api_key= auth) |
| GET /v1/me | Your key: scopes, source entitlements, limits, usage today |
| /v1/admin/keys[/{id}] | Key management (admin scope): GET list w/ usage, POST create, PATCH access/limits, DELETE revoke (+?purge=true deletes a revoked key for good) |
| PATCH /v1/admin/series/{id} | Set or clear the expected-arrival override (admin) — the schedule the catalog, API and MCP report for model building |
| GET /v1/status, /freshness, /sources, /storage | Pipeline health: expectation vs reality, per-series freshness, what is physically stored |
| GET /v1/health, /v1/summary | Public: liveness + counts-only platform summary |
| POST /v1/access-request | Public: ask the operator for a key (name, contact, use case) — keys are never self-service |
Range caps: 92 days per request (2 days for tick series, 7 for bid stacks). Split longer
ranges into chunks. All error detail strings say what to do instead.
# point-in-time (backtest-grade): the day-ahead curve as it was knowable at 09:00
curl -H "X-API-Key: $LFL_KEY" \
"{ORIGIN}/v1/series/day_ahead_price/data?zone=EE&start=2026-07-10T00:00:00Z&end=2026-07-11T00:00:00Z&as_of=2026-07-10T09:00:00Z"
# one coherent forecast run instead of the best-known splice
curl -H "X-API-Key: $LFL_KEY" \
"{ORIGIN}/v1/series/wind_forecast_ecmwf/data?zone=LV&start=2026-07-11T00:00:00Z&end=2026-07-13T00:00:00Z&issue_time=latest"
Series reference
Every series id the API serves, with what it is and when new data
arrives. Click a row to open the series. Full metadata (quirks, usage
hints, entities) via GET /v1/catalog/{id}.
Live push (SSE)
One long-lived connection; update events fire the moment ingestion writes a
value that actually changed. Payload lists the affected series+zones — fetch fresh values
via /data or /latest. Events are named: use
addEventListener, not onmessage.
const es = new EventSource("{ORIGIN}/v1/stream?api_key=" + KEY);
es.addEventListener("update", (ev) => {
const { series, max_valid_time } = JSON.parse(ev.data);
// [{series_key: "imbalance_price", zone: "EE"}, ...]
});
es.addEventListener("heartbeat", () => {/* every 30 s, keeps proxies open */});
MCP for AI agents
The same data over the Model Context Protocol — 7 tools (catalog search, series description, data with auto-downsampling, latest values, forecast runs, source health). Values are returned as compact CSV-in-text, resampled to ≤300 points.
# Claude Code / Claude Desktop, hosted instance (streamable HTTP — note the trailing slash)
claude mcp add --transport http lfl-power-data {ORIGIN}/mcp/ \
--header "X-API-Key: $LFL_KEY"
# self-hosted / local checkout (stdio)
claude mcp add lfl-power-data -- uv run --directory ~/Desktop/E2E_LFL lfl mcp
Then ask things like "compare the latest ECMWF vs GFS wind forecast for LV" — the agent discovers series and semantics through the catalog tools.
Access, limits & licensing
- Keys.
lfl_<key_id>_<secret>; the secret is stored hashed and shown once at creation. Keys are issued by the operator only — request one from the launch page ("Request access") and you will hear back at the contact you give. Lifecycle via CLI:lfl keys create --name alice [--sources btd,entsoe] [--expires-days 90],lfl keys list,lfl keys revoke <key_id>. Revocation is immediate. - Source entitlements. Each key may be restricted to specific upstreams.
Syspower (Volue/SKM) data is contractually licensed — it is only granted to keys
whose owners hold their own license. Unentitled requests get a 403 with the reason;
multi-source series transparently fall back to a source you may read; the SSE stream
only shows you events for series you can access. Check yours at
/v1/me. - Rate limit. 600 requests/minute per key (per-key override possible). On 429,
honor
Retry-After. - Metering. Requests and points served are recorded per key per day —
visible in
/v1/measusage_today. - Scopes.
readfor data;adminunlocks the Admin console and the/v1/admin/keysendpoints — create keys, edit source access, change rate limits, revoke. Every mutation is written to the audit log.
Errors
All errors are RFC 9457 application/problem+json with a detail
written for self-correction:
{"type": "about:blank", "title": "Not entitled", "status": 403,
"detail": "This key is not entitled to source(s) ['syspower'] ... contact the operator."}
| status | meaning |
|---|---|
| 401 | No key presented |
| 403 | Key unknown/revoked/expired, or not entitled to this source |
| 404 | Unknown series id, or no data yet |
| 422 | Bad parameters — the detail names the parameter and the fix |
| 429 | Rate limited — wait Retry-After seconds |