The ENTSO-E Transparency Platform API is one free REST endpoint, https://web-api.tp.entsoe.eu/api, that returns day-ahead prices, load, generation and outages for every European bidding zone as XML. Access needs a security token: register at transparency.entsoe.eu, then e-mail transparency@entsoe.eu with the subject “Restful API access” — a person approves access, in our case within three working days, and you then generate the 36-character token in your account settings. Timestamps are UTC (yyyyMMddHHmm) and the limit is 400 requests per minute per IP and per token.
The Transparency Platform gives you load, generation, prices and outages for every European bidding zone, for free. The official documentation tells you what the endpoints are. It does not tell you what breaks. This page covers both: the endpoints, and how the API actually behaves.
Only need a chart or a one-off spreadsheet? You do not need the API at all: the Transparency Platform website exports the same data without code.
Access is free but not automatic, and it is a two-step process that catches everyone out the first time. The key goes by three names — API key, token and securityToken — and they are all the same thing.
transparency@entsoe.eu with the subject "Restful API access", and put the e-mail address of the account you just registered in the body.The token is a 36-character UUID. It goes into every request as the securityToken query parameter. There is no OAuth, no refresh, no expiry that we have ever hit.
There is exactly one endpoint. Everything is a GET with query parameters, and the response is XML.
https://web-api.tp.entsoe.eu/api
?securityToken={TOKEN}
&documentType=A69
&processType=A01
&in_Domain=10YCZ-CEPS-----N
&periodStart=202609010000
&periodEnd=202609020000Four things decide what you get back: documentType (what kind of data), processType (forecast or realised), the domain parameter (which bidding zone or control area) and the time window.
Timestamps are UTC, formatted yyyyMMddHHmm. There is no timezone suffix and no way to ask for local time. If your market runs on CET/CEST, you convert on your side — and you handle the two days a year when that offset changes.
The full list has over eighty entries, most of which you will never touch. These are the ones that carry the data people actually build on.
| Code | What it returns | Typical use |
|---|---|---|
A44 | Day-ahead prices | Price series per bidding zone; the reference every spread is measured against. |
A65 | Total load | processType=A01 for the forecast, A16 for the realised value. |
A69 | Wind and solar forecast | A01 day-ahead, A18 intraday. The intraday one is fresher and far less used. |
A71 | Generation forecast | Total scheduled generation. |
A75 | Actual generation per production type | The realised counterpart to A69; how you measure forecast error. |
A80 | Generation unit unavailability | Outages. Published as urgent market messages — the interesting one for price spikes. |
A85 | Imbalance prices | Per imbalance settlement period. |
A86 | Imbalance volumes | System imbalance quantity. |
A83 | Activated balancing energy | aFRR / mFRR volumes by direction. |
The same document type means different things depending on processType: A01 day-ahead, A16 realised, A18 intraday total, A40 intraday process, A51 aFRR, A47 mFRR, A52 FCR. Getting this wrong returns valid XML with the wrong meaning, which is worse than an error.
Domains are EIC codes. Depending on the document type the parameter is in_Domain, outBiddingZone_Domain or controlArea_Domain — the documentation specifies which per document, and using the wrong one returns an empty response rather than an error.
| Zone | EIC code |
|---|---|
| Czech Republic | 10YCZ-CEPS-----N |
| Germany–Luxembourg | 10Y1001A1001A82H |
| Austria | 10YAT-APG------L |
| Slovakia | 10YSK-SEPS-----K |
| Poland | 10YPL-AREA-----S |
| Netherlands | 10YNL----------L |
| Hungary | 10YHU-MAVIR----U |
| France | 10YFR-RTE------C |
The XML nests a TimeSeries per series, a Period per contiguous block and a Point per value. Points carry a position, not a timestamp — you reconstruct the time from the period start and the resolution (PT60M, PT15M, sometimes PT30M).
import xml.etree.ElementTree as ET
import requests
NS = {"ns": "urn:iec62325.351:tc57wg16:451-6:generationloaddocument:3:0"}
def series(token, doc, proc, zone, start, end):
r = requests.get("https://web-api.tp.entsoe.eu/api", timeout=60, params={
"securityToken": token, "documentType": doc, "processType": proc,
"in_Domain": zone, "periodStart": start, "periodEnd": end})
r.raise_for_status()
root = ET.fromstring(r.content)
for ts in root.findall(".//ns:TimeSeries", NS):
for period in ts.findall(".//ns:Period", NS):
begin = period.find("ns:timeInterval/ns:start", NS).text
step = period.find("ns:resolution", NS).text
last = None
for point in period.findall("ns:Point", NS):
pos = int(point.find("ns:position", NS).text)
val = float(point.find("ns:quantity", NS).text)
yield begin, step, pos, val
last = posThe XML namespace differs between document families — generation and load use one, publication and balancing documents use others. Hard-coding one namespace works until the day you query a different document type. Read it off the root element instead.
429 and a 10-minute ban, with no retry-after worth trusting. Several machines sharing one token share one limit. Pace your backfills and fetch month by month rather than day by day.Acknowledgement_MarketDocument with a Reason code and text. Code 999 means "no matching data found" and is not a bug. Parse it; do not treat any 200 as success.The reference documentation is ENTSO-E’s own Postman collection, Transparency Platform Restful API. It lists every document type with a ready-made example request, and you can import it into Postman, set your token as a variable and run the calls without writing any code — the fastest way to check that your key works.
Two things to know before you rely on it:
Guide.html page on transparency.entsoe.eu. That address now returns an error; the Postman collection replaced it.For account and access questions ENTSO-E runs a knowledge base with a Web API section, linked from the collection, and answers at transparency@entsoe.eu.
This one is not in the documentation and it silently ruins quantitative work.
Forecasts are versioned, and the API serves you the latest version. When you download the day-ahead wind forecast for a date last year, you get the best forecast that existed for that date — not the forecast that was published before the market closed. If you feed that into a backtest, your model is trained on information nobody had at the time, and the results will look excellent and mean nothing.
There are only two honest ways around it: archive every forecast yourself at the moment it is published and backtest against your own archive, or treat forecast-derived features as unusable for anything but explanation. We learned this the expensive way.
The entsoe-py library wraps the endpoint and returns pandas objects. For exploration and one-off analysis it will save you an afternoon, and we recommend starting there.
For anything running unattended we ended up writing our own thin client, for three reasons: we needed control over retries and caching, we wanted the raw XML kept for audit, and we needed document types the wrapper did not cover. The endpoint is simple enough that a purpose-built client is a few hundred lines.
entsoe-py in five lines. Install it with pip install entsoe-py, pass the security token, and ask for a zone by its short name instead of an EIC code. Timestamps must be timezone-aware; the result is a pandas Series in local time. Tested with entsoe-py 0.8.1 on 26 September 2026:
import pandas as pd
from entsoe import EntsoePandasClient
client = EntsoePandasClient(api_key="your-entso-e-token")
start = pd.Timestamp("2026-09-25", tz="Europe/Brussels")
end = pd.Timestamp("2026-09-26", tz="Europe/Brussels")
prices = client.query_day_ahead_prices("DE_LU", start=start, end=end)
print(prices.head(3))
# 2026-09-25 00:00:00+02:00 196.99
# 2026-09-25 00:15:00+02:00 184.40
# 2026-09-25 00:30:00+02:00 175.79Since October 2025 the day-ahead auction clears in 15-minute periods, so a day is 96 values, not 24 — the library returns them as published. The same client has query_load, query_generation and query_imbalance_prices (Long and Short columns, see our imbalance prices page).
A rough rule: a wrapper for research, your own client for anything whose output somebody trades on.
If you want a starting point rather than a blank file, we published the thin version: entsoe-quickstart — one file, standard library only, MIT. It handles the three things listed above that a naive script gets wrong: per-period resolution, multi-day responses, and errors that arrive as XML with HTTP 200.
The Transparency Platform is a regulatory publication, not a trading feed. It is late by design, it is aggregated, and it says nothing about the order book.
For intraday work you need the exchange itself. In the Czech market that means OTE’s AMQP interface for the live XBID order book, authenticated with a client certificate — a different protocol, a different data model and a different set of problems. National TSOs publish their own faster feeds too: ČEPS for the Czech system imbalance, and regelleistung.net for German reserve auctions.
ENTSO-E is the right source for anything cross-border, comparative or historical. It is the wrong source for anything you need within the quarter hour.
We run ENTSO-E, OTE and ČEPS feeds in production — live dashboards, AMQP bridges to the intraday order book, and backtesting that respects publication times. If you need an integration that holds up under real trading, talk to us.
Get in touch