progrunners.comenergy & technology
progrunners / ENTSO-E API guide

The ENTSO-E API, as it actually behaves

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.

Written from production use: we run ENTSO-E feeds inside CEPS Live and our intraday tooling for the Czech market. Last reviewed September 2026.
On this page
  1. Getting an API key
  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 trap that invalidates backtests
  8. entsoe-py, or your own client
  9. Where ENTSO-E stops

Getting an API key

Access is free but not automatic, and it is a two-step process that catches everyone out the first time.

  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 processes it. In our case the token arrived in three working days; other teams report up to a week.

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

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

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 processed 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.
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?
Around 400 requests per minute per IP address. 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.

Explore the rest of the site

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