API reference
Quickstart
Vatio gives you clean, normalized US grid data — prices, demand and the full fuel mix — for
dashboards, price-aware automation, storage/BESS analytics, forecasting and research, without
wrangling seven different operator feeds. It's REST over HTTPS; every response is JSON (CSV on
history endpoints). One uniform shape covers all seven US grids — swap the {iso}
path segment. The base URL is http://localhost:8000. Grab your API key
from the dashboard,
then send it on every request as the X-API-Key header.
# a live snapshot, no account required (15-min delayed) $ curl http://localhost:8000/v1/sample
# create a free key in the dashboard, then pull any grid $ curl http://localhost:8000/v1/caiso/lmp/realtime?hub=SP15 \ -H "X-API-Key: vt_live_…"
The machine-readable schema is at /openapi.json.
Authentication
Every request is authenticated with your secret API key, sent as the
X-API-Key header:
X-API-Key: vt_live_…
Create, reveal and revoke keys in your dashboard — each key is shown once, so store it securely. Keep it secret: anyone with your key can spend your quota.
Plans & limits
Your plan controls four things: throughput (requests per second — the main lever), how fresh the data is (free is 15-minutes delayed; paid is live), how far back history goes, and a generous monthly fair-use cap. See pricing.
| Plan | Price | Rate | Data | History | Calls / mo |
|---|---|---|---|---|---|
free | $0 | 5 / s | 15-min delayed | 30 days | 10,000 |
pro | $29 | 30 / s | Live | 2 years | Fair use |
Track your month-to-date usage any time in your dashboard.
Errors & rate limits
Standard HTTP status codes; the body is always
{"detail": "…"}. Exceed your per-second rate and you'll get
429 — back off and retry.
| Code | Meaning |
|---|---|
400 | Bad request — e.g. an unknown hub. |
401 | Missing or invalid API key / session token. |
402 | Pro feature (nodal LMP, DART, spreads) requested with a Free key. |
404 | No data available yet for that query. |
422 | Invalid parameters (e.g. password under 8 chars). |
429 | Rate limit exceeded for your plan. |
503 | Billing not configured (Stripe keys absent). |
Every authenticated data response carries your limits in headers:
X-RateLimit-Limit-Second (your per-second rate), X-RateLimit-Limit
(monthly cap) and X-RateLimit-Remaining — poll them instead of guessing.
Grids & coverage
Every data endpoint is the same shape across grids — just swap the {iso}
path segment. Authenticate with your API key (X-API-Key). Timestamps are UTC;
prices are $/MWh, power is MW. Free callers see data ≥ 15 min old.
{iso} | Operator | Prices (LMP) | Nodal + DART | Demand | Fuel mix |
|---|---|---|---|---|---|
caiso | California ISO | ✅ 5-min | ✅ RT + DA | ✅ 5-min | hourly |
nyiso | New York ISO | ✅ 5-min | soon | hourly | hourly |
ercot | ERCOT (Texas) | rolling out | soon | hourly | hourly |
pjm | PJM | rolling out | soon | hourly | hourly |
miso | Midcontinent ISO | rolling out | soon | hourly | hourly |
isone | ISO New England | rolling out | soon | hourly | hourly |
spp | Southwest Power Pool | rolling out | soon | hourly | hourly |
Data sources & cadence
Prices (LMP) come from each operator's own real-time feed and are live for
CAISO and NYISO today (5-minute intervals). The other grids return
404 on price endpoints until their feed lands — create a key now and they switch
on with no migration. Demand and the full fuel mix come from the U.S. EIA-930
feed, updated hourly for every grid (CAISO demand is 5-minute, from its native
feed). EIA publishes with a few hours' lag, so treat demand and fuel mix as an hourly series,
not a sub-minute tick. Hubs per grid are listed by
/v1/public/isos.
Prices (LMP)
Latest real-time price (RTD LMP) for a hub.
| Param | Type | Notes |
|---|---|---|
hub | string | Defaults to the ISO's primary hub. CAISO: NP15, SP15, ZP26. |
$ curl http://localhost:8000/v1/caiso/lmp/realtime?hub=SP15 \ -H "X-API-Key: vt_live_…"
200{
"iso": "caiso",
"hub": "SP15",
"market": "RTD",
"interval_start": "2026-06-27T18:05:00Z",
"price": -15.63,
"unit": "$/MWh"
}
Historical price series for a hub, oldest-first. Window is clamped to your plan's history depth.
| Param | Type | Notes |
|---|---|---|
hub | string | Defaults to the ISO's primary hub. |
start | ISO-8601 | Default: 24h before end. |
end | ISO-8601 | Default: now (minus plan delay). |
limit | int | 1–5000, default 500. |
format | string | json (default) or csv. |
200{
"iso": "caiso",
"hub": "SP15",
"market": "RTD",
"unit": "$/MWh",
"delayed_minutes": 0,
"count": 2,
"data": [
{
"interval_start": "2026-06-27T18:20:00Z",
"price": -16.71
},
{
"interval_start": "2026-06-27T18:25:00Z",
"price": -17.04
}
]
}
Demand
Latest system demand (actual + forecast), in MW. interchange_mw is net
interchange with neighboring grids — EIA sign: positive = net exports, negative =
net imports (net imports are why generation ≠ demand). Hourly via EIA-930.
200{
"iso": "ercot",
"interval_start": "2026-06-27T18:00:00Z",
"actual_mw": 67160.0,
"forecast_mw": 66890.0,
"interchange_mw": -1240.0,
"unit": "MW"
}
Historical demand series. Same start / end / limit / format params as /lmp/history.
200{
"iso": "ercot",
"unit": "MW",
"delayed_minutes": 0,
"count": 168,
"data": [
{
"interval_start": "2026-06-27T17:00:00Z",
"actual_mw": 66740.0,
"forecast_mw": 66510.0
}
]
}
Generation (fuel mix)
Generation by fuel type, in MW — the full mix (gas, nuclear, coal, hydro, wind, solar,
battery, oil, other), hourly for every grid. Every response also carries derived metrics built
for storage and forecasting work: renewable_mw / renewable_pct
(solar + wind), net_load_mw (demand − solar − wind — the net-load curve), and
carbon_intensity in gCO₂/kWh.
Latest generation mix for a grid.
200{
"iso": "ercot",
"interval_start": "2026-06-27T16:00:00Z",
"mix": {
"nuclear": 4900,
"coal": 8200,
"gas": 38000,
"wind": 9100,
"solar": 14500,
"battery": 1200,
"other": 600
},
"total_mw": 76500,
"renewable_mw": 23600,
"renewable_pct": 30.8,
"net_load_mw": 43560,
"carbon_intensity": 320.8,
"unit": "MW"
}
Net load = demand − (solar + wind), the series most forecasters and battery operators model against. Carbon intensity is direct fossil-combustion CO₂ (coal, gas, oil) per MWh of total generation, in gCO₂/kWh — non-fossil sources count as zero. Both appear on the history endpoint too, and as columns in the CSV.
Historical fuel-mix series. Accepts start / end / limit / format=csv (one column per fuel).
200{
"iso": "ercot",
"fuels": [
"nuclear",
"coal",
"gas",
"wind",
"solar",
"battery",
"other"
],
"unit": "MW",
"delayed_minutes": 0,
"count": 24,
"data": [
{
"interval_start": "2026-06-27T15:00:00Z",
"mix": {
"gas": 37500,
"wind": 9400,
"solar": 15200
},
"renewable_mw": 24600,
"net_load_mw": 42100,
"carbon_intensity": 259.7
}
]
}
Renewables (solar + wind)
A convenience view of the two variable renewables, derived from the fuel mix.
200{
"iso": "caiso",
"interval_start": "2026-06-27T18:05:00Z",
"solar_mw": 19672.0,
"wind_mw": 3261.0,
"unit": "MW"
}
Historical solar + wind series. Same params as /lmp/history (incl. format=csv).
Nodal & trader data Pro
Node-level LMP with its full component breakdown (energy, congestion, loss, and CAISO's
GHG adder), day-ahead prices, the DART spread, and cross-location spreads — the data
batteries (BESS) and traders actually act on. Nodal coverage today: CAISO
(trading hubs + default load-aggregation points). Node discovery is open on any plan;
nodal prices, DART and spreads require Pro (else 402).
Find the pricing node your asset sits on.
| Param | Type | Notes |
|---|---|---|
search | string | Case-insensitive match on node id or name. |
limit | int | 1–1000, default 100. |
200[
{
"iso": "caiso",
"node_id": "TH_SP15_GEN-APND",
"name": "SP15 Trading Hub",
"zone": "SP15",
"node_type": "HUB"
}
]
Latest nodal LMP with components. price = energy + congestion + loss + ghg.
| Param | Type | Notes |
|---|---|---|
node | string | Required. A PNode id from /nodes. |
market | string | RTM (5-min, default) or DAM (hourly day-ahead). |
200{
"iso": "caiso",
"node_id": "TH_SP15_GEN-APND",
"market": "RTM",
"interval_start": "2026-07-01T12:10:00Z",
"price": 13.4,
"energy": 12.06,
"congestion": 1.66,
"loss": -0.32,
"ghg": 0.0,
"unit": "$/MWh"
}
Nodal series with components, oldest-first. Same node/market params,
plus start/end/limit/format like /lmp/history.
The DART spread — each real-time interval matched to its hour's day-ahead price.
spread = real_time − day_ahead.
| Param | Type | Notes |
|---|---|---|
node | string | Required. A PNode id. |
start/end/limit/format | — | As /lmp/history. |
200{
"iso": "caiso",
"node_id": "TH_SP15_GEN-APND",
"unit": "$/MWh",
"count": 1,
"data": [
{
"interval_start": "2026-07-01T12:00:00Z",
"day_ahead": 20.12,
"real_time": 13.69,
"spread": -6.43
}
]
}
Latest price spread between two nodes — same or cross-ISO. spread = price_a − price_b.
| Param | Type | Notes |
|---|---|---|
iso_a, a | string | ISO + node id of location A. |
iso_b, b | string | ISO + node id of location B. |
market | string | RTM (default) or DAM. |
200{
"market": "RTM",
"interval_start": "2026-07-01T12:10:00Z",
"a": "caiso:TH_NP15_GEN-APND",
"b": "caiso:TH_SP15_GEN-APND",
"price_a": 14.87,
"price_b": 13.4,
"spread": 1.47,
"unit": "$/MWh"
}
Derived grid signals for every ISO (open to any plan): net-load ramp, renewable share, and carbon intensity — the drivers behind price moves.
200{
"iso": "caiso",
"interval_start": "2026-07-01T06:00:00Z",
"demand_mw": 27134.0,
"net_load_mw": 22530.0,
"net_load_ramp_mw_per_hr": -2007.0,
"renewable_mw": 4604.0,
"renewable_pct": 25.3,
"carbon_intensity": 20.7
}
Output formats
All responses are JSON by default. Every */history endpoint also accepts
?format=csv for a download that drops straight into pandas or a spreadsheet:
$ curl http://localhost:8000/v1/ercot/generation/history?format=csv \ -H "X-API-Key: vt_live_…" -o ercot_generation.csv
CSV columns mirror the JSON fields (for fuel mix, one column per fuel).
Paginating long windows
History endpoints return up to 5000 rows per call (limit, default
500). For a longer window, page by time: request a range, then pass the last
interval_start you got back as the next call's start, repeating until
you receive fewer than limit rows. For bulk pulls, ?format=csv is the
fastest path into pandas or a spreadsheet.
Preview endpoints (no key)
These power the public live dashboard and landing demo. They're 15-minute delayed and need no key — handy for a quick look before you sign up.
Catalog of grids for the dashboard picker — slug, code, hubs, and whether prices are live.
Snapshot plus series for prices, demand and the fuel mix for one grid, in a single call. iso = any slug, hours = 1–168.
One metric (demand or price) across every grid, for the compare chart.
CAISO landing snapshot: hub prices plus demand, solar and wind.