Marginal Carbon Intensity Mapping for Corporate Procurement: Using Energy API to Generate Hourly Scope 2 Market-Based Reports

Marginal Carbon Intensity Mapping for Corporate Procurement: Using Energy API to Generate Hourly Scope 2 Market-Based Reports

Corporate buyers are being asked to prove decarbonization with real data, not narratives. Sustainability teams need hourly, location-specific carbon signals to calculate market-based Scope 2 emissions, finance needs defensible audit trails, and software teams must deliver dashboards that refresh without manual CSV uploads. The problem is that the data you need lives across dozens of government and market portals, each with its own naming schemes, delivery schedules, and formats. Stitching them into a coherent, production-grade pipeline can take months—and every regulator or auditor expectation you add (hourly detail, marginal intensity, time-of-use alignment, forecast-aware procurement) multiplies the complexity.

This post shows a faster path: how to map marginal carbon intensity and build hourly Scope 2 market-based reports using a single, normalized data surface. We will use the Energy category endpoints from Energy API to aggregate electricity prices, intraday curves, and grid carbon intensity across regions, then combine them into a developer-friendly workflow. You will learn how to pull the hourly price curve for your contract region, align it to the same clock as grid carbon intensity, and compute market-based emissions that stand up under ESG and audit scrutiny.

If you are a developer, data engineer, trader, or sustainability product lead, the aim is to help you ship features in hours, not weeks of ETL work. By the end, you will have a blueprint for building a reliable Scope 2 engine that unifies electricity market prices, emissions intensity, and portfolio analytics—with concrete API calls, JSON schemas, and real-world implementation guidance.

Why Energy API

Building a defensible Scope 2 reporting pipeline requires more than one dataset. You need day-ahead electricity prices to value procurement, intraday or hourly curves for time-matching, regional carbon intensity to estimate emissions, and cross-commodity context for narrative and risk (e.g., how gas and carbon allowance prices influence power). Trying to coordinate OMIE, ENTSO-E, ESIOS, EIA/FRED, and Ember feeds is a full-time integration project. Energy API collapses that into one normalized REST surface so you can focus on analytics, not plumbing.

Key developer advantages:

  • One schema across energy assets. Whether you query electricity, gas, oil, coal, carbon allowances, or grid carbon intensity, you get the same response structure and metadata fields. This means you can write one parser and reuse it everywhere—ideal for services that ingest multiple commodities in the same job.
  • Time alignment without chaos. Intraday electricity curves (15-min or hourly where available) and country-level carbon intensity are accessible through consistent endpoints. You no longer have to reconcile different time zones, day-ahead auction semantics, or weekend/holiday publishing behavior yourself—the API normalizes it.
  • Commodity composability. Pull electricity prices, EUA carbon allowance prices, and carbon intensity in a single pass. That makes it straightforward to compute market-based Scope 2 emissions, price-attributed emissions, or to construct scenario analyses that cross-link price and emissions signals.
  • Production ergonomics. Core endpoints include discovery (/symbols), latest, historical, timeseries, intraday electricity curves, day-ahead forecast (for auction-based series), and carbon-intensity by country. Error responses are consistent, and each resource surfaces currency, frequency, and publish dates so you can build robust pipelines and alerts.

Bottom line: The Energy category endpoints abstract away the heterogeneity of market sources and let you ship data-driven Scope 2 features quickly, from hourly emissions accounting to procurement optimization and audit-ready histories.

Quick Start

All examples below use the Energy category endpoints hosted at the following base URL:

Base URL: https://energy-api.com/api/v1

To sanity check connectivity and schema, start by discovering a few symbols (e.g., EU gas, EU ETS carbon allowance, and a carbon intensity series). This one call demonstrates the normalized interface and consistent metadata that you will use across every commodity.

curl -G https://energy-api.com/api/v1/symbols \
--data-urlencode "category=carbon_intensity" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"count": 2,
"symbols": [
{
"symbol": "CARBON_INT_DE",
"name": "Germany Grid Carbon Intensity",
"category": "carbon_intensity",
"country_code": "DE",
"currency_code": "N/A",
"frequency": "hourly",
"description": "Grid intensity in gCO2eq/kWh."
},
{
"symbol": "CARBON_INT_EU",
"name": "EU-27 Average Grid Carbon Intensity",
"category": "carbon_intensity",
"country_code": "EU",
"currency_code": "N/A",
"frequency": "hourly",
"description": "EU average grid intensity in gCO2eq/kWh."
}
]
}

