Tutorial

Fetching Electricity Prices from OMIE with Python

Access real-time and historical day-ahead electricity prices from the Spanish electricity market operator—no API key required.

Spanish electricity prices swing from €10 to €200 per MWh within a single week. With a simple Python function, you can fetch this data directly from OMIE—no API key required—and use it for energy analytics, battery optimization, or cost forecasting.

Seven days of hourly electricity prices from OMIE

In this article, you’ll build a reusable function to download any date range of 15-minute resolution prices for Spain and Portugal.

Questions

  1. Where does OMIE publish electricity price data?
  2. How do you download and parse a single day’s prices?
  3. How do you generalize this into a reusable function?
  4. How do you fetch multiple days efficiently?
  5. What are the common pitfalls to avoid?

Implementation

Finding OMIE data

OMIE publishes market data at omie.es/en/file-access-list. Navigate to:

  1. Day-ahead market
  2. Day-ahead market prices in Spain

The files follow a predictable naming pattern: marginalpdbc_YYYYMMDD.1

For example, the latest day-ahead prices might be at: https://www.omie.es/en/file-download?parents=marginalpdbc&filename=marginalpdbc_20260112.1

Download a single day

Let’s start by downloading yesterday’s prices directly:

import pandas as pd
import requests
from datetime import datetime, timedelta

yesterday = datetime.now() - timedelta(days=1)
date_str = yesterday.strftime('%Y%m%d')

url = f"https://www.omie.es/en/file-download?parents=marginalpdbc&filename=marginalpdbc_{date_str}.1"

Download: https://www.omie.es/en/file-download?parents=marginalpdbc&filename=marginalpdbc_20260111.1

Let’s look at the raw content:

MARGINALPDBC; 2026;01;11;1;108.2;108.2; 2026;01;11;2;97.72;97.72; 2026;01;11;3;93.7;93.7; 2026;01;11;4;87.99;87.99; 2026;01;11;5;90;90;

This is a semicolon-separated file with a header row (“MARGINALPDBC;”). The file contains 96 rows - one for each 15-minute period of the day. The columns are year, month, day, period (1-96), price Portugal, and price Spain. Pandas can read it directly from the URL:

df_raw = pd.read_csv(
  url, sep=';', skiprows=1, header=None,
  names=['year', 'month', 'day', 'period', 'price_portugal', 'price_spain', '_']
)

df_raw = df_raw.dropna(subset=['period'])
First 10 rows of 15-minute price data
Raw OMIE data

Parse the data

Each period represents a 15-minute interval (period 1 = 00:00, period 2 = 00:15, etc.):

df_prices = df_raw[['period', 'price_spain', 'price_portugal']].copy()
df_prices['datetime'] = pd.to_datetime(yesterday.date()) + pd.to_timedelta((df_prices['period'] - 1) * 15, unit='m')
df_prices = df_prices.set_index('datetime')
Electricity prices at 15-minute resolution for Spain and Portugal
15-minute prices

Price summary for 2026-01-11: - Minimum: 50.25 €/MWh - Maximum: 108.20 €/MWh - Spread: 57.95 €/MWh

Visualize the daily profile

Hourly electricity prices for Spain and Portugal

Create a reusable function

Now let’s wrap this into a function that can fetch any date:

def omie_day_ahead(date):
    """
    Fetch day-ahead electricity prices from OMIE for a specific date.

    Parameters:
        date: datetime object

    Returns:
        DataFrame with columns: period, price_spain, price_portugal (96 rows per day)
        Returns None if data unavailable
    """
    date_str = date.strftime('%Y%m%d')
    url = f"https://www.omie.es/en/file-download?parents=marginalpdbc&filename=marginalpdbc_{date_str}.1"

    try:
        df = pd.read_csv(url, sep=';', skiprows=1, header=None,
                         names=['year', 'month', 'day', 'period', 'price_portugal', 'price_spain', '_'])
        df = df.dropna(subset=['period'])
        df['datetime'] = pd.to_datetime(date.date()) + pd.to_timedelta((df['period'] - 1) * 15, unit='m')
        return df.set_index('datetime')[['period', 'price_spain', 'price_portugal']]
    except Exception:
        return None

Test with a few different dates:

df_yesterday = omie_day_ahead(datetime.now() - timedelta(days=1))
df_week_ago = omie_day_ahead(datetime.now() - timedelta(days=7))
df_future = omie_day_ahead(datetime.now() + timedelta(days=7))
  • Yesterday: 96 periods (96 = 24 hours × 4)
  • Week ago: 96 periods
  • Future date: 0 periods (expected - data not yet available)

Fetch multiple days

For analysis spanning weeks or months, we need to fetch a date range:

import time

def omie_day_ahead_range(start_date, end_date, delay=0.1):
    """
    Fetch OMIE prices for a date range.

    Parameters:
        start_date: First date to fetch
        end_date: Last date to fetch (inclusive)
        delay: Seconds between requests (be nice to the server)

    Returns:
        DataFrame with all available data
    """
    all_data = []
    current = start_date

    while current <= end_date:
        df = omie_day_ahead(current)
        if df is not None:
            all_data.append(df)
        current += timedelta(days=1)
        time.sleep(delay)

    if not all_data:
        return None

    return pd.concat(all_data)
end = datetime.now() - timedelta(days=1)
start = end - timedelta(days=6)

df_week = omie_day_ahead_range(start, end)

Fetched 672 periods across 7 days.

Seven days of hourly electricity prices from OMIE

Common pitfalls

  1. Period numbering: OMIE uses periods 1-96 (15-minute intervals). Period 1 = 00:00, period 96 = 23:45.
  2. Data availability: Day D prices are published around 13:00 CET on day D-1. Don’t fetch today’s prices before they exist.
  3. Rate limiting: Add delays between requests when fetching many days. OMIE may block aggressive scrapers.
  4. DST transitions: On daylight saving time change days, you’ll get 92 or 100 periods instead of 96.

Conclusions

OMIE electricity price data is freely accessible via predictable URLs:

  • Single day: https://www.omie.es/en/file-download?parents=marginalpdbc&filename=marginalpdbc_YYYYMMDD.1
  • Format: Semicolon-separated, 96 periods per day (15-minute resolution)
  • Coverage: Spain and Portugal day-ahead prices

With this data, you can build energy analytics applications:

Subscribe to our newsletter

Get weekly insights on data, automation, and AI.

© 2026 Datons. All rights reserved.