Building a Geo-Fenced Distributed Energy Resource Orchestrator with Energy API and MQTT for Low-Latency Control
Distributed Energy Resources (DERs) like battery storage, EV chargers, and flexible industrial loads are only as useful as the signals they respond to. You need low-latency control loops, but you also need control logic that is economically rational: dispatch or curtail when it’s profitable or when emissions are high, and stand down when the grid is clean or prices spike in the wrong direction. The missing link is a reliable stream of normalized energy market data—day-ahead curves, intraday electricity prices, grid carbon intensity, and related commodities like gas and carbon allowances—delivered in a single, developer-friendly interface.
In this post, we’ll build a geo-fenced DER orchestrator that ingests market data from Energy API, applies location-aware rules, and publishes device control signals over MQTT for sub-second fan-out to edge controllers. We’ll design the system to be maintainable in production: normalized JSON formats, deterministic day-ahead lookups, robust retries, health checks, and clear observability. You’ll get runnable cURL, Python, and JavaScript examples, and a blueprint for integrating wholesale markets, grid carbon intensity, and retail references (like PVPC in Spain) into a coherent control plane.
Why this approach? Without a normalized data source, developers spend weeks wrangling OMIE, ENTSO-E, EIA/FRED, ESIOS, and other portals—each with incompatible formats, schedules, naming conventions, and edge cases. Stitching them together in-house delays features and increases operational risk. Energy API aggregates official market data across electricity, gas, oil, coal, carbon, and carbon intensity into a single REST surface with a consistent schema. We’ll leverage that to focus on logic, not ETL.
Introduction: From Static Tariffs to Geo-Fenced, Signal-Aware Control
Most DER control stacks still assume static tariffs or a single hourly curve. That breaks down in real operations because the economics change throughout the day, and across regions. For example:
- An EV fleet in Madrid should charge when OMIE day-ahead prices are low and grid carbon intensity in Spain trends downward, but the same logic in Berlin needs to respect EPEX curves and German carbon intensity.
- A battery in NSW, Australia may follow AEMO intraday or day-ahead-like publication times (region-specific) and reverse power flow exports during negative price windows.
- Industrial loads seeking to reduce Scope 2 emissions require real-time or near-real-time carbon intensity overlays to shift consumption into cleaner windows.
Hardcoding these rules across different countries and commodities is brittle. You need:
- A normalized data backbone that speaks one JSON dialect regardless of source or commodity, so your rules engine stays simple.
- Multi-commodity awareness—gas and carbon can be hedging indicators for electricity exposure, and oil/coal can contextualize macro risk.
- Deterministic day-ahead lookups and intraday curves where available.
- A pub/sub fabric like MQTT so control commands fan out to devices in milliseconds.
We’ll show how to assemble these pieces using Energy API for data and MQTT for device messaging, with a clean separation between policy (pricing/carbon decisions) and mechanics (protocol, topics, QoS).
Why Energy API
Developers don’t want to maintain scrapers, parse CSVs from dozens of national portals, or juggle variable timezones and holiday calendars. Energy API solves this by aggregating official sources—OMIE, ENTSO-E, ESIOS, EIA/FRED, Ember—and presenting a unified, consistent REST surface. Here are a few differentiators that matter in production:
- One normalized REST interface across six categories (electricity, gas, oil, coal, carbon, and carbon intensity) with 39+ symbols and 16 endpoints. This eliminates glue code and lets you ship features in hours instead of weeks. Your code can request OMIE_ES_DA and CARBON_INT_ES with the same shape and consistent field names.
- Same JSON schema across commodities and endpoints like /latest, /historical, and /timeseries. You can plot BRENT_CRUDE alongside TTF_GAS or EUA_CO2 without bespoke ETL, enabling unified risk and strategy logic.
- Intraday electricity curves where sources publish them and deterministic day-ahead forecast lookups for auction-based markets. Your orchestrator can pre-schedule DER behavior for tomorrow the minute official results publish—no scraping or polling odd formats.
- Operational clarity: an explicit provider status endpoint helps you monitor data freshness and pipeline health, and a common error response format enables robust retries and alerting rules.
If you’re coordinating devices in multiple geographies or building portfolio-wide analytics, this uniformity is the difference between a fragile prototype and a reliable control plane.
Quick Start
Base URL: https://energy-api.com/api/v1
Requests use a query parameter for authentication. We’ll include it directly in examples for simplicity. Below is a first request to fetch the most recent values for oil, gas, and carbon in one call—demonstrating multi-commodity queries with a single endpoint.
curl -G https://energy-api.com/api/v1/latest \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS,EUA_CO2" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample JSON response:
{
"success": true,
"date": "2026-06-11",
"base": "MIXED",
"rates": {
"BRENT_CRUDE": 74.82,
"TTF_GAS": 38.15,
"EUA_CO2": 67.40
},
"dates": {
"BRENT_CRUDE": "2026-06-11",
"TTF_GAS": "2026-06-11",
"EUA_CO2": "2026-06-11"
},
"currencies": {
"BRENT_CRUDE": "USD",
"TTF_GAS": "EUR",
"EUA_CO2": "EUR"
}
}
Field highlights:
- success: Indicates the call succeeded. Standard across endpoints.
- date: The reference date for the rates object. Helpful for caching and time alignment.
- base: Indicates the currency base context; “MIXED” appears when requesting multiple commodities with different currency codes.
- rates: Keyed by symbol, providing the latest price/value. Symbols span electricity, gas, oil, coal, carbon, and carbon intensity.
- dates: Publication date per symbol, useful if some symbols publish at different times during the day.
- currencies: Currency code per symbol (USD, EUR, etc.).
This single call paradigm simplifies cross-commodity orchestration. For instance, you can condition a charging policy on EUA_CO2 movements while watching TTF_GAS to infer power price sensitivity in certain EU markets.
Core Endpoints for a Geo-Fenced DER Orchestrator
We’ll focus on endpoints that provide time-critical inputs to your control loop and portfolio planner. Each example includes the path, key params, a cURL, a complete JSON example, and how to use the fields in practice.
1) /electricity/hourly — Intraday or Day-Ahead Curves for Dispatch Windows
Use this to retrieve the full price curve (15-minute or hourly depending on the source) for a specific market symbol and date. For example, OMIE_ES_DA (Spain day-ahead) can drive EV charging windows or battery charge/discharge strategies in Spain.
Endpoint: GET /electricity/hourly
Key params:
- symbol (required): e.g., OMIE_ES_DA, EPEX_DE_DA, AEMO_NSW1
- date (required): YYYY-MM-DD (publication date of the curve)
curl -G https://energy-api.com/api/v1/electricity/hourly \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "date=2026-06-12" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample JSON response (illustrative):
{
"success": true,
"symbol": "OMIE_ES_DA",
"date": "2026-06-12",
"currency": "EUR",
"frequency": "hourly",
"curve": [
{"time": "2026-06-12T00:00:00+02:00", "price": 52.10},
{"time": "2026-06-12T01:00:00+02:00", "price": 49.80},
{"time": "2026-06-12T02:00:00+02:00", "price": 46.75},
{"time": "2026-06-12T03:00:00+02:00", "price": 45.20},
{"time": "2026-06-12T04:00:00+02:00", "price": 47.10},
{"time": "2026-06-12T05:00:00+02:00", "price": 51.00},
{"time": "2026-06-12T06:00:00+02:00", "price": 60.25},
{"time": "2026-06-12T07:00:00+02:00", "price": 68.40},
{"time": "2026-06-12T08:00:00+02:00", "price": 72.85},
{"time": "2026-06-12T09:00:00+02:00", "price": 70.10},
{"time": "2026-06-12T10:00:00+02:00", "price": 66.30},
{"time": "2026-06-12T11:00:00+02:00", "price": 63.20},
{"time": "2026-06-12T12:00:00+02:00", "price": 61.00},
{"time": "2026-06-12T13:00:00+02:00", "price": 59.40},
{"time": "2026-06-12T14:00:00+02:00", "price": 58.00},
{"time": "2026-06-12T15:00:00+02:00", "price": 57.25},
{"time": "2026-06-12T16:00:00+02:00", "price": 58.90},
{"time": "2026-06-12T17:00:00+02:00", "price": 64.10},
{"time": "2026-06-12T18:00:00+02:00", "price": 70.50},
{"time": "2026-06-12T19:00:00+02:00", "price": 74.00},
{"time": "2026-06-12T20:00:00+02:00", "price": 71.60},
{"time": "2026-06-12T21:00:00+02:00", "price": 66.75},
{"time": "2026-06-12T22:00:00+02:00", "price": 60.20},
{"time": "2026-06-12T23:00:00+02:00", "price": 55.90}
]
}
How to use it:
- curve: Array of {time, price}, aligned to the market’s timezone. For a geo-fenced orchestrator, you’ll map devices by ISO-2 country or market area to the correct symbol and curve.
- frequency: Indicates hourly or 15-minute cadence. Use this to compute slot-level dispatch commands.
- currency: Important for cost accounting and ROI analysis across mixed portfolios.
2) /forecast — Next Published Day-Ahead for Deterministic Planning
When your strategy depends on tomorrow’s day-ahead prices, you need a deterministic lookup the moment official results publish—no scraping or guessing. Use /forecast for auction-sourced electricity symbols (returns 404 for non-auction symbols).
Endpoint: GET /forecast
Key params:
- symbol (required): e.g., OMIE_ES_DA, EPEX_DE_DA
curl -G https://energy-api.com/api/v1/forecast \
--data-urlencode "symbol=EPEX_DE_DA" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample JSON response (illustrative):
{
"success": true,
"symbol": "EPEX_DE_DA",
"forecast_date": "2026-06-13",
"currency": "EUR",
"frequency": "hourly",
"curve": [
{"time": "2026-06-13T00:00:00+02:00", "price": 48.10},
{"time": "2026-06-13T01:00:00+02:00", "price": 46.50},
{"time": "2026-06-13T02:00:00+02:00", "price": 45.30}
/* ... more hours omitted for brevity ... */
],
"published_at": "2026-06-12T12:45:00+02:00"
}
How to use it:
- forecast_date: The operational day you’ll schedule against. Generate setpoints for the entire day and load them into your scheduler as soon as published_at is reached.
- published_at: Timestamp of official publication. Helpful for audit trails and for idempotent scheduling: only schedule if the receipt is newer than a previous cached version.
- 404 behavior: If you query a non-auction symbol, you’ll get a 404 with a standard error payload. Handle gracefully and fall back to /electricity/hourly for the current day, or keep polling until results publish.
3) /carbon-intensity — Emissions-Aware Dispatch Windows
Price is only half the story; many organizations prioritize emissions reductions or carbon cost internalization. Use /carbon-intensity to align dispatch with lower-emissions hours. This is particularly useful for load shifting or for choosing time windows to run energy-intensive jobs (e.g., cold storage defrost, data center batch workloads).
Endpoint: GET /carbon-intensity
Key params:
- country (ISO-2, required): e.g., DE, ES, EU for aggregated view
- base (optional): Filter currency if applicable
curl -G https://energy-api.com/api/v1/carbon-intensity \
--data-urlencode "country=DE" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample JSON response (illustrative):
{
"success": true,
"country": "DE",
"unit": "gCO2eq/kWh",
"date": "2026-06-11",
"intensity": 312,
"metadata": {
"symbol": "CARBON_INT_DE",
"source": "Official grid operator and data partners",
"last_updated": "2026-06-11T10:05:00+02:00"
}
}
How to use it:
- intensity: Point value in gCO2eq/kWh you can use as a threshold (e.g., only charge when intensity < 250).
- metadata.last_updated: Helps order updates and resolve race conditions across polling intervals.
- Combine with /electricity/hourly: For example, choose the lowest-price hours that also meet an emissions threshold.
4) /latest — Real-Time Portfolio Snapshot Across Commodities
For fleets spanning many markets and for hedging logic, you often need a cross-commodity picture in one request. /latest lets you fetch electricity references, gas, oil, coal, and carbon simultaneously with the same field structure.
Endpoint: GET /latest
Key params:
- symbols (required, comma-separated): e.g., OMIE_ES_DA,EUA_CO2,TTF_GAS,BRENT_CRUDE
- base (optional), category (optional)
curl -G https://energy-api.com/api/v1/latest \
--data-urlencode "symbols=OMIE_ES_DA,TTF_GAS,EUA_CO2,BRENT_CRUDE" \
--data-urlencode "api_key=YOUR_API_KEY"
Illustrative JSON response:
{
"success": true,
"date": "2026-06-11",
"base": "MIXED",
"rates": {
"OMIE_ES_DA": 62.25,
"TTF_GAS": 38.15,
"EUA_CO2": 67.40,
"BRENT_CRUDE": 74.82
},
"dates": {
"OMIE_ES_DA": "2026-06-11",
"TTF_GAS": "2026-06-11",
"EUA_CO2": "2026-06-11",
"BRENT_CRUDE": "2026-06-11"
},
"currencies": {
"OMIE_ES_DA": "EUR",
"TTF_GAS": "EUR",
"EUA_CO2": "EUR",
"BRENT_CRUDE": "USD"
}
}
How to use it:
- rates: Build dashboards and alerts for trading, risk, or operational decisions in one call. If EUA_CO2 spikes, adjust charging windows; if TTF_GAS trends up, anticipate higher marginal power costs.
- dates and currencies: Ensure correct time alignment and currency normalization in your calculations.
5) /timeseries — Strategy Backtesting and Forecast Evaluation
You need to backtest dispatch rules across months or years. /timeseries returns a date-keyed series in a normalized structure across commodities—ideal for charts, trend modeling, and P&L calculations.
Endpoint: GET /timeseries
Key params:
- start (required), end (required): YYYY-MM-DD
- symbols (required, comma-separated)
- base (optional)
curl -G https://energy-api.com/api/v1/timeseries \
--data-urlencode "start=2025-01-01" \
--data-urlencode "end=2025-03-31" \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS" \
--data-urlencode "api_key=YOUR_API_KEY"
Complete JSON response example from the reference, with brief field annotations:
{
"success": true,
"base": "MIXED",
"start_date": "2025-01-01",
"end_date": "2025-03-31",
"rates": {
"BRENT_CRUDE": {
"2025-01-02": 76.30,
"2025-01-03": 75.90
},
"TTF_GAS": {
"2025-01-02": 46.80,
"2025-01-03": 47.10
}
},
"frequencies": {
"BRENT_CRUDE": "daily",
"TTF_GAS": "daily"
},
"currencies": {
"BRENT_CRUDE": "USD",
"TTF_GAS": "EUR"
}
}
How to use it:
- rates: Each symbol contains a map of date to value—perfect for generating charts and computing moving averages.
- frequencies: Useful when mixing daily, hourly, or intraday cadences across symbols—helps resampling logic.
- currencies: Apply FX normalization if needed.
6) /status — Operational Health and Circuit Breakers
Production DER systems need proactive monitoring. Use /status to observe last fetch health per provider and decide whether to trust or temporarily bypass certain signals. Combine with circuit breaker logic in your orchestrator to prevent stale data from causing bad dispatches.
Endpoint: GET /status
curl -G https://energy-api.com/api/v1/status \
--data-urlencode "api_key=YOUR_API_KEY"
Illustrative JSON response:
{
"success": true,
"providers": [
{"name": "OMIE", "last_fetch": "2026-06-11T10:10:00Z", "status": "ok"},
{"name": "ENTSO-E", "last_fetch": "2026-06-11T10:08:00Z", "status": "ok"},
{"name": "EIA", "last_fetch": "2026-06-10T21:00:00Z", "status": "ok"},
{"name": "FRED", "last_fetch": "2026-06-11T09:55:00Z", "status": "ok"},
{"name": "ESIOS", "last_fetch": "2026-06-11T10:06:00Z", "status": "ok"}
]
}
How to use it:
- providers[].status: If any provider is degraded, switch to cached values, widen control deadbands, or reduce aggressive arbitrage to mitigate risk.
- providers[].last_fetch: Create alerts if staleness exceeds thresholds for critical markets.
Designing the Geo-Fenced DER Orchestrator
A geo-fenced orchestrator routes policy decisions to devices based on their latitude/longitude, ISO-2 country, or market area. It aligns symbols and data feeds per device cluster, then publishes MQTT commands with low-latency fan-out. Here’s a reference architecture:
- Device registry: For each device or site, store geolocation, country, market symbol (e.g., OMIE_ES_DA), emissions country (e.g., ES), and operational constraints (min SOC, max power, ramp limits).
- Data layer: Use /electricity/hourly for next-day or same-day curve, /forecast when available, /carbon-intensity for emissions gating, and /latest for supporting risk signals like EUA_CO2 and TTF_GAS.
- Policy engine: Define rules such as “charge EVs during the 4 lowest-price hours of the next 24h if carbon intensity < 300 gCO2eq/kWh; otherwise limit to essential SOC.”
- MQTT fabric: Topic hierarchy like energy/{country}/{site_id}/{device_id}/cmd with retained state in energy/{…}/state. Use QoS 1 for delivery guarantees and keep messages compact.
- Reliability: Poll /status for pipeline health, implement exponential backoff for transient 429s, and maintain a local fallback schedule if data is temporarily stale.
You should separate concerns:
- Data fetchers run on a schedule aligned to publication windows and push normalized payloads into a cache/bus.
- Schedulers convert price/emissions into target setpoints for future windows (e.g., next 24 hours).
- Real-time controllers subscribe over MQTT, apply device-specific constraints, and execute.
Implementation: From Data to MQTT Topics
Python: Fetch Curves, Apply Rules, Publish Commands
Below is a Python sketch using requests and paho-mqtt. It fetches OMIE_ES_DA day-ahead, overlays carbon intensity, selects windows, and publishes control commands for a Spanish EV charger cluster.
import time
import json
import requests
import datetime as dt
from paho.mqtt import client as mqtt
API_BASE = "https://energy-api.com/api/v1"
API_KEY = "YOUR_API_KEY"
MQTT_BROKER = "mqtt.example.com"
MQTT_PORT = 8883
MQTT_USERNAME = "orchestrator"
MQTT_PASSWORD = "********"
TOPIC_CMD = "energy/ES/site-123/ev-fleet/cmd"
def get_hourly_curve(symbol, date_str):
r = requests.get(
f"{API_BASE}/electricity/hourly",
params={"symbol": symbol, "date": date_str, "api_key": API_KEY},
timeout=15
)
data = r.json()
if not data.get("success"):
raise RuntimeError(data.get("error", "unknown error"))
return data
def get_carbon_intensity(country):
r = requests.get(
f"{API_BASE}/carbon-intensity",
params={"country": country, "api_key": API_KEY},
timeout=10
)
data = r.json()
if not data.get("success"):
raise RuntimeError(data.get("error", "unknown error"))
return data
def select_low_price_clean_hours(curve, intensity_threshold=300, top_n=4):
# curve: list of {"time": iso, "price": float}
# For this example, assume point intensity gating using current value
prices = [(c["time"], c["price"]) for c in curve]
# Sort by price ascending
prices_sorted = sorted(prices, key=lambda x: x[1])
return [t for t, _ in prices_sorted[:top_n]]
def mqtt_connect():
client = mqtt.Client(client_id="es-der-orchestrator", clean_session=True)
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
client.tls_set() # configure CA as needed
client.connect(MQTT_BROKER, MQTT_PORT, keepalive=30)
return client
def build_commands(selected_hours, charge_power_kw=11.0):
# Create a concise schedule payload for edge devices
cmds = []
for ts in selected_hours:
cmds.append({"start": ts, "duration_min": 60, "power_kw": charge_power_kw, "mode": "charge"})
return {"schedule": cmds, "version": int(time.time())}
def main():
today = dt.date.today()
tomorrow = (today + dt.timedelta(days=1)).isoformat()
# Fetch prices and emissions
hourly = get_hourly_curve("OMIE_ES_DA", tomorrow)
carbon = get_carbon_intensity("ES")
carbon_value = carbon.get("intensity", 0)
if carbon_value > 0 and carbon_value > 300:
# If too dirty, reduce aggressiveness by picking fewer hours or lower power
top_hours = select_low_price_clean_hours(hourly["curve"], intensity_threshold=300, top_n=2)
payload = build_commands(top_hours, charge_power_kw=7.0)
else:
top_hours = select_low_price_clean_hours(hourly["curve"], intensity_threshold=300, top_n=4)
payload = build_commands(top_hours, charge_power_kw=11.0)
client = mqtt_connect()
client.publish(TOPIC_CMD, json.dumps(payload), qos=1, retain=False)
client.disconnect()
print("Published schedule:", payload)
if __name__ == "__main__":
main()
Notes:
- The example uses a simple emissions threshold gate; you can extend it to blend price and carbon via weighted scores per time slot.
- Always validate “success” and handle “error” fields from the API. For 429 responses, implement exponential backoff. For 404 on /forecast, schedule a retry aligned to expected publication windows.
- Use retained MQTT messages only when you want late subscribers to receive the last known schedule; otherwise, keep them transient.
JavaScript (Node.js): Cross-Commodity Snapshot and On-the-Fly Adjustments
Next, a Node.js snippet that fetches /latest for multiple commodities and adjusts device limits when EUA_CO2 or TTF_GAS crosses thresholds, then publishes an immediate curtailment command.
const https = require("https");
const mqtt = require("mqtt");
const API_BASE = "https://energy-api.com/api/v1";
const API_KEY = "YOUR_API_KEY";
const symbols = ["OMIE_ES_DA","TTF_GAS","EUA_CO2","BRENT_CRUDE"].join(",");
function getLatest() {
const url = new URL(`${API_BASE}/latest`);
url.searchParams.set("symbols", symbols);
url.searchParams.set("api_key", API_KEY);
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = "";
res.on("data", (chunk) => data += chunk);
res.on("end", () => {
try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
});
}).on("error", reject);
});
}
async function run() {
const snapshot = await getLatest();
if (!snapshot.success) {
throw new Error(snapshot.error || "Unknown error");
}
const gas = snapshot.rates["TTF_GAS"];
const co2 = snapshot.rates["EUA_CO2"];
let mode = "normal";
let limitKw = 50;
if (co2 > 80 || gas > 60) {
mode = "curtail";
limitKw = 20;
}
const mqttClient = mqtt.connect("mqtts://mqtt.example.com:8883", {
username: "orchestrator",
password: "********",
clean: true,
reconnectPeriod: 2000
});
mqttClient.on("connect", () => {
const cmd = { ts: Date.now(), mode, limit_kw: limitKw };
mqttClient.publish("energy/ES/site-123/plant/cmd", JSON.stringify(cmd), { qos: 1 }, () => {
console.log("Published curtailment:", cmd);
mqttClient.end();
});
});
}
run().catch(err => {
console.error(err);
process.exit(1);
});
This is valuable for portfolio-wide levers—quickly tightening power caps or switching operating modes across many devices in response to a cross-commodity shock while awaiting updated day-ahead schedules.
Error Handling, Reliability, and Observability
Even the best pipelines encounter edge cases. Energy API standardizes error shapes, enabling robust orchestration:
- 401: Missing or invalid api_key. The response shape: {"success": false, "error": "..."}.
- 404: No data for given symbols or date (including non-auction symbols for /forecast). Fall back to cached data or alternative endpoints.
- 422: Validation errors—missing required params (e.g., symbol or date), wrong formats.
- 429: Rate limit exceeded—implement exponential backoff and jitter. Batch symbols where possible to reduce call volume.
Best practices:
- Use /status for quick health checks. If a provider looks stale, widen your control deadband or freeze day-ahead schedules until fresh data arrives.
- Cache last-good responses and couple them with an expiration policy. For day-ahead, once you have the official curve, store it for 24 hours with a clear version marker.
- Idempotency: Stamp schedules and commands with a monotonic version and the forecast_date or curve date. Edge controllers can reject older schedules.
- Circuit breakers: If you see non-transient 5xx or consistent 404s during expected publication windows, move the system into a safe “holding” mode with conservative setpoints.
End-to-End Example: Spain vs. Germany Geo-Fencing
Suppose your fleet has two clusters:
- Cluster ES (Madrid): Uses OMIE_ES_DA and CARBON_INT_ES.
- Cluster DE (Berlin): Uses EPEX_DE_DA and CARBON_INT_DE.
A single scheduler process can:
- Call /forecast for both symbols as soon as results publish.
- If /forecast returns 404 for either (not yet published), temporarily use /electricity/hourly for same-day or last-known day-ahead for tomorrow, with a retry policy.
- Overlay carbon intensity for the relevant country with a dynamic threshold based on ESG targets.
- Choose 4-6 of the lowest-cost hours that also meet emissions criteria; produce per-cluster schedules.
- Publish to distinct MQTT topics: energy/ES/site-123/ev-fleet/cmd and energy/DE/site-456/ev-fleet/cmd with schedules tagged by region and forecast_date.
This keeps your logic consistent while respecting regional differences in data cadence and emissions profiles.
Symbols and Discovery
As you scale to more markets and commodities, dynamic discovery prevents hardcoding. Use /symbols to enumerate active symbols by category or provider metadata. For example, to list gas symbols:
curl -G https://energy-api.com/api/v1/symbols \
--data-urlencode "category=gas" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample JSON:
{
"success": true,
"count": 3,
"symbols": [
{
"symbol": "TTF_GAS",
"name": "TTF Natural Gas Day-Ahead",
"category": "gas",
"country_code": "EU",
"currency_code": "EUR",
"frequency": "daily",
"description": "TTF day-ahead price published by EEX."
}
]
}
Use the resulting metadata to map country_code to your geo-fencing registry and to annotate dashboards with friendly names and units.
Real-World Use Cases
Here are three production-grade applications you can build on top of the same normalized API.
1) Price- and Emissions-Aware EV Fleet Charging
Aim: Minimize cost and emissions while meeting required SOC by morning. Use /forecast or /electricity/hourly for the applicable symbol (e.g., OMIE_ES_DA for Spain), and /carbon-intensity for the same country. Pick the cheapest 4-6 hours under an emissions threshold, then publish the schedule to energy/{country}/{site}/ev-fleet/cmd over MQTT. Monitor /status; if staleness detected, reduce aggressiveness and revert to last-good schedule until fresh data appears.
2) ESG Dashboard for Portfolio Operations
Aim: Provide operations and compliance teams with a unified view of price drivers and emissions. Use /latest to pull EUA_CO2, electricity references, and gas in one call. Overlay /timeseries for trend analysis and volatility. Display intensity from /carbon-intensity alongside P&L markers to quantify trade-offs between cost and carbon. This dashboard informs policies that the orchestrator enforces over MQTT.
3) Industrial Load Shaping with P&L and Carbon Guardrails
Aim: Shift non-critical industrial loads (HVAC pre-cooling, process batching) into low-price, low-emissions windows while capping downside on volatility. Use /timeseries for backtesting, /electricity/hourly for execution curves, and /fluctuation for change and volatility checks over near-term windows. Combine with an MQTT-driven rules engine that can push immediate curtailment or rescheduling commands when short-term signals deteriorate.
FAQ
How often does the TTF gas price update?
TTF_GAS is standardized within the same JSON surface as other commodities. Use /latest for the most recent value and /timeseries to analyze historical updates. Check the dates field in /latest and the frequencies map in /timeseries to understand cadence; pair with /status to verify provider freshness.
Can I retrieve historical energy prices going back multiple years?
Yes. Use /timeseries with start and end parameters to retrieve historical series for supported symbols. The response includes frequencies and currencies so you can correctly resample and normalize across commodities during backtesting and analytics.
Does the API support multiple commodities in a single call?
Yes. This is a major advantage of /latest, /historical, and /timeseries. You can ask for OMIE_ES_DA, TTF_GAS, EUA_CO2, and BRENT_CRUDE together and receive a consistent JSON response, dramatically simplifying cross-commodity logic and visualization.
How do I handle data availability across holidays and weekends?
Use /historical to retrieve values for a specific date; if the date falls on a non-publishing day, the API returns the most recent value before it. Always check the dates field in responses and implement sensible fallbacks in your scheduler for days without new publications.
What’s the best way to prevent stale data from driving bad control decisions?
Poll /status to verify provider freshness, use the dates and last_updated metadata in responses, and maintain a cache of last-good curves with expiration. Add circuit breakers to pause aggressive strategies when sources are stale or intermittent, and keep conservative fallback schedules on the edge.
Putting It All Together: A DER Orchestrator Control Loop
Let’s outline a daily and intraday loop that scales across countries:
- Discovery: Use /symbols to build a mapping of symbols to geographies and device clusters.
- Day-ahead planning: When auction results publish, call /forecast for each relevant symbol. If not available, fall back to /electricity/hourly for same-day or retry until publication time. Cache with a version tag and forecast_date.
- Emissions overlay: Pull /carbon-intensity per country to set thresholds. Optionally, compute a blended score: score = alpha * normalized_price + beta * normalized_intensity.
- Cross-commodity risk: Fetch /latest for gas and carbon allowances; adjust how aggressively you select low-price windows to reflect macro signals (e.g., tightening carbon markets).
- Scheduling: Convert selected hours into device-specific schedules (power limits, durations) and publish to MQTT topics. Include a version and validity window for device-side verification.
- Intraday adaptation: If new information arrives (e.g., volatility from /fluctuation or updated intensity), push incremental commands with updated power caps or ad-hoc curtailment.
- Observability and safety: Poll /status and watch error responses. If 429 appears, back off and coalesce symbol requests. If any critical provider is stale, hold schedules steady and notify operators.
Additional Endpoint Highlights for Operations
/fluctuation — Change and Percent Change Over a Period
For risk-sensitive operations, monitor absolute and percentage changes to decide whether to tighten or relax constraints. For example, apply more conservative EV charging when TTF_GAS change_pct > 5% over the last week.
curl -G https://energy-api.com/api/v1/fluctuation \
--data-urlencode "start=2026-06-01" \
--data-urlencode "end=2026-06-11" \
--data-urlencode "symbols=TTF_GAS,EUA_CO2" \
--data-urlencode "api_key=YOUR_API_KEY"
Illustrative JSON:
{
"success": true,
"base": "MIXED",
"period": {"start": "2026-06-01", "end": "2026-06-11"},
"symbols": {
"TTF_GAS": {
"start_value": 35.00,
"end_value": 38.15,
"change": 3.15,
"change_pct": 9.00
},
"EUA_CO2": {
"start_value": 65.10,
"end_value": 67.40,
"change": 2.30,
"change_pct": 3.53
}
}
}
Use change_pct to trigger guardrails or alerts and to inform next-day schedule aggressiveness.
/electricity/latest — Fast Portfolio-Wide Electricity Snapshot
If you manage multiple power markets, this endpoint aggregates only electricity symbols and can be filtered by country. Use it for dashboards and quick periodic checks.
curl -G https://energy-api.com/api/v1/electricity/latest \
--data-urlencode "country=ES" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample JSON (illustrative):
{
"success": true,
"category": "electricity",
"country": "ES",
"date": "2026-06-11",
"rates": {
"OMIE_ES_DA": 62.25,
"PVPC_ES_2TD": 0.165
},
"currencies": {
"OMIE_ES_DA": "EUR",
"PVPC_ES_2TD": "EUR"
}
}
If you build consumer-facing tools or need retail reference overlays (e.g., PVPC), this provides a fast snapshot while you use more detailed curves for operations.
Field-by-Field: Interpreting Responses in Control Logic
Across endpoints, a few fields are especially important for reliability and correctness:
- success: Always gate logic on success before reading values.
- date / forecast_date: Aligns your control windows; include in MQTT payloads for traceability.
- dates map in /latest: Per-symbol freshness; when mixed, ensure no symbol silently lags behind.
- currencies: When combining symbols, normalize currency if you’re computing blended financial metrics.
- frequency and curve times: Ensures you respect slot boundaries and local timezones for accurate device scheduling.
- metadata.last_updated (where present): Critical for debouncing and ensuring you do not override a newer schedule with stale data.
Developer Tips: Performance and Best Practices
- Batch requests: Use the comma-separated symbols param in /latest and /timeseries to reduce round trips and stay consistent across related values.
- Regional fetchers: Run region-specific workers close to your MQTT brokers to minimize end-to-end latency from data acquisition to device command.
- Retries/backoff: For transient network issues or 429, use exponential backoff with jitter and log error response bodies (they’re human-readable).
- Streaming and fan-out: Use MQTT retain selectively; for live setpoints that change frequently, avoid retain to prevent outdated commands from applying after reconnects.
- Auditability: Include forecast_date, version, and a hash of the selected hourly slots in your MQTT command for downstream verification.
Sample End-to-End cURL + JSON Flow
Let’s simulate a daily cycle for Spain:
- Fetch tomorrow’s day-ahead curve:
curl -G https://energy-api.com/api/v1/forecast \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "api_key=YOUR_API_KEY"
- Get emissions snapshot:
curl -G https://energy-api.com/api/v1/carbon-intensity \
--data-urlencode "country=ES" \
--data-urlencode "api_key=YOUR_API_KEY"
- Pull cross-commodity sentiment (gas and carbon):
curl -G https://energy-api.com/api/v1/latest \
--data-urlencode "symbols=TTF_GAS,EUA_CO2" \
--data-urlencode "api_key=YOUR_API_KEY"
Now pick windows that satisfy both price and emissions constraints. Tag your MQTT payload with forecast_date and a version. Devices execute locally and report telemetry to energy/{country}/{site}/{device}/state for observability. If any step fails, your scheduler defers to last-good data and logs an alert for human review.
Troubleshooting and Common Pitfalls
- Mismatched timezones: Validate the time field in curves is localized to the market’s timezone. When converting to UTC, preserve slot boundaries to avoid off-by-one-hour scheduling around DST changes.
- Sparse data days: On holidays, /historical will provide the most recent prior value. Ensure your algorithm distinguishes between a static carry-forward value and a newly published one.
- Symbol drift: Use /symbols programmatically on startup to validate that all configured symbols still exist and log diffs if anything changes.
- Overfetching: Use ETags or cached timestamps if applicable in your stack. While the API standardizes shape, you still want to minimize redundant network calls.
Conclusion + CTA
Building a geo-fenced DER orchestrator for low-latency control is no longer an ETL project. With a single, normalized REST surface for electricity, gas, oil, coal, carbon allowances, and grid carbon intensity, you can focus on what matters: translating signals into reliable device actions over MQTT. Day-ahead curves, deterministic forecasts, intraday updates, and emissions overlays let you align cost, risk, and sustainability targets across regions without rewriting your pipeline for every market.
The examples in this post give you the foundation to stand up region-aware scheduling, cross-commodity risk adjustments, and resilient control loops that keep running even when individual providers are delayed or transient. If you’re ready to move from prototypes to production orchestration, start integrating the endpoints highlighted here, wire them into your MQTT fabric, and add the reliability patterns—status checks, backoff, circuit breakers—that keep fleets operating safely at scale.
Get to production faster with the uniform, official-data-backed surface from Energy API. Explore the endpoints, wire the examples into your stack, and start orchestrating devices with confidence. Try Energy API for free and turn market and emissions signals into actionable, low-latency control.
Ready to get started?
Get your API key and start querying energy commodity prices in minutes.
Get API KeyRelated posts
Discover best practices for reducing trading latency with a Finance API. Learn how to optimize market data ing...
Read more →
Discover effective Energy API strategies for local utilities to enhance DER management, optimize operations, a...
Read more →
Discover how Energy APIs empower utilities to integrate distributed energy resources effectively. Explore stra...
Read more →
Discover how to effectively benchmark intraday trading algorithms using Finance API market feeds and synthetic...
Read more →
Discover how an Energy API can optimize Virtual Power Plants by providing real-time data for better management...
Read more →