Vibe Coding #3 - Creando una librería Python para una API | Datos energéticos europeos ENTSO-E

Construyendo una librería Python para la API de la Plataforma de Transparencia ENTSO-E. Desde un wrapper básico hasta un paquete PyPI publicable con interfaz limpia, type hints y documentación.

11 de febrero de 2026 18:00 CET 1:50:34 EN
pythonentsoeapiopen-sourceenergylibrary

Recibe actualizaciones semanales

Sesiones de live coding, librerías Python para datos energéticos y tutoriales prácticos. Un email por semana, sin relleno.

Resultado

Una librería Python limpia y bien tipada que convierte la compleja API XML de ENTSO-E en DataFrames de pandas — lista para análisis y publicada en PyPI.

from entsoe import Client

client = Client(api_key="tu-token")
prices = client.prices.day_ahead(area="Spain", start="2026-02-01", end="2026-02-07")
# → DataFrame con precios horarios en EUR/MWh

El problema

  • La API REST de ENTSO-E devuelve XML complejo que requiere parseo manual
  • Las librerías Python existentes usan nombres de función como parámetros, lo que dificulta el autocompletado
  • Me llevó varios intentos entender qué códigos de área y parámetros necesitaba para cada consulta
  • En Vibe Coding #2 construimos herramientas MCP — ahora empaquetamos el cliente API subyacente como librería reutilizable

Plataforma de Transparencia ENTSO-E mostrando el mix de generacion en Francia por tecnologia

La solución

Partimos del cliente API básico usado en el Vibe Coding #2 para las herramientas MCP. El objetivo: transformarlo en una librería publicable con una interfaz limpia e intuitiva.

Diseño de la API

Empezamos analizando la librería eia-py (cliente para la API de energía de EE.UU.) como referencia de diseño:

Libreria Python EIA en accion — explorando rutas, facets y filtros con notacion punto

Lo que nos gusta: autocompletado, agrupación lógica de métodos, sin strings mágicos. El objetivo: llevar esa misma experiencia a ENTSO-E.

Diapositiva de agenda mostrando el patron de namespaces: client.prices, client.generation

Claude Code propuso tres opciones de diseño:

  1. Opción A: Funciones planas (get_day_ahead_prices(), get_generation_mix(), etc.)
  2. Opción B: Clases por tipo de dato (Prices, Generation, Transmission)
  3. Opción C: Namespaces tipados con accesores (client.prices.day_ahead(), client.generation.mix())

Claude Code proponiendo opciones de diseno — seleccionada la Opcion C (typed namespace accessors)

Seleccionamos la Opción C — namespaces con type hints completos para aprovechar el autocompletado del IDE y descubrimiento de métodos mediante IntelliSense.

Construyendo la librería

Claude Code generó toda la estructura de la librería en una sola pasada:

Claude Code creando la estructura de la libreria — namespaces, mappings y cliente HTTP

Primeros resultados

Ejecutamos la primera consulta real — mix de generación de Francia:

client = Client(api_key=os.getenv("ENTSOE_API_KEY"))
generation = client.generation.mix(area="France", start="2026-02-01", end="2026-02-07")

Primera consulta exitosa — DataFrame del mix de generacion de Francia en una linea de codigo

El resultado: un DataFrame limpio con tipos de generación en columnas y timestamps como índice — listo para análisis.

Graficos Plotly mostrando generacion real y prevista por tipo en Francia

Arquitectura de namespaces

La librería organiza métodos en cinco namespaces principales:

client.load          # Demanda eléctrica (actual + forecast)
client.prices        # Precios day-ahead, intraday, clearing
client.generation    # Mix, forecast, unidades instaladas
client.transmission  # Flujos transfronterizos, capacidad
client.balancing     # Precios de equilibrio, reservas

Cada namespace mapea a una clase dedicada que encapsula la lógica de consulta y transformación.

Manejo de códigos de área

La API de ENTSO-E usa códigos de área EIC (Energy Identification Code) — por ejemplo, 10YFR-RTE------C para Francia. Para evitar que los usuarios tengan que memorizarlos, implementamos un sistema de resolución:

# Soporta nombres, códigos ISO y códigos EIC
client.prices.day_ahead(area="Spain")     # → 10YES-REE------0
client.prices.day_ahead(area="ES")        # → 10YES-REE------0
client.prices.day_ahead(area="10YES-REE------0")  # → pass-through

Mapeo de codigos de area — abstrayendo codigos ENTSO-E a codigos simples como ES, FR, DE

Más de 60 áreas soportadas — desde países completos hasta zonas de oferta regionales.

