📈 Battery revenue with our forecast simulated:See tomorrow’s forecast →
progrunners / ENTSO-E API guide

ENTSO-E Transparency Platform API, as it actually behaves

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.

· Written from production use: we run ENTSO-E feeds inside CEPS Live and our intraday tooling for the Czech market.
On this page
  1. Getting an API key (security token)
  2. The shape of a request
  3. Document types worth knowing
  4. Bidding zone codes
  5. Reading the response
  6. Limits, errors and empty responses
  7. The official documentation, and what it leaves out
  8. The trap that invalidates backtests
  9. entsoe-py: Python example, or your own client
  10. Where ENTSO-E stops

Getting an API key (security token)

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.

  1. Create an account at transparency.entsoe.eu.
  2. Send an e-mail to transparency@entsoe.eu with the subject "Restful API access", and put the e-mail address of the account you just registered in the body.
  3. Wait. A human approves the request — in our case within three working days; other teams report up to a week. Then log in, open My Account Settings and generate the token yourself.

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.

Do not put the token in client-side code. Every request carries it in the URL, so it ends up in browser history, proxy logs and referrer headers. Keep the calls server-side.

The shape of a request

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=202609020000

Four 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.

Time format

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 daylight-saving trap. On the short October day a Czech or German delivery day has 25 hours, and on the March day it has 23. If you build a day by adding 24 hours to midnight local time, you will silently lose or duplicate an hour twice a year, and the error will look like a data problem rather than a code problem.

Document types worth knowing

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.

CodeWhat it returnsTypical use
A44Day-ahead pricesPrice series per bidding zone; the reference every spread is measured against.
A65Total loadprocessType=A01 for the forecast, A16 for the realised value.
A69Wind and solar forecastA01 day-ahead, A18 intraday. The intraday one is fresher and far less used.
A71Generation forecastTotal scheduled generation.
A75Actual generation per production typeThe realised counterpart to A69; how you measure forecast error.
A80Generation unit unavailabilityOutages. Published as urgent market messages — the interesting one for price spikes.
A85Imbalance pricesPer imbalance settlement period.
A86Imbalance volumesSystem imbalance quantity.
A83Activated balancing energyaFRR / mFRR volumes by direction.

Process types

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.

Bidding zone codes

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.

ZoneEIC code
Czech Republic10YCZ-CEPS-----N
Germany–Luxembourg10Y1001A1001A82H
Austria10YAT-APG------L
Slovakia10YSK-SEPS-----K
Poland10YPL-AREA-----S
Netherlands10YNL----------L
Hungary10YHU-MAVIR----U
France10YFR-RTE------C

Reading the response

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 = pos
Positions are sparse. When a value repeats, ENTSO-E may omit the following positions entirely: you get position 1 and then position 5, and positions 2 to 4 are implied to hold the last value. Code that assumes one point per interval silently produces a shorter series than the day it represents. This is the single most common bug we see in other people’s ENTSO-E integrations.

Namespaces change per document

The 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.

Limits, errors and empty responses

The official documentation, and what it leaves out

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:

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.

The trap that invalidates backtests

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.

Publication delay differs per document. Realised values appear hours after delivery, balancing data later still, and outage messages in near real time. If a feature uses data that was published after your decision point, it is lookahead — even when the timestamp looks fine.

entsoe-py: Python example, or your own client

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.79

Since 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.

Where ENTSO-E stops

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.

Frequently asked questions

Can I get ENTSO-E data without the API?
Yes. The Transparency Platform website shows every dataset and exports CSV or XLSX with a free account — see our guide to the ENTSO-E Transparency Platform. The API is worth it once you download the same data repeatedly.
Where is the ENTSO-E API documentation?
In ENTSO-E's Postman collection "Transparency Platform Restful API" on documenter.getpostman.com. It replaced the old Guide.html user guide, which no longer loads. It covers request parameters per document type; behaviour such as sparse positions and versioned forecasts is not documented there.
Is the ENTSO-E API free?
Yes. Registration and API access cost nothing, and there is no paid tier. You are expected to stay within the request limit and to credit the platform when you republish data.
How long does it take to get an API key?
Between one day and a week. The request is approved by a person, not automatically — send the e-mail to transparency@entsoe.eu with the subject "Restful API access" and the address of your registered account. Once access is enabled, you generate the token yourself in My Account Settings. The key is the same thing as the "security token" in the documentation.
Why does my query return an empty document?
Almost always one of three things: the time window is longer than the document allows, the domain parameter has the wrong name for that document type, or there genuinely is no data for that period. Shorten the window first — it is the quickest test.
What is the rate limit?
400 requests per minute, counted per IP address and per security token; going over it means a 10-minute ban. Backfills should be chunked by month with a pause between requests; bursts of daily queries will hit 429 quickly.
Can I get historical forecasts as they were published?
No. The API returns the latest version of a forecast, so historical downloads carry information that was not available at the time. If you need point-in-time data, you have to archive it yourself as it is published.
Should I use entsoe-py or write my own client?
Use entsoe-py for research and exploration. Write your own for production systems where you need retry control, caching, raw-response retention or document types the library does not cover.

We build this for a living

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