Home / Blog / Energy retailer 101: NEM dispatch prices via API
2026-05-20 · Harry Vass
Energy retailer 101: NEM dispatch prices via API
A walkthrough of pulling AEMO NEM 5-minute dispatch prices via API, for energy analysts who need cross-domain context, not deep market analytics.
> Up front, before the recipe: if you're an energy analyst doing serious NEM market analysis, you should be using NEMOSIS (UNSW CEEM, free, the canonical Python toolkit for AEMO data) or OpenElectricity (formerly OpenNEM, web + API, beautiful UI, deep dispatch-stack analytics). Both are mature, well-maintained, and built specifically for NEM workflow. This post is for the cross-domain case, when you want NEM dispatch prices alongside CPI, real wages, and ABS retail energy spend in the same API call. That's the gap ausdata.io fills.
The NEM (National Electricity Market) clears dispatch every 5 minutes across five regional reference nodes, NSW1, QLD1, SA1, TAS1, VIC1. Wholesale prices fluctuate from the floor ($-1,000/MWh, paid by generators to consume during oversupply) to the cap ($17,500/MWh, charged in extreme scarcity). For an energy retailer, getting yesterday's price curve into a dashboard takes a tour through AEMO's NEMWeb CSVs.
For a cross-domain analyst, someone correlating wholesale power with CPI energy components, or tying retail margins to ABS household energy spend, the toolchain is more painful: AEMO CSVs in one shape, ABS SDMX in another, RBA in yet another. ausdata.io's /v1/data/aemo/trading_price give you the AEMO side of that join in the same envelope as everything else.
<!-- IMG: nem-dispatch-5min-chart.png -->
What's actually in /v1/data/aemo/trading_price
import requests
r = requests.get(
"https://api.ausdata.io/v1/data/aemo/trading_price",
headers={"X-API-Key": "ak_..."}, timeout=30,
).json()
print(r["data"])
Output:
{
"data": {
"window_start": "2026-05-19T00:00:00+10:00",
"window_end": "2026-05-20T00:00:00+10:00",
"nsw1_24h_avg_aud_mwh": 84.3,
"qld1_24h_avg_aud_mwh": 71.5,
"sa1_24h_avg_aud_mwh": 92.1,
"tas1_24h_avg_aud_mwh": 62.8,
"vic1_24h_avg_aud_mwh": 88.7,
"nem_total_demand_mwh": 552_103,
"renewable_share_pct": 39.2,
"cpi_energy_yoy_pct": 4.1
},
"meta": {
"sources": [
{"name": "AEMO",
"url": "https://aemo.com.au/energy-systems/electricity/national-electricity-market-nem/data-nem/aggregated-data",
"attribution": "© AEMO, CC-BY 4.0"},
{"name": "ABS CPI",
"url": "https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia",
"attribution": "© Commonwealth of Australia (ABS), CC-BY 4.0"}
]
}
}
24-hour averages across all five regions, NEM-wide demand, renewable share, and the ABS Energy CPI annual change side-by-side. That's the cross-domain context shot in one call.
Step 1, pull live 5-minute dispatch for one region
import requests
API_KEY = "ak_..."
r = requests.get(
"https://api.ausdata.io/v1/data/aemo/trading_price",
headers={"X-API-Key": API_KEY},
params={"region": "NSW1", "hours": 24},
timeout=30,
).json()
# Last 288 5-min intervals (24h × 12 intervals/hour)
print(f"Returned {r['meta']['row_count']} intervals")
for row in r["data"][-5:]:
print(f"{row['settlementdate']} ${row['rrp']:>7.2f}/MWh "
f"demand: {row['totaldemand']:>6.0f} MW")
Output:
Returned 288 intervals
2026-05-19T23:40:00+10:00 $ 74.50/MWh demand: 6420 MW
2026-05-19T23:45:00+10:00 $ 72.10/MWh demand: 6298 MW
2026-05-19T23:50:00+10:00 $ 68.95/MWh demand: 6150 MW
2026-05-19T23:55:00+10:00 $ 65.80/MWh demand: 5994 MW
2026-05-20T00:00:00+10:00 $ 62.30/MWh demand: 5847 MW
rrp is the Regional Reference Price, what generators receive and retailers pay at the regional reference node. Demand is in MW (average over the 5-min interval).
Step 2, quick sanity-check chart
import matplotlib.pyplot as plt
from datetime import datetime
times = [datetime.fromisoformat(r["settlementdate"]) for r in r["data"]]
prices = [r["rrp"] for r in r["data"]]
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(times, prices, linewidth=0.8)
ax.set_ylabel("RRP (AUD/MWh)")
ax.set_title("NSW1 dispatch price, last 24h")
ax.axhline(0, color="grey", linewidth=0.5)
plt.tight_layout()
plt.savefig("nsw1_24h.png", dpi=120)
You'll see the canonical NEM shape: a duck-curve sag around midday (rooftop solar drives wholesale into single digits, sometimes negative), an evening peak as solar drops out and aircon ramps up, then overnight base.
Step 3, the cross-domain join (where we add value)
This is the bit NEMOSIS and OpenElectricity don't do, because it's not what they're built for. They give you market depth, bid stacks, FCAS components, generator-by-generator. We give you dispatch prices alongside non-energy macro context:
import requests
API_KEY = "ak_..."
def fetch(path, **params):
r = requests.get(f"https://api.ausdata.io{path}",
headers={"X-API-Key": API_KEY},
params=params, timeout=30)
r.raise_for_status()
return r.json()["data"]
snap = fetch("/v1/data/aemo/trading_price")
cpi = fetch("/v1/data/abs/CPI", limit=1)[0]
wages = fetch("/v1/real-wages", limit=1)[0]
# NSW1 24h average wholesale vs. CPI energy component vs. real wages
print(f"NSW1 wholesale 24h avg: ${snap['nsw1_24h_avg_aud_mwh']}/MWh")
print(f"ABS Energy CPI YoY: {snap['cpi_energy_yoy_pct']}%")
print(f"All-items CPI: {cpi['value']}%")
print(f"Real wages YoY: {wages['real_wages_yoy_pct']:+.1f}%")
The story this lets you tell quickly: "Yesterday's NSW1 wholesale averaged $84/MWh, compared to all-items inflation at 2.8% YoY, energy CPI is running at 4.1%, and real wages are barely positive at +0.4%". That's a sentence in a household-energy-affordability article, and the API call to produce it was three lines.
What this isn't
This endpoint is not:
- A bid-stack feed. Generator-by-generator bid data lives in AEMO MMS_DATA tables and is well-served by NEMOSIS.
- An FCAS price feed. Ancillary services prices have their own dataset; we expose dispatch energy only.
- A constraint-aware dispatch model. We give you what cleared, not what would have cleared without binding constraints.
- Historical depth back to 1998. We expose the last 7 days of 5-min dispatch and aggregates back to 2020 monthly. For longer historical research, use NEMOSIS, it can pull the entire NEM history into a Pandas DataFrame and is what every PhD thesis on AEMO is built on.
- WEM (Western Australia) data. WEM is a separate market with separate AEMO data feeds, not yet exposed via our API.
If your job title is "energy market analyst", the right next step is reading the NEMOSIS docs and the OpenElectricity docs. They're built for you. Come back to us when you want NEM context in a non-NEM dashboard.
Real-world cross-domain use cases
The cases where ausdata.io's NEM endpoints make sense:
- Household-affordability journalism. Wholesale price alongside CPI, ABS retail energy spend, and household income.
- Retail-energy churn forecasting. Wholesale price level + real-wages stagnation as proxies for stress-induced switching.
- Macro-research on inflation pass-through. Wholesale → retail tariff lag against CPI energy component.
- ESG / DEI dashboards. Renewable share trend alongside national emissions reporting (we don't have emissions yet, on roadmap).
These are all cases where you'd rather make one API call to one provider than glue three open-source toolkits together.
Pricing
- Free: 500 calls/month.
/v1/data/aemo/trading_pricereturning 288 intervals counts as 1 call. So 500 dashboard refreshes/month. - Analyst: $29/mo, 10k calls, fine for an hourly cross-domain dashboard.
- Pro: $99/mo, 100k calls + webhooks, for a SaaS product pushing alerts on price-cap or floor events.
Free key at ausdata.io.