Quickstart
Fetch your first Dutch imbalance prices in under two minutes, with no API key.
There is no signup and no API key. Every endpoint below is public, and the only thing you need is a terminal.
Read the latest price
curl https://api.tolven.eu/v0/series/imbalance-prices/latestEach observation carries the value, the interval it applies to, and the
publication_ts and revision it came from. You always know which
revision you are looking at.
How fresh is latest
Imbalance settlement arrives in one batch per Dutch local day, not continuously. So the newest observation is normally between 1 and 25 hours old. It is youngest just after a batch lands and oldest just before the next one does. Plan around that shape rather than around a fixed interval. We poll far more often than upstream publishes, so our own delay is not what you are waiting on.
Measured 2026-08-16: upstream published at about 23:00 UTC on each of the previous 44 days. Before 2026-06-30 it published several hours later. The time also follows the Dutch local day rather than UTC, so expect it around 00:00 UTC while the Netherlands is on winter time. Treat that clock time as an observation of recent behaviour, not as a schedule, and build on the shape instead.
Read a window
curl "https://api.tolven.eu/v0/series/imbalance-prices?from=2026-07-01T00:00:00Z&to=2026-07-02T00:00:00Z"A window spans at most 31 days, one whole calendar month. The API rejects
a wider window with 400 rather than truncating it silently, so a partial
answer is never mistaken for a complete one. For longer spans, pull
history in bulk.
Ask what was known at a point in time
TenneT restates imbalance settlement after the fact. If you backtest against today’s values, you are trading on information you could not have had. Your returns will look better than they were.
Revisions are rare here, which is what makes them dangerous. Between 2026-06-18 and 2026-08-15 we observed one revision in 11,120 observations. That one moved a settlement price from €500.00/MWh to €113.49/MWh, thirteen days after the interval had closed. A backtest run against live data used the wrong value for that period, and nothing flagged it.
Add as_of and you get the latest revision known at that instant, with
anything published later hidden:
curl "https://api.tolven.eu/v0/series/imbalance-prices?from=2026-07-01T00:00:00Z&to=2026-07-02T00:00:00Z&as_of=2026-07-02T00:00:00Z"Same window, same series — but the values as they stood the moment the day closed, not as they were later restated.
One boundary to know about. Every series and bulk response carries
revisions_observed_from: the instant the API started watching upstream.
Points after it have true revision history. The API saw earlier points
only retrospectively: their revision: 1 is the final settled value, not
what was first published. Upstream serves no revision archive, so that
earlier history cannot be reconstructed. The API rejects an as_of before
the boundary with 400 rather than returning an empty result that would
read as “nothing was known”. The gaps endpoint is live-only in v0, so it
carries no boundary and takes no as_of.
Check for gaps before you trust a window
The API never interpolates gaps, so ask for them explicitly:
curl "https://api.tolven.eu/v0/gaps/imbalance-prices?from=2026-01-01T00:00:00Z&to=2026-07-01T00:00:00Z"You get interior gaps plus a coverage envelope: first_observed and
last_observed per field. Absence outside the envelope is not a gap, so
the API does not report it.
Pull history in bulk
For anything longer than a few days, skip JSON. The whole history is one object:
curl -O https://api.tolven.eu/v0/bulk/imbalance-prices/full.parquetThat is every redistributable revision back to 2014-12-05 in a single request. Take it once, then stay current from the monthly partitions:
curl https://api.tolven.eu/v0/bulk/imbalance-pricesEach month in the manifest carries its bounds, row count, byte size, ETag and an absolute URL. Frozen months keep stable URLs and ETags, so they cache indefinitely. Re-download only the months whose ETag moved, which is normally just the settlement tail.
import pandas as pd
df = pd.read_parquet("https://api.tolven.eu/v0/bulk/imbalance-prices/full.parquet")
# Every row carries publication_ts and revision, so point-in-time# filtering is yours to do; no query engine required.cutoff = pd.Timestamp("2026-08-01", tz="UTC")known_at_cutoff = ( df[df["publication_ts"] <= cutoff] .sort_values("revision") .groupby(["field", "ts"], as_index=False) .last())Rate limits
The JSON endpoints allow 120 requests a minute per IP. Bulk downloads
allow 60 a minute. Past a limit the API answers 429 with a Retry-After
header; the limit resets within a minute. No workflow here needs many
requests. The whole history is one download. Staying current after it
costs one manifest read plus the months that moved. Higher limits do not
exist yet. They would arrive with the paid tier. The energy-data
page says how to ask for it.
Where to go next
| Method | Path | What it does |
|---|---|---|
| GET | /v0/series/{dataset} | Windowed reads, optionally as_of |
| GET | /v0/series/{dataset}/latest | Most recent observation per field |
| GET | /v0/gaps/{dataset} | Interior gaps plus a coverage envelope |
| GET | /v0/bulk/{dataset}/full.parquet | The whole history in one Parquet |
| GET | /v0/bulk/{dataset} | Monthly Parquet manifest and downloads |
Full parameters, response shapes and error codes live in the API reference. The running API generates it, so it cannot drift. Before you build on any of this in production, read attribution and licensing; it says what you may redistribute.