Building Offline-First Mobile Apps for Field Technicians with Energy API: Sync, Conflict Resolution, and Local Analytics
Field technicians work in harsh, connectivity-challenged environments: substations at the grid edge, compressor stations in the middle of nowhere, refineries and wind farms spread across vast geographies. Yet the decisions they make—when to schedule maintenance during a price dip, whether to curtail load at times of high carbon intensity, how to explain a sudden gas price swing to a plant manager—are tightly coupled to fast-moving energy market data. Building an offline-first mobile app that brings reliable, normalized energy data to those field teams is one of the highest-leverage investments an energy organization can make, but it’s hard work unless the data layer “just works.”
This post shows how to build offline-first, conflict-resilient mobile apps for field operations that integrate wholesale energy market data and grid carbon intensity. We will use a single unified data surface from Energy API that aggregates electricity, natural gas, crude oil, coal, carbon allowances, and carbon intensity into a consistent JSON schema. We’ll cover sync strategies, conflict resolution with local changes, and lightweight on-device analytics. You’ll see how to query multiple commodities in one call, how to hydrate local stores fast, how to avoid duplicate work with timeseries boundaries, and how to gracefully handle errors when the radio goes dark.
The result: technicians always have the context they need—not just today’s price point, but the historical arc that shows what’s normal, what’s anomalous, and whether to act now or later. Developers get to ship in hours because the hard parts (provider differences, symbol naming, value normalization) are already solved. And product teams gain confidence that analytics stay coherent whether the app is online, offline, or intermittently connected.
Why Energy API
Energy data is notoriously fragmented. OMIE, ENTSO-E, EIA/FRED, ESIOS, and other sources all publish on different schedules, use different symbol and currency conventions, and sometimes retrofit historical series as methodologies evolve. Stitching these into a cohesive mobile experience usually means weeks of ETL, brittle scrapers, and reconciling a dozen edge cases. Energy API replaces all of this with one normalized REST interface across six commodity categories and 39+ symbols. For offline-first apps, the benefits are especially strong:
- One response shape for all commodities. A single client-side parser drives UI components for gas (TTF_GAS, HENRY_HUB), oil (BRENT_CRUDE, WTI_CRUDE), electricity (OMIE_ES_DA, EPEX_DE_DA, PVPC_ES_2TD, AEMO_NSW1), coal (COAL_ROTTERDAM, COAL_NEWCASTLE), carbon allowances (EUA_CO2), and carbon intensity (CARBON_INT_DE, CARBON_INT_EU). With a unified schema, your local cache, time-bucketed stores, and analytics functions can be shared across the app.
- Deterministic intraday and day-ahead endpoints. Electricity endpoints for hourly or 15-min curves and day-ahead auction results allow you to pre-fetch “tomorrow’s” prices as soon as they’re published. That means your app can proactively sync during a short connectivity window and stay useful all day—even if the field device never reconnects.
- Batching and multi-commodity queries. Query multiple symbols in a single request to minimize over-the-air transfers. Less bandwidth means more reliability in low-signal environments and faster cold-start times when the app launches after being idle.
- Operational clarity. With provider status and clear error codes, you can build robust retry and backoff logic, surface friendly UX messages to technicians, and avoid corrupting your local cache. Reliability comes from simple, predictable API behavior—critical for mobile apps that treat the network as an occasional optimization, not a guarantee.
Quick Start
The base URL for all requests is:
https://energy-api.com/api/v1
Here is a first request to pull the latest values for gas, oil, and carbon allowances in a single round-trip. This is a great pattern for offline-first hydration: do fewer multi-symbol calls when the connection is available, then write results into your local data store keyed by symbol and date for conflict-free upserts later.
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"
}
}
Key fields:
- success: Boolean to confirm the call worked. Use this gate before updating local caches.
- date: The reference date for the returned “latest” values—helpful for stamping entries in a local table if a symbol’s latest data falls on a recent previous day.
- rates: A symbol-to-price map that you can atomically write to your local store. Values are normalized but may have different currencies per symbol.
- dates: Per-symbol date stamps. For offline-first merges, use these keys to ensure idempotent upserts; if you already have the same date and symbol recorded, skip or compare for conflicts.
- currencies: The currency code for each symbol. If your UI targets a single display currency, this guides conversion logic in your local analytics pipeline.
Core Endpoints for Offline-First Apps
For field apps, choose endpoints that compress lots of signal into minimal bytes and deterministic shapes. Below are several endpoints that pair especially well with sync windows, local-first storage, and conflict-safe merges. We’ll cover endpoint paths, parameters, example requests, and realistic responses, followed by implementation notes and field-level explanations so you can build guards around your local cache.
1) GET /symbols — Discover and bootstrap your local catalog
Purpose: Seed your local store with all supported symbols and metadata so the UI can enable/disable features based on availability. This is essential during first-run experiences and after app updates when new commodities are added.
Endpoint:
GET /symbols
Key params:
- base (optional)
- category (gas | electricity | oil | coal | carbon_intensity)
- provider (e.g., fred | omie | eex)
cURL:
curl -G https://energy-api.com/api/v1/symbols \
--data-urlencode "category=gas" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample response:
{
"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."
},
{
"symbol": "HENRY_HUB",
"name": "Henry Hub Natural Gas",
"category": "gas",
"country_code": "US",
"currency_code": "USD",
"frequency": "daily",
"description": "US natural gas benchmark price."
},
{
"symbol": "NBP_GAS",
"name": "NBP Natural Gas",
"category": "gas",
"country_code": "GB",
"currency_code": "GBP",
"frequency": "daily",
"description": "UK NBP gas benchmark."
}
]
}
Field notes:
- symbol/name/category: Use these to build local registries and index per-symbol caches. The symbol is your stable primary key for timeseries tables.
- country_code and currency_code: Drive per-country UI toggles and currency display logic without a separate lookup.
- frequency: Guides your sync cadence. For daily symbols, schedule background fetches accordingly; for intraday endpoints (see electricity/hourly), set tighter windows.
2) GET /timeseries — Hydrate local analytics windows
Purpose: Load historical data between two dates for a small set of symbols and compute rolling metrics offline. Great for first-run hydration and for repairing any missing days after an offline stretch.
Endpoint:
GET /timeseries
Key params:
- start (YYYY-MM-DD, required)
- end (YYYY-MM-DD, required)
- symbols (comma-separated, required)
- base (optional)
cURL:
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"
Sample response:
{
"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,
"2025-01-06": 76.10
},
"TTF_GAS": {
"2025-01-02": 46.80,
"2025-01-03": 47.10,
"2025-01-06": 45.95
}
},
"frequencies": {
"BRENT_CRUDE": "daily",
"TTF_GAS": "daily"
},
"currencies": {
"BRENT_CRUDE": "USD",
"TTF_GAS": "EUR"
}
}
Field notes:
- rates: A nested map keyed by symbol then date. For conflict-safe merges, use a composite key (symbol, date) and an “upsert if not exists or if more recent” rule. Because values are scalar, Last-Write-Wins is usually fine; opportunistically preserve your “source_date” (e.g., from dates map on /latest) for traceability.
- start_date and end_date: Echoed back so you can confirm the window you think you fetched. Useful for slicing your local window updates.
- frequencies and currencies: Exactly how your local processors should interpret values. For analytics, you may convert all values to a display currency at read time rather than write time, which keeps storage normalized.
3) GET /electricity/hourly — Intraday curves for operational decisions
Purpose: Load 15-min or hourly electricity curves for a given symbol and date. Field teams can align dispatch, maintenance, or charging operations with the most favorable time blocks. Offline-first apps should cache at least the next and previous day so that even without connectivity, the UI can still render intraday curves and simple forecasts like “lowest hour of the day.”
Endpoint:
GET /electricity/hourly
Key params:
- symbol (required, e.g., OMIE_ES_DA, EPEX_DE_DA)
- date (required, YYYY-MM-DD)
cURL:
curl -G https://energy-api.com/api/v1/electricity/hourly \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "date=2026-06-11" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample response (illustrative):
{
"success": true,
"symbol": "OMIE_ES_DA",
"date": "2026-06-11",
"unit": "EUR/MWh",
"interval": "hourly",
"curve": [
{"time": "00:00", "value": 82.10},
{"time": "01:00", "value": 79.45},
{"time": "02:00", "value": 77.20},
{"time": "03:00", "value": 75.80},
{"time": "04:00", "value": 74.10},
{"time": "05:00", "value": 76.50},
{"time": "06:00", "value": 88.00},
{"time": "07:00", "value": 96.30},
{"time": "08:00", "value": 102.40},
{"time": "09:00", "value": 98.10},
{"time": "10:00", "value": 94.20},
{"time": "11:00", "value": 92.10},
{"time": "12:00", "value": 90.00},
{"time": "13:00", "value": 88.90},
{"time": "14:00", "value": 87.50},
{"time": "15:00", "value": 86.20},
{"time": "16:00", "value": 88.30},
{"time": "17:00", "value": 95.10},
{"time": "18:00", "value": 108.70},
{"time": "19:00", "value": 112.80},
{"time": "20:00", "value": 110.10},
{"time": "21:00", "value": 101.00},
{"time": "22:00", "value": 93.50},
{"time": "23:00", "value": 88.60}
]
}
Field notes:
- curve: Each entry is an independent datapoint keyed by time. In a local store, use (symbol, date, time) as a composite key. Compute min/max hours, average, or variance locally for analytics that work offline. When online, reconcile by overwriting the entire curve for a date to keep it simple.
- unit and interval: Use these to label charts and to determine downstream analytics (e.g., 15-min vs hourly aggregations).
4) GET /fluctuation — Lightweight edge analytics
Purpose: Precompute change metrics server-side to save bandwidth and compute on older field devices. Useful for showing operators quick deltas since the start of a shift or week, even when full timeseries data isn’t yet cached.
Endpoint:
GET /fluctuation
Key params:
- start (YYYY-MM-DD, required)
- end (YYYY-MM-DD, required)
- symbols (comma-separated, required)
- base (optional)
cURL:
curl -G https://energy-api.com/api/v1/fluctuation \
--data-urlencode "start=2026-06-01" \
--data-urlencode "end=2026-06-11" \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS,EUA_CO2" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample response (illustrative):
{
"success": true,
"base": "MIXED",
"start": "2026-06-01",
"end": "2026-06-11",
"results": {
"BRENT_CRUDE": {
"start_value": 73.10,
"end_value": 74.82,
"change": 1.72,
"change_pct": 2.35
},
"TTF_GAS": {
"start_value": 36.90,
"end_value": 38.15,
"change": 1.25,
"change_pct": 3.39
},
"EUA_CO2": {
"start_value": 66.05,
"end_value": 67.40,
"change": 1.35,
"change_pct": 2.04
}
}
}
Field notes:
- change and change_pct: Perfect for badges and “since last sync” summaries. Store these alongside a “computed_at” timestamp in your local DB to avoid stale displays.
- start_value and end_value: Your local validation can check if these align with cached timeseries endpoints; if not, update the missing days during the next sync window.
5) GET /electricity/latest and GET /forecast — Prep tomorrow’s operations
Purpose: Production teams care about both what’s happening now and what’s coming next. Use these endpoints together to show the latest available electricity prices across markets and to fetch the next published day-ahead results for auction-sourced symbols. For offline-first apps, schedule a sync after typical publication times so tomorrow’s values are already in the device’s cache by the start of the day.
Endpoints:
GET /electricity/latest
GET /forecast
cURL examples:
curl -G https://energy-api.com/api/v1/electricity/latest \
--data-urlencode "api_key=YOUR_API_KEY"
curl -G https://energy-api.com/api/v1/forecast \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "api_key=YOUR_API_KEY"
Sample responses (illustrative):
{
"success": true,
"count": 4,
"base": "MIXED",
"symbols": {
"OMIE_ES_DA": {"value": 89.75, "date": "2026-06-11", "currency": "EUR"},
"EPEX_DE_DA": {"value": 92.30, "date": "2026-06-11", "currency": "EUR"},
"PVPC_ES_2TD": {"value": 0.19, "date": "2026-06-11", "currency": "EUR/kWh"},
"AEMO_NSW1": {"value": 88.10, "date": "2026-06-11", "currency": "AUD/MWh"}
}
}
{
"success": true,
"symbol": "OMIE_ES_DA",
"target_date": "2026-06-12",
"unit": "EUR/MWh",
"status": "published",
"value": 91.40
}
Field notes:
- electricity/latest: Useful for a dashboard tile showing current conditions across markets. Write these per-symbol as the “latest snapshot” entity in your local DB.
- forecast: Deterministic day-ahead results (for auction symbols). If the symbol is not auction-based, you may receive a 404; handle gracefully by hiding the forecast tile for that symbol.
6) GET /carbon-intensity and GET /emissions/latest — Operational ESG context
Purpose: Many organizations align field actions with emissions targets. Surface grid carbon intensity and EU ETS allowance prices side-by-side, so technicians can plan actions for lower-emission windows or tag work orders with contextual emissions data, even when reconnecting later.
Endpoints:
GET /carbon-intensity
GET /emissions/latest
cURL examples:
curl -G https://energy-api.com/api/v1/carbon-intensity \
--data-urlencode "country=DE" \
--data-urlencode "api_key=YOUR_API_KEY"
curl -G https://energy-api.com/api/v1/emissions/latest \
--data-urlencode "api_key=YOUR_API_KEY"
Sample responses (illustrative):
{
"success": true,
"country": "DE",
"unit": "gCO2eq/kWh",
"date": "2026-06-11",
"value": 322
}
{
"success": true,
"symbol": "EUA_CO2",
"date": "2026-06-11",
"currency": "EUR",
"value": 67.40
}
Field notes:
- carbon-intensity: Ideal for tagging site visits or dispatch windows with an ESG context. Store intensity alongside the time the field action was performed for later analytics.
- emissions/latest: Snapshot of EUA allowance price. Use it for trend banners or quick comparisons to previous cached EUA values fetched via /timeseries.
7) GET /status — Build resilient sync and observability
Purpose: Before a planned background sync, you may ping provider status to adapt your fetch plan. If a source is temporarily stale, skip fetching dependent symbols and avoid marking your local cache as incomplete. This improves the perceived reliability of your app.
Endpoint:
GET /status
cURL:
curl -G https://energy-api.com/api/v1/status \
--data-urlencode "api_key=YOUR_API_KEY"
Sample response (illustrative):
{
"success": true,
"providers": {
"OMIE": {"last_fetch": "2026-06-11T09:15:00Z", "status": "ok"},
"ENTSO-E": {"last_fetch": "2026-06-11T09:10:00Z", "status": "ok"},
"EIA": {"last_fetch": "2026-06-10T21:00:00Z", "status": "ok"},
"FRED": {"last_fetch": "2026-06-10T20:55:00Z", "status": "ok"},
"ESIOS": {"last_fetch": "2026-06-11T09:12:00Z", "status": "ok"}
}
}
Field notes:
- status and last_fetch: Drive a decision tree for what to fetch next, especially when your background job budget is tight. For example, skip intraday curves if the source has not published updates since your last sync.
Designing Offline-First Sync for Field Technicians
An effective mobile sync strategy does three things well: minimizes bandwidth and requests, ensures deterministic merges into the local cache, and preserves a consistent UI even when the backend can’t be reached. With Energy API’s normalized schema and multi-symbol batching, you can structure your client in layers:
- Bootstrap: On first run or after long offline periods, call /symbols to populate your local catalog; then issue a small number of /timeseries calls to hydrate “operational windows” (e.g., last 180 days for BRENT_CRUDE, TTF_GAS, EUA_CO2).
- Recurring hydration: On app foreground or scheduled background tasks, call /latest for a multi-symbol set to refresh snapshots and optionally /fluctuation for small windows like the last 7 or 30 days to populate badges without pulling full timeseries.
- Intraday electricity: After typical publication times, call /electricity/hourly for “today” and, when available, “tomorrow” (using /forecast to know when to fetch). Always write curves for a full date in one transaction to simplify merges.
- ESG context: Fetch /carbon-intensity for the technician’s operating country at low frequency (daily or every few hours) and store it in a local table keyed by country+date.
- Observability: Ping /status occasionally; if sources are stale, adjust your sync plan and present a non-blocking banner in the UI to set expectations.
Because values returned by Energy API are already normalized, conflict resolution is straightforward. We recommend:
- Use composite keys aligned with API semantics: for daily series, (symbol, date); for intraday curves, (symbol, date, time).
- Prefer idempotent upserts: If a row exists with the same key, overwrite value and metadata (currency/unit) to avoid partial merges. Store “fetched_at” timestamps per row for troubleshooting.
- When network is offline: Buffer user-generated annotations (notes, flags, site tags) separately from market data. Use local timestamps and a client-unique ID for each annotation, and apply Last-Write-Wins or version counters if technicians can edit the same record on multiple devices.
- Handle deletions by tombstones when applicable: For your own domain objects (not market data), keep a deleted=true flag until the server acknowledges. Market data is append-only keyed by date/time, so tombstones are unnecessary there.
Conflict Resolution Patterns for Market Data + Local Notes
Conflict resolution is simpler when market data and user annotations live in separate tables. Market data from Energy API is authoritative and immutable per (symbol, date, [time])—so you can always favor the latest fetch. For user notes or decisions that reference market snapshots:
- Attach foreign keys to stable symbols and dates. For a note about “TTF_GAS on 2026-06-11,” store symbol=TTF_GAS and date=2026-06-11. This avoids ambiguous lookups after sync.
- If a technician edits the same note on two devices, resolve with Last-Write-Wins using the higher client_clock or server_received_at when eventually synced. Market data remains unchanged; only the note’s text/tags vary.
- For batched writes after reconnect: Send domain objects (notes, checklists) upstream first; then refresh market data with /latest and /timeseries. That order keeps the UI consistent—the technician sees their own actions reflected immediately, then fresh prices land afterward.
For read-time analytics (e.g., rolling averages), prefer recomputing from the local cache instead of persisting aggregates, to avoid merge conflicts. If performance becomes a concern on low-end devices, you can persist derived metrics with a version tag tied to the last “rates hash” or window end-date; if a newer fetch arrives that overlaps the computed window, invalidate those aggregates.
Local Analytics That Work Without Connectivity
With timeseries data for 90–180 days per symbol and electricity intraday curves, you can compute a powerful set of analytics on-device:
- Rolling 7/30/90-day averages and standard deviations per symbol to flag anomalies during site inspections. Compute on read or as a background task after hydration.
- Intraday “best hour to run” by scanning the curve for min value, plus a “risk” band from standard deviation using historical curves if stored.
- Cross-commodity deltas: Compare TTF_GAS vs BRENT_CRUDE weekly changes to contextualize refinery or cogeneration economics on the ground. Use /fluctuation to avoid pulling a full window.
- Carbon-aware scheduling: Given CARBON_INT_DE at 322 gCO2eq/kWh today, suggest non-urgent operations at hours with lower intensity tomorrow, if your region’s data cadence supports forecasts or if “next-day” publication rhythms are known.
These analytics require predictable, normalized inputs—which is precisely what Energy API provides. Your code can be commodity-agnostic: pass a symbol list to /timeseries or /latest, then feed results into shared compute paths keyed by symbol metadata from /symbols.
Error Handling, Retries, and Health Checks
In the field, errors are normal. Design your client to treat them as signals for alternate flows:
- 401: If authentication fails, do not clear your local cache. Show cached data with a subtle banner. Defer remediation to when the device reconnects to a trusted network. Avoid retry storms.
- 404: The symbol/date may be unavailable (e.g., non-auction symbol for /forecast). Fallback to related endpoints (/latest or /timeseries) and hide forecast tiles for that symbol.
- 422: Validation error. Log the offending parameter set locally for debugging; show the last good data.
- 429: Rate limit exceeded. Implement exponential backoff with jitter. Queue any additional background syncs for later instead of retrying immediately.
The /status endpoint provides upstream visibility that helps you choose when to make heavier calls (e.g., /electricity/hourly for multiple symbols). If a provider is momentarily stale, do a lightweight refresh with /latest and postpone big timeseries updates until the source recovers.
Implementation Examples: cURL, JavaScript, Python
Below are practical snippets that you can adapt to your sync engine. They show multi-commodity hydration, intraday curves, and idempotent local writes (pseudocode for storage).
Multi-symbol latest snapshot (cURL)
curl -G https://energy-api.com/api/v1/latest \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS,EUA_CO2,OMIE_ES_DA" \
--data-urlencode "api_key=YOUR_API_KEY"
Idempotent write notes:
- For each (symbol, value, currency, date), upsert into local_snapshots with a unique index on (symbol). Always overwrite to keep the snapshot current. For timeseries stores, use (symbol, date) and only insert if not present or if value changed (rare).
Hydrating timeseries and computing a rolling average (JavaScript)
async function fetchTimeseries(symbols, start, end, apiKey) {
const url = new URL("https://energy-api.com/api/v1/timeseries");
url.searchParams.set("start", start);
url.searchParams.set("end", end);
url.searchParams.set("symbols", symbols.join(","));
url.searchParams.set("api_key", apiKey);
const res = await fetch(url.toString());
const data = await res.json();
if (!data.success) throw new Error("Timeseries fetch failed");
// Upsert per (symbol, date)
for (const sym of Object.keys(data.rates)) {
const series = data.rates[sym];
for (const [date, value] of Object.entries(series)) {
await upsertDaily(sym, date, value, data.currencies[sym], data.frequencies[sym]);
}
}
return data;
}
function rollingAverage(values, window) {
const out = [];
let sum = 0;
for (let i = 0; i < values.length; i++) {
sum += values[i];
if (i >= window) sum -= values[i - window];
if (i + 1 >= window) out.push(sum / window);
}
return out;
}
Intraday electricity curve and best-hour computation (Python)
import requests
def fetch_hourly(symbol, date, api_key):
url = "https://energy-api.com/api/v1/electricity/hourly"
params = {"symbol": symbol, "date": date, "api_key": api_key}
r = requests.get(url, params=params, timeout=15)
data = r.json()
if not data.get("success"):
raise RuntimeError("Hourly curve fetch failed")
# Upsert whole curve in one transaction
for point in data["curve"]:
upsert_intraday(symbol, data["date"], point["time"], point["value"], data["unit"], data["interval"])
return data
def best_hour(curve):
return min(curve, key=lambda p: p["value"])
# Usage:
# resp = fetch_hourly("OMIE_ES_DA", "2026-06-11", YOUR_API_KEY)
# hour = best_hour(resp["curve"])
# print(f"Lower-cost operation hour: {hour['time']} at {hour['value']} {resp['unit']}")
End-to-End JSON Walkthrough: From Discovery to Decisions
Let’s run a compact but complete sequence that a field app might execute during a brief connectivity window, including realistic JSON and what your local store should do with it.
1) Discover symbols for the EU portfolio
curl -G https://energy-api.com/api/v1/symbols \
--data-urlencode "category=electricity" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"count": 3,
"symbols": [
{
"symbol": "OMIE_ES_DA",
"name": "Spain Day-Ahead (OMIE)",
"category": "electricity",
"country_code": "ES",
"currency_code": "EUR",
"frequency": "daily",
"description": "Day-ahead auction results from OMIE."
},
{
"symbol": "EPEX_DE_DA",
"name": "Germany Day-Ahead (EPEX)",
"category": "electricity",
"country_code": "DE",
"currency_code": "EUR",
"frequency": "daily",
"description": "Day-ahead auction results from EPEX."
},
{
"symbol": "PVPC_ES_2TD",
"name": "Spain PVPC 2.0TD Retail Reference",
"category": "electricity",
"country_code": "ES",
"currency_code": "EUR/kWh",
"frequency": "hourly",
"description": "Official Spanish PVPC hourly retail reference."
}
]
}
Local action: Upsert symbol metadata; enable UI cards for Spanish and German day-ahead, plus PVPC hourly view. Store frequency for sync cadence.
2) Hydrate the last 90 days for BRENT_CRUDE, TTF_GAS, EUA_CO2
curl -G https://energy-api.com/api/v1/timeseries \
--data-urlencode "start=2026-03-13" \
--data-urlencode "end=2026-06-11" \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS,EUA_CO2" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"base": "MIXED",
"start_date": "2026-03-13",
"end_date": "2026-06-11",
"rates": {
"BRENT_CRUDE": {
"2026-06-07": 74.20,
"2026-06-10": 74.55,
"2026-06-11": 74.82
},
"TTF_GAS": {
"2026-06-07": 37.60,
"2026-06-10": 37.95,
"2026-06-11": 38.15
},
"EUA_CO2": {
"2026-06-07": 66.80,
"2026-06-10": 67.10,
"2026-06-11": 67.40
}
},
"frequencies": {
"BRENT_CRUDE": "daily",
"TTF_GAS": "daily",
"EUA_CO2": "daily"
},
"currencies": {
"BRENT_CRUDE": "USD",
"TTF_GAS": "EUR",
"EUA_CO2": "EUR"
}
}
Local action: Upsert each (symbol, date) row; compute rolling 7-day deltas and tag anomalies. Use currencies to format UI values consistently.
3) Get today’s and tomorrow’s Spanish day-ahead context
curl -G https://energy-api.com/api/v1/electricity/hourly \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "date=2026-06-11" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"symbol": "OMIE_ES_DA",
"date": "2026-06-11",
"unit": "EUR/MWh",
"interval": "hourly",
"curve": [
{"time": "00:00", "value": 82.10},
{"time": "01:00", "value": 79.45},
{"time": "02:00", "value": 77.20}
]
}
curl -G https://energy-api.com/api/v1/forecast \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"symbol": "OMIE_ES_DA",
"target_date": "2026-06-12",
"unit": "EUR/MWh",
"status": "published",
"value": 91.40
}
Local action: Store the entire 2026-06-11 curve; surface a “lowest-cost hour” recommendation. Cache the forecast value for 2026-06-12; schedule another fetch for the full curve after publication hours.
4) Emissions context for a German asset
curl -G https://energy-api.com/api/v1/carbon-intensity \
--data-urlencode "country=DE" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"country": "DE",
"unit": "gCO2eq/kWh",
"date": "2026-06-11",
"value": 322
}
Local action: Attach this intensity to any checklists completed today in Germany and display a carbon-aware suggestion banner to defer non-urgent loads to hours of lower intensity if available.
Real-World Use Cases
Here are three concrete apps our customers routinely build with this approach.
- Maintenance window optimizer: A mobile tool that proposes the two cheapest hours today and tomorrow to run power-intensive maintenance at Spanish and German sites. Endpoints: /electricity/hourly (OMIE_ES_DA, EPEX_DE_DA), /forecast for day-ahead readiness, /carbon-intensity for ESG overlays. The app caches curves, computes min windows offline, and posts a one-tap schedule to a work management system when online.
- On-site procurement brief: A technician can open a summary tile showing the latest BRENT_CRUDE, TTF_GAS, EUA_CO2 values and 7-day percentage changes with /latest and /fluctuation. This makes quick “should we buy now or later?” conversations grounded in fresh numbers, even if the device connected only minutes earlier at the gatehouse.
- ESG audit companion: During a field audit, the app logs carbon intensity for the country from /carbon-intensity and references EU ETS spot prices from /emissions/latest, anchoring each observation with a timestamp and local market context. Later, compliance teams can reconcile the audit data with central records without worrying about data format drift.
FAQ
How often does the TTF gas price update?
TTF_GAS is provided as a daily series via Energy API. Many teams poll /latest for quick checks and fill gaps with /timeseries for specific windows. Because schedules can vary by source, use date keys returned in the payload to confirm freshness and drive your local cache policy.
Can I get historical energy prices going back several years?
Energy API’s /timeseries endpoint returns historical data between start and end dates for supported symbols. You can hydrate multi-year windows for analytics and charting and then incrementally extend your local cache with periodic calls. Always store by (symbol, date) so you can easily backfill missing days after long offline periods.
Does the API support multiple commodities in one call?
Yes. Many endpoints, including /latest, /historical, /timeseries, and /fluctuation, accept comma-separated symbol lists so you can fetch gas, oil, carbon, coal, and electricity side-by-side. This significantly reduces bandwidth and simplifies your sync engine—ideal for devices with intermittent connectivity.
What’s the best way to handle intraday electricity curves offline?
Fetch the entire curve for the relevant date using /electricity/hourly and upsert all points in one transaction keyed by (symbol, date, time). Compute min/max and display analytics locally. When reconnecting, overwrite the full curve for that date to simplify merges and avoid partial updates.
How should I recover from validation or not-found errors?
For 422 errors, log the params and fall back to showing cached data. For 404 on endpoints like /forecast with non-auction symbols, hide the forecast UI for that symbol and rely on /latest or /electricity/hourly where applicable. Avoid clearing your cache on errors; offline-first UX should keep working with the last known good state.
Best Practices: Performance, Reliability, and Governance in the Mobile Client
While Energy API standardizes data representation, your mobile architecture determines real-world reliability. Adopt these practices for production-grade field apps:
- Batch aggressively: Prefer multi-symbol requests (/latest, /timeseries) over many single-symbol calls. This reduces setup/teardown overhead and keeps your sync windows short.
- Use deterministic keys: Index your local tables by (symbol, date) and (symbol, date, time) for intraday curves. This keeps upserts idempotent and avoids “double counting.”
- Exponential backoff and jitter: When you encounter 429 or transient network failures, back off to protect device battery life and avoid futile retry storms in low-signal zones.
- Health-aware fetch plans: Read /status to decide whether to initiate “heavy” calls (e.g., multiple hourly curves). If the provider is stale, deprioritize those calls and lean on /latest.
- Observability and audits: Stamp each row with fetched_at and source fields where available. When technicians report discrepancies, you can trace exactly what the device saw.
- Data locality on device: Encrypt your local database at rest. Even if market data is public, your annotations, schedules, and notes aren’t. Keep schema migrations additive to support seamless upgrades in the field.
- UI that respects stale states: Always show “as of DATE” next to values. If a fetch fails, keep rendering cached data with a subtle banner instead of blocking workflows.
Extended Endpoint Coverage and Practical Tips
Beyond the endpoints already demonstrated, here is how additional Energy API endpoints fit into a robust offline-first toolkit, with practical advice for each:
- GET /historical: Fetch all prices for specified symbols on a particular past date. Use this to fill in a single date that went missing. If the date is a non-publishing day, the API returns the most recent prior value—your local storage should record the echoed date and optionally mark it as a carry-forward.
- GET /gas/latest, GET /coal/latest: For category-specific snapshots, these endpoints consolidate benchmarks like TTF_GAS vs HENRY_HUB and COAL_ROTTERDAM vs COAL_NEWCASTLE, which is handy for category dashboards without specifying symbols manually.
- GET /electricity/pvpc: Spanish PVPC hourly retail reference prices. Useful when your field work involves retail tariff timing. Store the entire hourly array per date for the same conflict-free strategy used for /electricity/hourly.
- GET /ohlc: Weekly, monthly, or quarterly OHLC candles. For executive overviews and volatility assessments on the device, fetch monthly candles to render charts without heavy daily timeseries pulls. Store by (symbol, period_start, period_type).
- POST /cost-estimate: Send a simple body with kWh/month and either a symbol or country to compute an estimated monthly wholesale cost. On device, this helps technicians give ballpark figures during site visits without maintaining their own calculator logic. Remember that estimates exclude taxes, network charges, and hourly profiles.
Practical tips:
- When rendering charts online or offline, favor minimal transforms. The API already normalizes data; your client should only format and aggregate for visualization.
- For daily series, precompute and cache the last 30 days locally at app launch; lazy-load older ranges on scroll or user demand.
- For intraday, maintain a rolling cache of yesterday, today, and tomorrow curves, replacing them atomically when a new publication arrives.
Complete JSON Examples for Additional Endpoints
GET /historical (single-day backfill)
curl -G https://energy-api.com/api/v1/historical \
--data-urlencode "date=2025-09-15" \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"date": "2025-09-15",
"base": "MIXED",
"rates": {
"BRENT_CRUDE": 71.45,
"TTF_GAS": 36.20
},
"currencies": {
"BRENT_CRUDE": "USD",
"TTF_GAS": "EUR"
}
}
Use date as the authoritative key for inserts. If your local DB has a gap at 2025-09-15, this fills it deterministically.
GET /gas/latest (category snapshot)
curl -G https://energy-api.com/api/v1/gas/latest \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"symbols": {
"TTF_GAS": {"value": 38.15, "date": "2026-06-11", "currency": "EUR"},
"HENRY_HUB": {"value": 2.95, "date": "2026-06-11", "currency": "USD"}
}
}
Category endpoints simplify UI tabs—store as snapshots keyed by symbol.
GET /ohlc (monthly candles)
curl -G https://energy-api.com/api/v1/ohlc \
--data-urlencode "symbols=BRENT_CRUDE,TTF_GAS" \
--data-urlencode "period=monthly" \
--data-urlencode "start=2025-01-01" \
--data-urlencode "end=2025-12-31" \
--data-urlencode "api_key=YOUR_API_KEY"
{
"success": true,
"period": "monthly",
"results": {
"BRENT_CRUDE": [
{"period": "2025-01", "open": 76.30, "high": 78.90, "low": 74.10, "close": 75.60, "data_points": 21},
{"period": "2025-02", "open": 75.70, "high": 79.50, "low": 74.80, "close": 77.90, "data_points": 20}
],
"TTF_GAS": [
{"period": "2025-01", "open": 46.80, "high": 49.20, "low": 44.70, "close": 47.10, "data_points": 21},
{"period": "2025-02", "open": 47.20, "high": 50.00, "low": 45.30, "close": 48.40, "data_points": 20}
]
}
}
Candles are ideal for overview charts that carry well even when the device hasn’t been online recently. Store by (symbol, period).
Troubleshooting and Developer Concerns
If a field device shows unexpected values, check:
- Local clock skew: Make sure your app uses date strings from API responses as authoritative; do not infer “today” from the device clock when mapping timeseries keys.
- Partial writes: Always upsert full objects for intraday curves to avoid half-updated charts. Use transactions if your local DB supports them.
- Currency mismatches: The API returns per-symbol currencies. If you present a unified currency, run conversions at read time to avoid stale conversions when exchange rates change.
- Endpoint fit: Use /fluctuation when you only need deltas; use /timeseries when you need the raw points for analytics. Using the wrong endpoint often leads to excess bandwidth or insufficient local data.
Finally, log the exact request parameters and store last successful payload snippets (or hashes) in your diagnostics. In the field, these minimal breadcrumbs often solve cases that would otherwise require reproduction in the lab.
Conclusion + CTA
Building a robust offline-first mobile app for field technicians doesn’t have to mean wrestling with dozens of provider portals, inconsistent symbols, and brittle scrapers. With a single normalized REST surface across electricity, gas, oil, coal, carbon, and carbon intensity, Energy API gives you deterministic JSON you can trust, multi-commodity batching that respects weak connectivity, and specialized endpoints (intraday curves, day-ahead forecasts, fluctuation deltas) that minimize the work your app does on-device.
Teams that adopt this approach ship faster. They hydrate local stores in minutes, compute resilient analytics on-device, and present operators with data that remains actionable even when the network disappears. The result is fewer delays, better maintenance timing, and decisions that reflect both cost and carbon considerations—right where the work happens.
If you’re ready to put normalized energy data into the hands of your field teams, start integrating today. Explore the endpoints, copy the cURL calls, and build your offline cache and analytics pipeline on top of a single, unified interface. Try Energy API for free and turn intermittent connectivity into a non-issue for your technicians.
Ready to get started?
Get your API key and start querying energy commodity prices in minutes.
Get API KeyRelated posts
Discover how Energy API empowers local energy communities by streamlining access to market data, enabling effi...
Read more →
Discover how Energy API enhances collaboration between utilities and local governments, providing real-time da...
Read more →
Discover how Energy API empowers microgrids, streamlining data access for resilient local energy solutions. Un...
Read more →
Discover how to leverage Energy API to enhance smart building technologies and improve energy efficiency. Unlo...
Read more →
Unlock the power of Energy API to build accurate energy forecasting models. Discover how to streamline data an...
Read more →