Notice the consistent field names (symbol, category, country_code, frequency). That uniformity is what lets you query electricity prices, carbon intensity, and allowances with the same client code.

Core Endpoints for Marginal Carbon Intensity Mapping and Hourly Scope 2

To build an hourly, market-based Scope 2 engine, you will typically combine:

  • Electricity intraday curves (GET /electricity/hourly) to obtain the hourly (or 15-min) price for your region.
  • Grid carbon intensity by country (GET /carbon-intensity) to align emissions with each hour’s consumption.
  • Day-ahead forecast for auction-based markets (GET /forecast) to plan procurement and generate predictive reports that lock to the next published delivery day.
  • Cross-commodity context such as EUA_CO2 (GET /latest or GET /timeseries) to track carbon market dynamics alongside your power prices.
  • Discovery and validation helpers like GET /symbols and GET /status to automate symbol management and production health checks.

1) Electricity Intraday Curves — GET /electricity/hourly

Purpose: Retrieve the full intraday price curve for a specific electricity symbol and date. Where sources provide quarter-hour resolution, the API returns all intervals; otherwise hourly. For market-based Scope 2, this is the foundation for time-matching your site’s consumption to the actual purchase price and delivery period.

Endpoint: GET /electricity/hourly

Key parameters:

  • symbol (required): e.g., OMIE_ES_DA (Spain day-ahead), EPEX_DE_DA (Germany), AEMO_NSW1 (Australia).
  • date (required): YYYY-MM-DD for the delivery date.

cURL example (Germany day-ahead, EPEX):

curl -G https://energy-api.com/api/v1/electricity/hourly \
--data-urlencode "symbol=EPEX_DE_DA" \
--data-urlencode "date=2026-06-11" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"symbol": "EPEX_DE_DA",
"date": "2026-06-11",
"currency_code": "EUR",
"interval": "hourly",
"timezone": "Europe/Berlin",
"curve": [
{ "start": "2026-06-11T00:00:00+02:00", "end": "2026-06-11T01:00:00+02:00", "price": 66.11 },
{ "start": "2026-06-11T01:00:00+02:00", "end": "2026-06-11T02:00:00+02:00", "price": 64.02 },
{ "start": "2026-06-11T02:00:00+02:00", "end": "2026-06-11T03:00:00+02:00", "price": 61.77 },
{ "start": "2026-06-11T03:00:00+02:00", "end": "2026-06-11T04:00:00+02:00", "price": 60.80 },
{ "start": "2026-06-11T04:00:00+02:00", "end": "2026-06-11T05:00:00+02:00", "price": 62.19 },
{ "start": "2026-06-11T05:00:00+02:00", "end": "2026-06-11T06:00:00+02:00", "price": 64.50 },
{ "start": "2026-06-11T06:00:00+02:00", "end": "2026-06-11T07:00:00+02:00", "price": 72.93 },
{ "start": "2026-06-11T07:00:00+02:00", "end": "2026-06-11T08:00:00+02:00", "price": 79.14 }
// ... remaining hours for the day
]
}

How to use it:

  • Use timezone to align to your metering and carbon-intensity feed. Always normalize to a single zone before joining datasets.
  • The curve array gives you price per delivery interval. Multiply by your site’s interval consumption to compute interval cost, then aggregate for daily or monthly totals.
  • For 15-min markets, interval may be "quarter_hourly" with four intervals per hour—store interval boundaries precisely to avoid double counting during DST transitions.

2) Carbon Intensity by Country — GET /carbon-intensity

Purpose: Obtain grid carbon intensity (gCO2eq/kWh) for a specific country. For Scope 2 market-based accounting you will often use supplier or contract-specific emission factors; however, grid carbon intensity is essential for residual-mix calculation, operational insights, and marginal emissions mapping (e.g., when correlating price spikes with higher marginal emissions hours).

Endpoint: GET /carbon-intensity

