python-esios: Download Spanish electricity data in a few lines of Python
A Python library that wraps the ESIOS API into simple method calls — search indicators, fetch historical data, and get DataFrames with automatic caching.
Spain’s electricity system runs on 2,000+ indicators published by REE through the ESIOS API — prices, demand, generation by technology, cross-border flows, and more. But working directly with the API means writing boilerplate HTTP requests, handling pagination for long date ranges, and converting nested JSON into usable DataFrames.
python-esios wraps all of that into a clean Python interface. In this article, we’ll walk through the library’s main features and build real analysis with 4 practical use cases.
Before vs after: the “aha” moment
Without python-esios, fetching a week of Spanish electricity prices requires:
# Raw requests approach: manual headers, pagination, JSON parsing
import requests
import pandas as pd
TOKEN = "your-token-here"
headers = {
"Accept": "application/json; application/vnd.esios-api-v1+json",
"Content-Type": "application/json",
"Host": "api.esios.ree.es",
"x-api-key": TOKEN,
}
# You also need to handle chunking for long date ranges,
# timezone conversion, and JSON-to-DataFrame conversion manually
response = requests.get(
"https://api.esios.ree.es/indicators/600",
headers=headers,
params={"start_date": "2024-06-01", "end_date": "2024-06-07T23:59:59"},
)
data = response.json()["indicator"]["values"]
df = pd.DataFrame(data)
# Still need to: parse datetimes, filter by country, set index... With python-esios:
# python-esios: 3 lines
from esios import ESIOSClient
client = ESIOSClient()
df = client.indicators.get(600).historical("2024-06-01", "2024-06-07") Key differences:
- No boilerplate — token from env, headers handled internally
- Automatic chunking — long date ranges are split into API-friendly windows
- Parquet caching — repeated queries are served from local cache instantly
- Clean DataFrames — DatetimeIndex with Europe/Madrid timezone, geo-pivoted columns
Installation
pip install python-esios You’ll need an ESIOS API token. Request one for free from REE’s API page. Set it as an environment variable:
export ESIOS_API_KEY=your-token-here Exploring the ESIOS catalog
The ESIOS API exposes 2,000+ indicators. Let’s see how to navigate them.
List all indicators
df_indicators = client.indicators.list()
df_indicators[["name", "short_name"]].head(10)
Search by name
client.indicators.search("eólica")
Get an indicator’s metadata
handle = client.indicators.get(600)
handle <IndicatorHandle id=600 name='Precio mercado SPOT Diario'>Each handle exposes the indicator’s available geographies:
handle.geos Out6 lines
[{'geo_id': 1, 'geo_name': 'Portugal'},
{'geo_id': 2, 'geo_name': 'Francia'},
{'geo_id': 3, 'geo_name': 'España'},
{'geo_id': 8826, 'geo_name': 'Alemania'},
{'geo_id': 8827, 'geo_name': 'Bélgica'},
{'geo_id': 8828, 'geo_name': 'Países Bajos'}]You can resolve geography names to IDs:
handle.resolve_geo("España") 3Use case 1: Day-ahead electricity prices
Indicator 600 is the SPOT market price. Let’s compare Spain, France, and Portugal for a week in June 2024:
price = client.indicators.get(600)
df_price = price.historical("2024-06-01", "2024-06-07")
df_price = df_price[["España", "Francia", "Portugal"]]
df_price.head()
fig = px.line(
df_price,
title="Day-ahead electricity prices (June 2024)",
labels={"value": "€/MWh", "datetime": ""},
)
fig.update_traces(line_shape="hv")
fig.update_layout(legend_title_text="Country")
fig.show() 
Price statistics
df_price.describe().round(2)
Use case 2: Real-time demand
Indicator 1293 tracks actual electricity demand at 5-minute resolution:
demand = client.indicators.get(1293)
df_demand = demand.historical("2024-06-01", "2024-06-03")
df_demand.head()
fig = px.line(
df_demand,
title="Real-time electricity demand — Spain (June 2024)",
labels={"value": "MW", "datetime": ""},
)
fig.update_layout(showlegend=False)
fig.show() 
Use case 3: Generation mix — wind vs solar
Let’s compare wind and solar photovoltaic P48 scheduled generation (the latest program before real-time):
# 82 = Wind onshore P48, 84 = Solar PV P48
df_gen = client.indicators.compare(
[82, 84],
"2024-06-01", "2024-06-07",
)
df_gen.head()
fig = px.area(
df_gen,
title="Wind vs Solar PV scheduled generation P48 — Spain (June 2024)",
labels={"value": "MW", "datetime": ""},
)
fig.update_layout(legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="left", x=0), legend_title_text="", title_y=1.0, title_yanchor="top", margin=dict(t=80))
fig.show() 
Daily totals
daily = df_gen.resample("D").sum() / 1000 # Convert MW·5min to GWh approx
daily.columns = ["Wind (GWh)", "Solar PV (GWh)"]
daily.round(1)
Use case 4: Cross-country price comparison
The compare() method makes it easy to fetch multiple indicators into one DataFrame. Let’s look at how Spain’s price compares to demand patterns:
df_compare = client.indicators.compare(
[600, 1293],
"2024-06-01", "2024-06-03",
)
df_compare = df_compare.ffill() # Forward-fill hourly price into 5-min rows
df_compare.head()
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df_compare.index,
y=df_compare.iloc[:, 0],
name="Price (€/MWh)",
yaxis="y",
line_shape="hv",
))
fig.add_trace(go.Scatter(
x=df_compare.index,
y=df_compare.iloc[:, 1],
name="Demand (MW)",
yaxis="y2",
opacity=0.6,
))
fig.update_layout(
title="Price vs Demand — Spain (June 2024)",
yaxis=dict(title="€/MWh"),
yaxis2=dict(title="MW", overlaying="y", side="right"),
legend=dict(x=0.01, y=0.99),
)
fig.show() 
Conclusions
python-esios turns the ESIOS API into a Pythonic interface:
client.indicators.list()— browse the full catalogclient.indicators.search("...")— find indicators by nameclient.indicators.get(id).historical(start, end)— fetch data as DataFramesclient.indicators.compare([...], start, end)— merge multiple indicators- Automatic caching — parquet files for instant re-queries
- Geo resolution —
resolve_geo("España")instead of memorizing IDs
For more details, check the GitHub repository.
Resources
- GitHub: datons/python-esios
- PyPI: python-esios
- ESIOS API documentation
- Tutorial: ESIOS API with raw Python — if you want to understand the underlying API
- python-entsoe — sister library for European ENTSO-E data
- python-eia — sister library for US EIA data