REST · JSON · v1
One key, six endpoints, no SDK required.
Everything the dashboard draws is available as JSON over HTTPS. Authenticate with a bearer token, pass a range, get back a typed envelope that tells you exactly how fresh the data is and how much quota you have left.
Getting started
Authentication
Every request needs an API key, sent either as a bearer token or in X-API-Key. Keys are shown once when created and stored only as SHA-256 hashes — if you lose one, issue a new key and revoke the old. We cannot recover it for you, by design.
curl https://ercot.tonal-data-analytics.com/api/v1/prices \ -H "Authorization: Bearer ek_live_YOUR_KEY" # equivalently curl https://ercot.tonal-data-analytics.com/api/v1/prices \ -H "X-API-Key: ek_live_YOUR_KEY"
The delay is the product boundary
Free keys receive the same data, held back 60 minutes from the moment we ingest it. The delay applies to publication time, not delivery time — so tomorrow’s cleared day-ahead prices and the seven-day load forecast are still present on a free key, just an hour after a paid key saw them.
Read meta.realtime rather than inferring freshness from timestamps.
"meta": {
"range": "24h",
"generatedAt": "2026-07-27T18:04:11.220Z",
"plan": "free",
"realtime": false,
"delayMinutes": 60,
"historyDays": 1
}Reference
Endpoints
openapi.jsonBase URL https://ercot.tonal-data-analytics.com/api/v1. All endpoints accept ?range=24h|7d|30d, clamped to the history depth your plan allows.
| Endpoint | Returns | ERCOT product |
|---|---|---|
| GET /prices | Real-time and day-ahead settlement point prices | NP6-905-CD, NP4-190-CD |
| GET /load | Actual system load against the current forecast vintage | NP6-345-CD, NP3-565-CD |
| GET /renewables | Wind and solar, actual against forecast, plus generation mix | NP4-732-CD, NP4-737-CD |
| GET /ancillary | Day-ahead ancillary service clearing prices | NP4-188-CD |
| GET /congestion | Binding transmission constraints by shadow price | NP6-86-CD |
| GET /snapshot | Current headline market figures | multiple |
Quota & rate limits
Every response carries your quota state, so you never need a separate call to check it.
X-Quota-Limit: 1000 X-Quota-Used: 26 X-Quota-Remaining: 974
Exceeding the per-minute ceiling returns 429 with Retry-After. Rate-limited requests do not consume monthly quota — only successful responses are counted — so a retry storm costs you nothing but a wait.
Errors
Errors return a JSON body with a stable code. Branch on that, not on the human-readable message.
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_api_key | No key supplied. |
| 401 | invalid_api_key | Unknown or revoked key. |
| 429 | rate_limit | Per-minute ceiling hit. Retry after the header says. |
| 429 | monthly_quota | Monthly allowance spent. Upgrade or wait for reset. |
| 500 | internal_error | Our fault — not billed. |
Limits by plan
See pricingRead from the same records the API enforces, so this table cannot drift from reality.
| Plan | Delay | History | Calls / month | Requests / min |
|---|---|---|---|---|
| Free | Real-time | 30 d | 1,000 | 10 |
| Starter | Real-time | 90 d | 50,000 | 60 |
| Pro | Real-time | 730 d | 500,000 | 300 |
| Enterprise | Real-time | 3650 d | Unmetered | 1,200 |