Key parameters:

  • country (ISO-2): e.g., DE, ES, FR, IT.
  • base (optional, generally not used for intensity since it’s not a currency).

cURL example (Germany):

curl -G https://energy-api.com/api/v1/carbon-intensity \
--data-urlencode "country=DE" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"country": "DE",
"unit": "gCO2eq/kWh",
"frequency": "hourly",
"timezone": "Europe/Berlin",
"series": [
{ "start": "2026-06-11T00:00:00+02:00", "end": "2026-06-11T01:00:00+02:00", "intensity": 345 },
{ "start": "2026-06-11T01:00:00+02:00", "end": "2026-06-11T02:00:00+02:00", "intensity": 338 },
{ "start": "2026-06-11T02:00:00+02:00", "end": "2026-06-11T03:00:00+02:00", "intensity": 332 }
// ... remaining hours
]
}

How to use it:

  • Join series to your electricity curve on aligned start timestamps. Compute hourly emissions as kWh_hour × intensity_hour / 1000 to get kgCO2eq, or divide by 1,000,000 for tCO2eq.
  • For marginal intensity mapping, analyze correlation between price_hour and intensity_hour. In thermal-constrained systems, high prices often coincide with higher marginal units, increasing emissions.
  • Store timezone and delivery windows to maintain audit integrity across DST boundaries.

3) Day-Ahead Forecast (Published Auction) — GET /forecast

Purpose: Retrieve the next published day-ahead price for auction-sourced electricity symbols. This is not a statistical prediction; it’s a deterministic lookup of the most recently published auction result for the next delivery day. For procurement planning and Scope 2 previews, you can compute tomorrow’s cost exposure and emissions alignment before the delivery window starts.

Endpoint: GET /forecast

Key parameters:

  • symbol (required): e.g., OMIE_ES_DA, EPEX_DE_DA.

cURL example:

curl -G https://energy-api.com/api/v1/forecast \
--data-urlencode "symbol=OMIE_ES_DA" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"symbol": "OMIE_ES_DA",
"date": "2026-06-12",
"currency_code": "EUR",
"interval": "hourly",
"timezone": "Europe/Madrid",
"curve": [
{ "start": "2026-06-12T00:00:00+02:00", "end": "2026-06-12T01:00:00+02:00", "price": 69.45 },
{ "start": "2026-06-12T01:00:00+02:00", "end": "2026-06-12T02:00:00+02:00", "price": 66.90 }
// ... remaining hours
]
}

How to use it:

  • Precompute tomorrow’s expected cost and emissions profile by joining the forecast curve to your expected load shape and anticipated carbon intensity for the same hours.
  • Flag risk windows: alert if certain hours exceed a price or emissions threshold; shift flexible load or dispatch storage accordingly.
  • Note that GET /forecast returns 404 for non-auction symbols; catch and fallback to previous published data or default schedules.

4) Latest Cross-Commodity Signals — GET /latest

Purpose: Pull multiple symbols across categories in a single request. For example, retrieve the latest EUA_CO2 (EU ETS allowance), TTF_GAS (EU hub), and BRENT_CRUDE to contextualize your power cost and emissions budgeting.

Endpoint: GET /latest

Key parameters:

  • symbols (required, comma-separated): e.g., EUA_CO2, TTF_GAS, BRENT_CRUDE.
  • base (optional): filter/normalize currency where applicable.

cURL example:

curl -G https://energy-api.com/api/v1/latest \
--data-urlencode "symbols=EUA_CO2,TTF_GAS,BRENT_CRUDE" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"date": "2026-06-11",
"base": "MIXED",
"rates": {
"EUA_CO2": 67.40,
"TTF_GAS": 38.15,
"BRENT_CRUDE": 74.82
},
"dates": {
"EUA_CO2": "2026-06-11",
"TTF_GAS": "2026-06-11",
"BRENT_CRUDE": "2026-06-11"
},
"currencies": {
"EUA_CO2": "EUR",
"TTF_GAS": "EUR",
"BRENT_CRUDE": "USD"
}
}

