Tutorial

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)
Tabla con 10 filas

Buscar por nombre

client.indicators.search("eólica")
Tabla con 60 filas

Obtener metadatos de un indicador

handle = client.indicators.get(600)
handle
Out
<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")
Out
3

Caso 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()
Tabla con 5 filas
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)
Tabla con 8 filas

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()
Tabla con 5 filas
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()
Tabla con 5 filas
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)
Tabla con 7 filas

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()
Tabla con 5 filas
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 completo
  • client.indicators.search("...") — busca indicadores por nombre
  • client.indicators.get(id).historical(start, end) — descarga datos como DataFrames
  • client.indicators.compare([...], start, end) — combina múltiples indicadores
  • Caché automático — archivos Parquet para consultas repetidas al instante
  • Resolución geográficaresolve_geo("España") en vez de memorizar IDs

Para más detalles, consulta el repositorio en GitHub.

Recursos

Suscríbete a nuestro newsletter

Recibe insights semanales sobre datos, automatización e IA.

© 2026 Datons. All rights reserved.