Parsing XML → DataFrame

La API devuelve documentos XML en formato GL_MarketDocument. Implementamos parsers especializados para cada tipo de consulta.

Cada parser extrae series temporales del XML y las convierte en DataFrames de pandas con columnas tipadas y timestamps como índice.

División automática de consultas

La API de ENTSO-E tiene un límite de un año por consulta. Para rangos más largos, la librería divide automáticamente en consultas anuales y concatena los resultados:

# Consulta de 3 años → automáticamente dividida en 3 peticiones
prices = client.prices.day_ahead(
    area="Germany",
    start="2023-01-01",
    end="2026-01-01"
)
# → DataFrame con 3 años de datos

Lógica de reintentos y rate limiting

Implementamos reintentos exponenciales con jitter para manejar errores de red y límites de tasa:

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=4, max=10) + wait_random(0, 2),
    retry=retry_if_exception_type((httpx.HTTPStatusError, httpx.RequestError))
)
def _make_request(self, params: dict) -> bytes:
    response = self.session.get(self.base_url, params=params)
    response.raise_for_status()
    return response.content

Manejo de archivos ZIP

Algunos endpoints devuelven respuestas comprimidas en ZIP. La librería detecta y descomprime automáticamente:

def _extract_xml(self, content: bytes) -> str:
    # Detecta si la respuesta es ZIP
    if content.startswith(b'PK'):
        with zipfile.ZipFile(io.BytesIO(content)) as zf:
            xml_file = zf.namelist()[0]
            return zf.read(xml_file).decode('utf-8')
    return content.decode('utf-8')

Publicación en PyPI

Configuramos el proyecto para publicación usando uv:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "python-entsoe"
version = "0.1.0"
description = "Typed Python client for ENTSO-E Transparency Platform API"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "httpx>=0.27.0",
    "pandas>=2.2.0",
    "tenacity>=9.0.0",
]

Subimos el paquete en directo:

uv build
uv publish

Publicado en menos de 20 segundos:

python-entsoe publicado en PyPI — en vivo en menos de 20 segundos

La página del paquete en PyPI muestra el README con instrucciones de instalación, ejemplos rápidos y la referencia completa de namespaces:

Pagina del paquete en PyPI con documentacion README y ejemplos de uso

Para validar que todo funciona end-to-end, instalamos desde un proyecto completamente nuevo — pip install python-entsoe, import, consulta, gráfico. Todo funcionó al primer intento:

Usando pip install python-entsoe desde un proyecto separado — graficos de datos funcionando al instante

CI/CD con GitHub Actions

Configuramos GitHub Actions para automatizar tests y publicación:

Pipeline CI/CD de GitHub Actions — publicacion automatica en PyPI con cada push

El workflow ejecuta tests en cada push y publica automáticamente en PyPI cuando se crea un tag de versión.

Referencia

Namespaces y métodos principales

NamespaceMétodoParámetrosDescripción
client.loadactual()area, start, endDemanda eléctrica real (MW)
forecast()area, start, endPrevisión de demanda (MW)
client.pricesday_ahead()area, start, endPrecios day-ahead (EUR/MWh)
intraday()area, start, endPrecios intraday (EUR/MWh)
client.generationmix()area, start, endMix por tipo de fuente (MW)
forecast()area, start, endPrevisión de generación (MW)
installed()area, start, endCapacidad instalada (MW)
client.transmissionflows()from_area, to_area, start, endFlujos transfronterizos (MW)
capacity()from_area, to_area, start, endCapacidad de interconexión (MW)
client.balancingprices()area, start, endPrecios de equilibrio (EUR/MWh)

Estructura del proyecto

src/entsoe
__init__.py# Exports públicos
client.py# Cliente principal con namespaces
_http.py# Capa HTTP (httpx + reintentos)
_xml.py# Parsers XML → DataFrame
_mappings.py# Códigos de área EIC
exceptions.py# Excepciones personalizadas
namespaces
_base.py# Clase base para namespaces
load.py# Namespace de demanda
prices.py# Namespace de precios
generation.py# Namespace de generación
transmission.py# Namespace de transmisión
balancing.py# Namespace de equilibrio

Stack

  • Python 3.11+ / httpx / pandas / tenacity
  • uv (gestión de dependencias + build + publicación)
  • GitHub Actions (CI/CD)
  • PyPI (distribución)

Recursos

Índice
Buscar sección

Usamos cookies para mejorar tu experiencia y analizar el uso del sitio. Puedes gestionar tus preferencias en cualquier momento.