How to use it:

  • rates exposes the latest value per symbol. dates shows publishing dates; store them for audit trails. currencies identifies the unit of account so you can convert or report consistently.
  • For Scope 2 narratives, comparing EUA_CO2 and TTF_GAS trends provides context on why grid intensity or power prices spiked (e.g., tight gas supply increasing marginal CCGT dispatch costs).
  • Use in dashboards and alerts; one call updates multiple panels without extra requests.

5) Historical Ranges — GET /timeseries

Purpose: Fetch historical series between two dates for charting, regression, or monthly rollups. Essential for audit backfills, performance overviews, and verifying that your emissions intensity mapping matches historical events.

Endpoint: GET /timeseries

Key parameters:

  • start (required): YYYY-MM-DD
  • end (required): YYYY-MM-DD
  • symbols (required): one or more symbols, comma-separated

cURL example (EUA and gas trend to contextualize emissions budgeting):

curl -G https://energy-api.com/api/v1/timeseries \
--data-urlencode "start=2026-01-01" \
--data-urlencode "end=2026-03-31" \
--data-urlencode "symbols=EUA_CO2,TTF_GAS" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"base": "MIXED",
"start_date": "2026-01-01",
"end_date": "2026-03-31",
"rates": {
"EUA_CO2": {
"2026-01-02": 78.10,
"2026-01-03": 77.65,
"2026-01-06": 79.20
},
"TTF_GAS": {
"2026-01-02": 42.75,
"2026-01-03": 43.10,
"2026-01-06": 41.95
}
},
"frequencies": {
"EUA_CO2": "daily",
"TTF_GAS": "daily"
},
"currencies": {
"EUA_CO2": "EUR",
"TTF_GAS": "EUR"
}
}

How to use it:

  • rates contains date-keyed values per symbol. Join series for composite charts and regression analysis (e.g., how much of your power price variance is explained by TTF moves).
  • frequencies clarifies the granularity; store this to avoid mixing monthly candles with daily rates in the same panel.
  • Use this endpoint for reproducible analytics—pin start_date and end_date in reports so your outputs are deterministic.

6) Discovery and Health — GET /symbols and GET /status

You will automate symbol discovery to keep your catalogs fresh and use provider status to build health checks. This matters for production-grade Scope 2 reporting where you need clear operational observability.

Discover active symbols:

curl -G https://energy-api.com/api/v1/symbols \
--data-urlencode "category=electricity" \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"count": 3,
"symbols": [
{
"symbol": "OMIE_ES_DA",
"name": "OMIE Spain Day-Ahead",
"category": "electricity",
"country_code": "ES",
"currency_code": "EUR",
"frequency": "hourly",
"description": "Day-ahead auction price."
},
{
"symbol": "EPEX_DE_DA",
"name": "EPEX Germany Day-Ahead",
"category": "electricity",
"country_code": "DE",
"currency_code": "EUR",
"frequency": "hourly",
"description": "Day-ahead auction price."
},
{
"symbol": "PVPC_ES_2TD",
"name": "Spain PVPC 2.0TD Retail Reference",
"category": "electricity",
"country_code": "ES",
"currency_code": "EUR",
"frequency": "hourly",
"description": "Regulated retail reference curve."
}
]
}

Poll provider status for runbook automation:

curl -G https://energy-api.com/api/v1/status \
--data-urlencode "api_key=YOUR_API_KEY"

Example JSON response:

{
"success": true,
"providers": [
{ "name": "OMIE", "last_fetch": "2026-06-11T13:05:12Z", "status": "ok" },
{ "name": "ENTSO-E", "last_fetch": "2026-06-11T13:02:33Z", "status": "ok" },
{ "name": "EIA", "last_fetch": "2026-06-11T12:58:00Z", "status": "ok" },
{ "name": "FRED", "last_fetch": "2026-06-11T12:58:45Z", "status": "ok" },
{ "name": "ESIOS", "last_fetch": "2026-06-11T13:01:07Z", "status": "ok" }
]
}

Use cases:

  • symbols lets you populate a symbol picker in your app, filter by category/country_code, and attach metadata like currency_code and frequency.
  • status integrates into your health dashboard, triggers alerts if a source experiences lag, and assists in graceful degradation (e.g., using last known good data).

End-to-End: Computing Hourly Market-Based Scope 2 Emissions

