Historical Spain OMIE Hourly Prices
You need historical hourly Spain OMIE prices you can program against, with correct units and a predictable schema. By the end of this guide you will query Spain OMIE Hourly (symbol OMIE_ES_DA) from the Energy API, parse the JSON, and build a clean timeseries suitable for analytics, dashboards, and backtesting.
What you will integrate (symbol, units, auth)
This post focuses exclusively on Spain OMIE Hourly:
- Symbol name: Spain OMIE Hourly
- Symbol code: OMIE_ES_DA
- Unit: EUR/MWh
- Authentication: api_key query parameter (value: YOUR_API_KEY)
You will use two endpoints that the Energy API exposes for this symbol:
- Hourly snapshot endpoint for a given date: GET https://energy-api.com/api/v1/electricity/hourly with query parameters symbol, date, api_key
- Timeseries endpoint for a historical range: GET https://energy-api.com/api/v1/timeseries with query parameters start, end, symbols, api_key
Billing quick facts: Starter $19.99/mo with a 7-day trial and 50 calls. Keep calls efficient by caching and batching where possible.
Quick start: single-day hourly fetch (official sample)
Use the hourly endpoint to retrieve Spain OMIE Day-Ahead hourly data for a given date. The following curl is the official sample—copy it exactly and substitute your key:
curl -G "https://energy-api.com/api/v1/electricity/hourly" --data-urlencode "symbol=OMIE_ES_DA" --data-urlencode "date=2026-06-18" --data-urlencode "api_key=YOUR_API_KEY"
Official JSON response example (use these exact values for testing parsers and field mappings):
{
"success": true,
"date": "2026-10-25",
"base": "EUR",
"rates": {
"OMIE_ES_DA": 111.8823
},
"dates": {
"OMIE_ES_DA": "2026-10-25"
},
"currencies": {
"OMIE_ES_DA": "EUR"
},
"base_filter_note": null
}
Notes:
- Base currency is EUR, matching the unit EUR/MWh for OMIE_ES_DA.
- The value for this symbol is exposed under rates.OMIE_ES_DA.
- Use the dates.OMIE_ES_DA field to confirm the market date associated with the returned value.
Field mapping cheat sheet (from the official response)
When building your parser, map fields deterministically so you can plug-and-play other symbols later without schema drift:
- success: boolean request status flag; handle false with retry/backoff logic.
- date: string date associated with the payload root; always validate against dates.OMIE_ES_DA.
- base: base currency for numeric values (EUR).
- rates.OMIE_ES_DA: numeric price.
- dates.OMIE_ES_DA: the date string for the specific symbol.
- currencies.OMIE_ES_DA: the currency code for the symbol.
- base_filter_note: optional note field; check for null before use.
Keep a copy of the official JSON around during implementation:
{
"success": true,
"date": "2026-10-25",
"base": "EUR",
"rates": {
"OMIE_ES_DA": 111.8823
},
"dates": {
"OMIE_ES_DA": "2026-10-25"
},
"currencies": {
"OMIE_ES_DA": "EUR"
},
"base_filter_note": null
}
Python example: fetch and read OMIE_ES_DA
The snippet below calls the hourly endpoint and reads the fields you’ll actually use. It also demonstrates how to assert on base currency and presence of the symbol.
import os
import sys
import json
import urllib.parse
import urllib.request
API_URL = "https://energy-api.com/api/v1/electricity/hourly"
API_KEY = os.environ.get("ENERGY_API_KEY", "YOUR_API_KEY")
def fetch_omie_hourly(date_str):
params = {
"symbol": "OMIE_ES_DA",
"date": date_str,
"api_key": API_KEY
}
url = API_URL + "?" + urllib.parse.urlencode(params)
with urllib.request.urlopen(url) as resp:
data = json.loads(resp.read().decode("utf-8"))
# Basic validations and access
if not data.get("success", False):
raise RuntimeError("Request not successful")
base = data.get("base")
if base != "EUR":
raise ValueError(f"Unexpected base currency: {base}")
rates = data.get("rates", {})
if "OMIE_ES_DA" not in rates:
raise KeyError("OMIE_ES_DA missing in rates")
value = rates["OMIE_ES_DA"] # EUR/MWh
symbol_date = data.get("dates", {}).get("OMIE_ES_DA")
currency = data.get("currencies", {}).get("OMIE_ES_DA")
return {
"date": symbol_date,
"symbol": "OMIE_ES_DA",
"unit": f"{currency}/MWh" if currency else "EUR/MWh",
"value": value
}
if __name__ == "__main__":
# Example: align dates to your collection schedule
date_arg = sys.argv[1] if len(sys.argv) > 1 else "2026-06-18"
record = fetch_omie_hourly(date_arg)
print(json.dumps(record, ensure_ascii=False))
Run with: python script.py 2026-06-18. The printed record is ready for storage in your timeseries database.
Node.js example: fetch and read OMIE_ES_DA
This JavaScript sample uses fetch and mirrors the same validations as the Python example.
import fetch from "node-fetch";
const API_URL = "https://energy-api.com/api/v1/electricity/hourly";
const API_KEY = process.env.ENERGY_API_KEY || "YOUR_API_KEY";
async function fetchOmieHourly(dateStr) {
const params = new URLSearchParams({
symbol: "OMIE_ES_DA",
date: dateStr,
api_key: API_KEY
});
const url = `${API_URL}?${params.toString()}`;
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const data = await res.json();
if (!data.success) throw new Error("API returned success=false");
if (data.base !== "EUR") throw new Error(`Unexpected base ${data.base}`);
const rate = data.rates?.OMIE_ES_DA;
const d = data.dates?.OMIE_ES_DA;
const c = data.currencies?.OMIE_ES_DA;
if (rate == null) throw new Error("OMIE_ES_DA missing in rates");
return { date: d, symbol: "OMIE_ES_DA", unit: `${c}/MWh`, value: rate };
}
fetchOmieHourly("2026-06-18")
.then(console.log)
.catch(err => {
console.error(err);
process.exit(1);
});
Range retrieval: build a historical series (timeseries endpoint)
When you need more than a single market date, call the timeseries endpoint. This is appropriate for backfills and analytics where you need contiguous daily or hourly coverage.
- Endpoint: GET https://energy-api.com/api/v1/timeseries
- Required query parameters: start, end, symbols, api_key
- Symbol: OMIE_ES_DA
Example request structure (no response body shown here):
curl -G "https://energy-api.com/api/v1/timeseries" \
--data-urlencode "start=2026-06-01" \
--data-urlencode "end=2026-06-30" \
--data-urlencode "symbols=OMIE_ES_DA" \
--data-urlencode "api_key=YOUR_API_KEY"
Parsing guidance:
- Read points[].value for the numeric price values as they appear in the series payload.
- Persist the timestamp/index from each point so you retain hourly alignment.
- Keep the unit as EUR/MWh for every point associated with OMIE_ES_DA.
Because the series schema is purpose-built for ranges, it is ideal for batch backfills. Use the hourly endpoint when you want a specific date and a thinner payload surface.
Another official JSON example for validator tests
If you are writing contract tests or integration validators, use the same official sample to assert schema and fields. Do not substitute other values for these tests:
{
"success": true,
"date": "2026-10-25",
"base": "EUR",
"rates": {
"OMIE_ES_DA": 111.8823
},
"dates": {
"OMIE_ES_DA": "2026-10-25"
},
"currencies": {
"OMIE_ES_DA": "EUR"
},
"base_filter_note": null
}
Recommended assertions:
- success is true.
- base is EUR.
- rates.OMIE_ES_DA is a finite number.
- dates.OMIE_ES_DA is a valid date string.
- currencies.OMIE_ES_DA equals EUR.
Data handling patterns that save time
Adopt these patterns early to avoid rework as your usage grows:
- Timezone awareness: hourly electricity markets are aligned to their market’s local time. Normalize all ingest into a canonical application timezone, but retain the original index to preserve trading alignment. Confirm timezone conventions in the Documentation before productionizing.
- Daylight saving transitions: hourly markets can contain 23 or 25 hours on transition days. Your DB schema and aggregations must not assume 24 fixed buckets.
- Null or missing hours: guard for missing observations; forward-fill only when your analytics explicitly allow it, and always tag imputed values.
- Caching: cache responses by (endpoint, symbol, date) to reduce duplicate calls, especially within the 7-day trial (50-call) window.
- Idempotent backfills: write ingestion jobs so they can re-run safely for the same date ranges without duplicating rows.
- Schema stability: code to field names shown here (success, base, rates, dates, currencies, base_filter_note) to reduce breakage risk.
From single-day fetch to a normalized timeseries
1) Collect
Use the hourly endpoint for specific dates and the timeseries endpoint for ranges. Keep requests organized by symbol and date for traceability.
2) Validate
Validate base=EUR, unit=EUR/MWh, and presence of OMIE_ES_DA before inserting rows. Log any discrepancy for audit.
3) Normalize
Map each observation to a canonical schema: timestamp (market local or canonical), symbol, value, unit, source. Avoid dynamic per-ingest schemas.
4) Store
Store in a columnar or time-indexed store. Ensure the timestamp type can represent timezone offsets and DST gaps/overlaps cleanly.
5) Aggregate
When producing daily averages or peak/off-peak splits, use the correct hour counts for DST days and maintain EUR/MWh units throughout.
Robustness: retries, monitoring, and fallbacks
Implement exponential backoff on transient HTTP errors and include structured logs with request IDs where possible. Alert on schema drift or missing fields, not just HTTP status codes.
- Timeouts: set client timeouts to prevent hung processes.
- Retries: retry idempotent GETs with jitter; bound the total time.
- Validation alarms: trigger alerts when base is not EUR for OMIE_ES_DA, or when rates.OMIE_ES_DA is missing.
Compliance notes for multi-commodity stacks
If your project also touches other commodities, keep this in mind:
- Do not pass base=USD when you also need TTF_GAS or EUA_CO2. Keep base handling consistent to avoid cross-symbol confusion.
- Enforce symbol-level units. Do not coerce EUR/MWh to other units without an explicit, versioned conversion step.
Frequently asked questions
Q1: How do I authenticate?
Pass your key using the api_key query parameter. Example: api_key=YOUR_API_KEY
Q2: What is the unit for OMIE_ES_DA?
EUR/MWh. The base field in the response is EUR, and currencies.OMIE_ES_DA is EUR.
Q3: How do I get a historical range rather than one day?
Use GET /api/v1/timeseries with start, end, symbols=OMIE_ES_DA, and api_key. Read values from points[].value.
Q4: How should I handle daylight saving time for hourly data?
Treat market-local transitions carefully; do not assume 24 fixed hours. Confirm conventions in the Documentation and preserve original hourly indices.
Q5: Can I validate my parser without live calls?
Yes. Use the official sample JSON below to test schema and field mapping:
{
"success": true,
"date": "2026-10-25",
"base": "EUR",
"rates": {
"OMIE_ES_DA": 111.8823
},
"dates": {
"OMIE_ES_DA": "2026-10-25"
},
"currencies": {
"OMIE_ES_DA": "EUR"
},
"base_filter_note": null
}
Next steps
Create your key and run the curl above against OMIE_ES_DA. Once you have a single-day fetch working, move to the timeseries endpoint to backfill the range you need. If you have questions on fields or query parameters, check the Documentation. To get started now, Register for the 7-day trial (50 calls) and begin ingesting OMIE_ES_DA in minutes.
Ready to get started?
Get your API key and start querying energy commodity prices in minutes.
Get API Key