Reading many timeseries
Read the latest values of up to 2,000 timeseries in one call.
POST /timeseries/values returns the latest values of up to
2,000 timeseries over one shared time range, in one call. Use it when you need the same window
from many entities, such as the schedules of every plant in a zone, instead of one
values call per timeseries.
The timeseries are listed in a JSON body, so the list can be long. The call only reads: it is a POST because a request body has no length limit, where a URL does.
Request
| Body field | Meaning |
|---|---|
series | a list of {entity, name, provider}, 1 to 2,000 items: the same three things one values call names |
start, end | the time range, shared by every series; both inclusive |
cursor | the next of the previous page; send the rest of the body unchanged |
curl -X POST "$API/timeseries/values" -H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{
"start": "2026-11-03T12:00:00Z",
"end": "2026-11-03T13:00:00Z",
"series": [
{"entity": "plant:18W000000000AI3J", "name": "production.per_unit.esios_p48_final_programme_injection", "provider": "esios"},
{"entity": "plant:18W000000000F3DO", "name": "production.per_unit.esios_p48_final_programme_injection", "provider": "esios"},
{"entity": "unit:18W000000000AI2L", "name": "production.per_unit.actual_generation_per_unit_a73.18w000000000ai2l", "provider": "entsoe"}
]
}'P48 = "production.per_unit.esios_p48_final_programme_injection"
body = {
"start": "2026-11-03T12:00:00Z",
"end": "2026-11-03T13:00:00Z",
"series": [
{"entity": "plant:18W000000000AI3J", "name": P48, "provider": "esios"},
{"entity": "plant:18W000000000F3DO", "name": P48, "provider": "esios"},
{"entity": "unit:18W000000000AI2L",
"name": "production.per_unit.actual_generation_per_unit_a73.18w000000000ai2l", "provider": "entsoe"},
],
}
blocks = []
while True:
page = requests.post(f"{API}/timeseries/values", headers=HEADERS, json=body).json()
blocks += page["series"]
if not page["next"]:
break
body["cursor"] = page["next"]
for s in blocks:
print(s["entity"], [p["value"] for p in s.get("points", [])])The Python version prints:
plant:18W000000000AI3J [7.25, 7.25, 7.25, 7.0, 7.0]
plant:18W000000000F3DO [58.675, 56.7, 54.15, 51.375, 48.625]
unit:18W000000000AI2L []Response
One block per series, in the order listed. A block is what the one-series values call returns
for it (without as_of, which is on the page), so code that reads one reads the other.
{
"start": "2026-11-03T12:00:00Z",
"end": "2026-11-03T13:00:00Z",
"as_of": "latest",
"series": [
{
"entity": "plant:18W000000000AI3J",
"name": "production.per_unit.esios_p48_final_programme_injection",
"provider": "esios",
"unit": "MWh",
"resolution": null,
"publication_time": "declared",
"columns": ["value"],
"points": [
{"valid_time": "2026-11-03T12:00:00Z", "value": 7.25, "knowledge_time": "2026-11-03T13:55:20Z", "annotation": null},
{"valid_time": "2026-11-03T12:15:00Z", "value": 7.25, "knowledge_time": "2026-11-03T13:55:20Z", "annotation": null},
{"valid_time": "2026-11-03T12:30:00Z", "value": 7.25, "knowledge_time": "2026-11-03T13:55:20Z", "annotation": null},
{"valid_time": "2026-11-03T12:45:00Z", "value": 7.0, "knowledge_time": "2026-11-03T13:55:20Z", "annotation": null},
{"valid_time": "2026-11-03T13:00:00Z", "value": 7.0, "knowledge_time": "2026-11-03T13:55:20Z", "annotation": null}
]
},
{
"entity": "plant:18W000000000F3DO",
…
},
{
"entity": "unit:18W000000000AI2L",
"name": "production.per_unit.actual_generation_per_unit_a73.18w000000000ai2l",
"provider": "entsoe",
"unit": "MW",
"resolution": "PT15M",
…
"points": []
}
],
"next": null
}The A73 unit published nothing in that hour, so its points is empty; that is not an error. A
series with several columns returns all of them, as values per point.
Errors per series
A series that cannot be read does not fail the call. Its block carries error instead of
points, with the code and message the one-series call would have returned, and the rest of
the list is still read:
{
"entity": "plant:18W000000000AI3J",
"name": "production.per_unit.esios_p48_programme",
"provider": "esios",
"error": {
"code": "not_found",
"message": "no timeseries 'production.per_unit.esios_p48_programme' on plant:18W000000000AI3J",
"closest": ["production.per_unit.esios_p48_final_programme_injection"]
}
}code | When |
|---|---|
not_found | the entity, or the name on it, does not exist |
bad_request | the provider does not publish this timeseries (the error lists the ones that do), or the window holds more than 100,000 points at this series' resolution |
platform_unavailable | the data platform did not answer for this series; ask for it again |
The whole call is 400 only when the body itself is malformed: no series, more than 2,000, an
item missing entity, name or provider, end before start, or a cursor this API did not
issue for this list. It is 503 with Retry-After when the API is still loading its list of
plants, units and portfolios (for a minute or two after it starts); send the same body again.
Paging
A page holds whole series, in the order listed, up to 200,000 points in total. When the next
series would go over, the page stops and next is set: send the same body again with cursor
set to it. A single series is still capped at 100,000 points, as in the one-series call, so a page
always holds at least one series.
For example, 1,500 plant schedules over 120 days at quarter hours (about 11,500 points each) come back 17 series per page, in 89 calls instead of 1,500.
Limits
One call counts as one request against the rate limit, whatever the number of series. Without a
key a call may list at most 5 series (Use without a key). Only
the latest version is returned; for values as known at an earlier time (as_of), use the
one-series values call.