The core calculation for market-based Scope 2 at hourly resolution is straightforward:

  • Obtain your electricity price curve for the delivery day from GET /electricity/hourly.
  • Load your hourly consumption profile (kWh) from metering or an internal system.
  • Obtain grid carbon intensity for your location via GET /carbon-intensity (or your supplier’s certified emission factor if you have a contract-method factor per MWh; the continuous grid intensity is still valuable for marginal analysis and operational visibility).
  • Join by interval boundary and compute emissions per interval: emissions_kg = (kWh_interval × intensity_g_per_kWh) / 1000.
  • Aggregate to daily/monthly totals and store interval details for audit support.

Below is a concise Python snippet that demonstrates the join and calculation logic. It assumes your consumption data is in memory and keyed by the same hourly timestamps as the API responses.

import requests
from datetime import datetime
from decimal import Decimal

BASE = "https://energy-api.com/api/v1"
API_KEY = "YOUR_API_KEY"

def get_hourly_curve(symbol, date):
r = requests.get(f"{BASE}/electricity/hourly", params={
"symbol": symbol,
"date": date,
"api_key": API_KEY
})
r.raise_for_status()
data = r.json()
return data["timezone"], data["curve"], data["currency_code"]

def get_intensity(country):
r = requests.get(f"{BASE}/carbon-intensity", params={
"country": country,
"api_key": API_KEY
})
r.raise_for_status()
return r.json() # contains timezone and 'series'

# Example inputs
symbol = "EPEX_DE_DA"
country = "DE"
date = "2026-06-11"

tz_price, price_curve, cur = get_hourly_curve(symbol, date)
intensity_payload = get_intensity(country)
tz_ci = intensity_payload["timezone"]
ci_series = intensity_payload["series"]

# Assume you have hourly kWh profile keyed by start ISO timestamp in the same TZ:
# Example: consumption_map["2026-06-11T00:00:00+02:00"] = 120.0
consumption_map = {} # fill with your metered data

emissions_total_kg = Decimal("0")
cost_total_eur = Decimal("0")

# Build a quick map for intensity by start time
ci_map = {entry["start"]: Decimal(str(entry["intensity"])) for entry in ci_series}

for interval in price_curve:
start = interval["start"]
price = Decimal(str(interval["price"]))
kwh = Decimal(str(consumption_map.get(start, 0.0)))
intensity_g_per_kwh = ci_map.get(start, Decimal("0"))

# Market-based cost
interval_cost = kwh * price / Decimal("1000") # EUR/MWh × kWh = EUR × (1/1000)
cost_total_eur += interval_cost

# Emissions (kgCO2eq)
interval_kg = (kwh * intensity_g_per_kwh) / Decimal("1000")
emissions_total_kg += interval_kg

print("Cost (EUR):", round(cost_total_eur, 2))
print("Emissions (kgCO2eq):", round(emissions_total_kg, 2))

Practical notes:

  • Always align timezones. Both endpoints return a timezone field—normalize early to avoid DST errors.
  • Use Decimal for currency math if you need cent-level precision. For emissions, Decimal or float may both suffice depending on tolerance.
  • If you operate multiple countries or balancing areas, store a country-to-symbol mapping in your configuration, discovered via GET /symbols.

Real-World Use Cases

1) ESG Dashboard with Hourly Market-Based Emissions and Cost Attribution

Build a portfolio view that shows today’s and tomorrow’s expected cost and emissions side-by-side for each site. Use GET /electricity/hourly for each site’s market symbol, GET /carbon-intensity for local intensity, and GET /forecast to preview tomorrow’s exposure. Roll up to a portfolio summary and let users drill down into the most carbon- and cost-intensive hours to guide load shifting or storage dispatch.

Endpoints used: /electricity/hourly, /carbon-intensity, /forecast, /symbols.

2) Procurement Optimization and Hedging Support

Integrate GET /latest and GET /timeseries for EUA_CO2, TTF_GAS, and BRENT_CRUDE to provide traders and procurement teams with cross-commodity context. Use GET /forecast to anticipate day-ahead price curves and correlate with expected intensity and load. The outcome: evidence-driven contracting, better hedge timing, and policy-ready justifications for renewable procurement strategies.

