python-esios: Descarga datos eléctricos españoles en pocas líneas de Python
Una librería Python que envuelve la API de ESIOS en llamadas simples — busca indicadores, descarga datos históricos y obtén DataFrames con caché automático.
El sistema eléctrico español funciona con más de 2.000 indicadores publicados por REE a través de la API de ESIOS — precios, demanda, generación por tecnología, flujos transfronterizos y más. Pero trabajar directamente con la API implica escribir peticiones HTTP repetitivas, gestionar la paginación para rangos largos de fechas y convertir JSON anidado en DataFrames utilizables.
python-esios envuelve todo eso en una interfaz Python limpia. En este artículo recorreremos las funcionalidades principales de la librería y construiremos análisis reales con 4 casos de uso prácticos.
Antes vs después: el momento “aha”
Sin python-esios, descargar una semana de precios de electricidad en España requiere:
# Enfoque con requests: headers manuales, paginación, parsing JSON
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,
}
# También necesitas gestionar el chunking para rangos largos,
# conversión de zona horaria y conversión JSON-a-DataFrame manualmente
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)
# Aún falta: parsear datetimes, filtrar por país, configurar el índice... Con python-esios:
# python-esios: 3 líneas
from esios import ESIOSClient
client = ESIOSClient()
df = client.indicators.get(600).historical("2024-06-01", "2024-06-07") Diferencias clave:
- Sin boilerplate — token desde variable de entorno, headers gestionados internamente
- Chunking automático — los rangos largos se dividen en ventanas compatibles con la API
- Caché en Parquet — las consultas repetidas se sirven desde caché local al instante
- DataFrames limpios — DatetimeIndex con zona horaria Europe/Madrid, columnas pivotadas por geografía
Instalación
pip install python-esios Necesitarás un token de la API de ESIOS. Solicítalo gratuitamente en la página de la API de REE. Configúralo como variable de entorno:
export ESIOS_API_KEY=your-token-here Explorando el catálogo de ESIOS
La API de ESIOS expone más de 2.000 indicadores. Veamos cómo navegar por ellos.
Listar todos los indicadores
df_indicators = client.indicators.list()
df_indicators[["name", "short_name"]].head(10)
Buscar por nombre
client.indicators.search("eólica")
Obtener metadatos de un indicador
handle = client.indicators.get(600)
handle <IndicatorHandle id=600 name='Precio mercado SPOT Diario'>Cada handle expone las geografías disponibles del indicador:
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'}]Puedes resolver nombres de geografía a IDs:
handle.resolve_geo("España") 3Caso de uso 1: Precios del mercado diario
El indicador 600 es el precio del mercado SPOT. Comparemos España, Francia y Portugal durante una semana de junio de 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() 
Estadísticas de precios
df_price.describe().round(2)
Caso de uso 2: Demanda en tiempo real
El indicador 1293 registra la demanda real de electricidad con resolución de 5 minutos:
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() 
Caso de uso 3: Mix de generación — eólica vs solar
Comparemos la generación programada P48 de eólica y solar fotovoltaica (el último programa antes del tiempo real):
# 82 = Eólica terrestre P48, 84 = Solar fotovoltaica 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() 
Totales diarios
daily = df_gen.resample("D").sum() / 1000 # Convertir MW·5min a GWh aprox
daily.columns = ["Wind (GWh)", "Solar PV (GWh)"]
daily.round(1)
Caso de uso 4: Precio vs demanda
El método compare() facilita combinar varios indicadores en un solo DataFrame. Veamos cómo se relaciona el precio de España con los patrones de demanda:
df_compare = client.indicators.compare(
[600, 1293],
"2024-06-01", "2024-06-03",
)
df_compare = df_compare.ffill() # Forward-fill del precio horario en filas de 5 min
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() 
Conclusiones
python-esios convierte la API de ESIOS en una interfaz Pythónica:
client.indicators.list()— explora el catálogo completoclient.indicators.search("...")— busca indicadores por nombreclient.indicators.get(id).historical(start, end)— descarga datos como DataFramesclient.indicators.compare([...], start, end)— combina múltiples indicadores- Caché automático — archivos Parquet para consultas repetidas al instante
- Resolución geográfica —
resolve_geo("España")en vez de memorizar IDs
Para más detalles, consulta el repositorio en GitHub.
Recursos
- GitHub: datons/python-esios
- PyPI: python-esios
- Documentación de la API de ESIOS
- Tutorial: API de ESIOS con Python crudo — si quieres entender la API subyacente
- python-entsoe — librería hermana para datos europeos de ENTSO-E
- python-eia — librería hermana para datos de EIA de EE.UU.