Endpoints used: /latest, /timeseries, /forecast.

3) Automated Scope 2 Reporting Pipeline with Backfills and Validation

Schedule a daily job that loads yesterday’s electricity curve and intensity, computes interval emissions and cost, and posts results to your data warehouse. Use GET /symbols to maintain your region lists, GET /status for monitoring, and GET /historical or /timeseries to backfill gaps or re-run periods when auditors request re-computations. You get a turnkey, reproducible pipeline that scales across regions without bespoke ETL per provider.

Endpoints used: /electricity/hourly, /carbon-intensity, /historical, /timeseries, /symbols, /status.

Error Handling, Data Quality, and Best Practices

Even with a normalized API, production systems must anticipate edge cases. Here are common scenarios and recommended strategies.

  • Non-publishing days: GET /historical returns the most recent value before a non-publishing date. For deterministic reports, surface the effective date you used and store the dates map when using GET /latest, as those indicate the actual publication dates per symbol.
  • 404 for non-auction symbols in GET /forecast: When a symbol doesn’t support day-ahead auctions, the endpoint returns 404. Implement a fallback to the last available intraday or daily price or present "Not applicable" in the UI.
  • 422 for validation errors: Validate required parameters (e.g., symbol, date, country). For UI forms, constrain inputs based on GET /symbols to prevent invalid combinations.
  • 429 rate-limit responses: Implement exponential back-off and retry with jitter for idempotent GETs. For scheduled jobs, use a staggered start and cached results to minimize burst loads.
  • Timezones and DST: Use the timezone field returned by each endpoint and store interval boundaries. When aggregating, sum on normalized timestamps. For cross-region portfolios, convert to UTC internally and render in local time only when displaying to the user.

Error response format:

{
"success": false,
"error": "Human-readable message."
}

Best practice tips:

  • Cache symbol metadata and provider status. Refresh symbols daily and status every few minutes; degrade gracefully if a provider is temporarily delayed.
  • Use a stable schema in your warehouse (e.g., price_intervals, intensity_intervals) keyed by symbol and start timestamp. This avoids reprocessing the same hours during backfills.
  • When computing emissions, maintain a clear lineage: price_source_symbol, intensity_country, and versioned calculation methods so audits are reproducible even if you later improve methodology.
  • If you combine market-based factors (supplier EF) with grid intensity for marginal analysis, store both results and clearly label them. The two serve different accounting purposes.

Additional Endpoint Patterns You Will Want

Comparing Today vs. Yesterday — GET /fluctuation

To communicate change over a period (e.g., week-over-week EUA_CO2 or TTF_GAS movement), use GET /fluctuation. It returns start_value, end_value, absolute change, and percentage change for each symbol—ideal for summary cards and alerts.

curl -G https://energy-api.com/api/v1/fluctuation \
--data-urlencode "start=2026-06-01" \
--data-urlencode "end=2026-06-11" \
--data-urlencode "symbols=EUA_CO2,TTF_GAS" \
--data-urlencode "api_key=YOUR_API_KEY"

Response fields will include change_pct you can plot alongside emissions intensity shifts to narrate why your costs or emissions moved.

Monthly Context and Volatility — GET /ohlc

For investor-facing summaries or risk sections of ESG reports, you may want monthly candles for commodities that influence power and emissions. GET /ohlc returns open, high, low, close for weekly, monthly, or quarterly periods.

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=2026-06-01" \
--data-urlencode "api_key=YOUR_API_KEY"

Use the resulting candles to frame volatility narratives and connect them to procurement and emissions intensity trends.

Putting It Together: A Minimal JavaScript Service

Below is a compact Node.js service that joins an electricity hourly curve with country carbon intensity and returns interval-level cost and emissions. This is not a full server; it demonstrates request flow and data joins that you can embed into your backend or serverless function.

import fetch from "node-fetch";

const BASE = "https://energy-api.com/api/v1";
const API_KEY = process.env.ENERGY_API_KEY; // provide your key out-of-band

async function getCurve(symbol, date) {
const url = new URL(`${BASE}/electricity/hourly`);
url.searchParams.set("symbol", symbol);
url.searchParams.set("date", date);
url.searchParams.set("api_key", API_KEY);
const r = await fetch(url);
if (!r.ok) throw new Error(`Curve fetch failed: ${r.status}`);
return r.json();
}

async function getIntensity(country) {
const url = new URL(`${BASE}/carbon-intensity`);
url.searchParams.set("country", country);
url.searchParams.set("api_key", API_KEY);
const r = await fetch(url);
if (!r.ok) throw new Error(`Intensity fetch failed: ${r.status}`);
return r.json();
}

// Example join function
export async function computeHourly(symbol, date, country, kwhProfileMap) {
const curve = await getCurve(symbol, date);
const intensity = await getIntensity(country);

const ciMap = new Map(intensity.series.map(x => [x.start, x.intensity]));
const result = [];

for (const row of curve.curve) {
const start = row.start;
const price = row.price; // EUR/MWh
const kwh = kwhProfileMap[start] || 0;
const ci = ciMap.get(start) || 0; // gCO2eq/kWh

const eur = (kwh * price) / 1000.0;
const kg = (kwh * ci) / 1000.0;

result.push({
start,
end: row.end,
price_eur_per_mwh: price,
kwh,
carbon_intensity_g_per_kwh: ci,
cost_eur: Number(eur.toFixed(2)),
emissions_kgco2e: Number(kg.toFixed(3))
});
}

return {
symbol,
date,
currency: curve.currency_code,
timezone: curve.timezone,
unit_intensity: intensity.unit,
intervals: result
};
}

This join pattern covers 90% of Scope 2 reporting needs. You can add PVPC retail references via GET /electricity/pvpc for Spain, include contract-specific emission factors, or switch symbols for other countries without rewriting glue code—thanks to the unified schema.

FAQ

How do I align electricity prices and carbon intensity to the same hours?

Both GET /electricity/hourly and GET /carbon-intensity return timezone fields and interval start/end timestamps. Normalize to a single timezone (often the local grid’s timezone) and join on the start field. For DST days, rely on explicit start and end boundaries rather than assuming 24 intervals.

Can I fetch multiple commodities in one call to build a cross-commodity dashboard?

Yes. Use GET /latest or GET /timeseries with a comma-separated symbols list. For example, request EUA_CO2, TTF_GAS, and BRENT_CRUDE together to correlate power price drivers and narrative context in sustainability and procurement dashboards.

How often does the TTF gas price update, and can I compare it to EU ETS prices?

You can pull the most recent published values with GET /latest and trend them with GET /timeseries. Use shared dates and frequencies fields to validate granularity and confidently compare TTF_GAS against EUA_CO2 in the same charts.

What happens if a day-ahead auction result hasn’t published yet?

GET /forecast provides the next published day-ahead result for auction-sourced symbols; if it is not available, the endpoint may not yet return the new day. Implement a simple retry window, or fall back to the prior day’s published schedule for continuity in pre-compute pipelines.

How can I troubleshoot data gaps or late publications?

Use GET /status to verify provider health and last_fetch timestamps. For historical backfills or re-computations, use GET /historical and GET /timeseries to fill gaps deterministically, and store effective publication dates in your warehouse for audit-ready transparency.

Conclusion + CTA

Hourly, market-based Scope 2 reporting no longer requires duct-taping together heterogeneous feeds from power exchanges, carbon registries, and national data portals. With the Energy category endpoints from Energy API, you can unify electricity intraday curves, grid carbon intensity, and cross-commodity signals behind one stable JSON schema. The result is a clean developer experience, faster time-to-insight, and a reporting stack that withstands audit scrutiny with reproducible, timestamped inputs.

Whether you are building an internal ESG dashboard, a procurement engine, or a customer-facing sustainability product, the implementation patterns in this post—intraday price curves, hourly intensity alignment, and forecast-aware planning—will get you from zero to production quickly. Start integrating the endpoints shown above, automate your joins, and ship value-adding features that help your organization or customers decarbonize with data.

Ready to build? Explore symbols, query carbon intensity for your regions, and wire up hourly electricity curves today. Try Energy API for free and turn your Scope 2 reporting into a rigorous, automated workflow.

Ready to get started?

Get your API key and start querying energy commodity prices in minutes.

Get API Key

Related posts