API documentation
US Electricity Prices API
Since 2001What a kilowatt-hour actually costs, by state and by sector: the official EIA retail series with price, consumption, revenue and customer counts — the four figures together, because a price only means something next to the sales that produced it.
500 req/mo · No credit card required · One key, every API on the platform
- Checking status…
- Monthly since January 2001
- 50 states + DC, plus national and census-region aggregates
- 6 sectors · 4 measures per point
- Release: monthly, about three months after the fact
Base URL & authentication
All endpoints live under one base URL. Authenticate every request with your key
in the X-Api-Key header — no OAuth, no signing. The same key works
on every API of the platform.
# Base URL {{GATEWAY_URL}} # Every request X-Api-Key: sk_live_YOUR_KEY
Missing or invalid keys return 401 — see
errors. The demo endpoint is the only keyless route.
Endpoint
One endpoint, filtered by query parameters. Every 200 ships
ETag and Cache-Control; points come
newest first.
| Method | Path | Summary |
|---|---|---|
| GET | /electricity/retail-sales Free+ | Price, consumption, revenue and customers for a location and sector |
Sample — 200 /electricity/retail-sales?location=texas§or=residential&limit=2
{
"data": [
{
"location": "texas",
"location_code": "TX",
"location_name": "Texas",
"location_is_aggregate": false,
"sector": "residential",
"sector_is_aggregate": false,
"period": "2026-05",
"price_cents_per_kwh": 16.44,
"sales_million_kwh": 12822.49699,
"revenue_million_usd": 2107.48852,
"customers": 12903067,
"source": "Source: U.S. Energy Information Administration (August 2026)"
}
],
"meta": {
"count": 2,
"location": "texas",
"location_is_aggregate": false,
"sector": "residential",
"frequency": "monthly",
"next_cursor": "2026-03"
}
}
Real response, trimmed to one point. Every numeric field is a JSON number — the upstream source returns all of them as strings.
Parameters
| Name | Default | Accepted values |
|---|---|---|
location |
us |
A state slug (texas), its two-letter code (TX) or its name
(Texas) — all three resolve to the same series. Also the national total
(us) and the ten census regions (new-england,
middle-atlantic, east-north-central,
west-north-central, south-atlantic,
east-south-central, west-south-central,
mountain, pacific-contiguous,
pacific-noncontiguous). |
sector |
all |
residential · commercial · industrial ·
transportation · other · all |
frequency |
monthly |
monthly · quarterly · annual |
limit |
24 |
1–500 points per page |
cursor |
— | A period from meta.next_cursor. Returns points at or before it. |
Aggregates are flagged, not hidden. The national total and the census
regions are real series and you can query them, but they already contain the states:
adding texas to west-south-central
counts Texas twice. Both come back with
location_is_aggregate: true, and the sector
all with
sector_is_aggregate: true, so your code can tell them apart
without a lookup table.
Pagination
Keyset by period, newest first. Ask for a page, and if there is more,
meta.next_cursor carries the period to continue from; on the last
page it is null.
# first page GET /electricity/retail-sales?location=california&limit=2 # -> meta.next_cursor = "2026-03" # next page GET /electricity/retail-sales?location=california&limit=2&cursor=2026-03
No offsets, so a page never shifts under you when the source publishes a new month.
Errors, auth & limits
One uniform error contract:
{ "error": { "code", "message" }, "source" } with stable,
machine-readable codes. The source attribution travels in error
bodies too, and errors are never cached.
| Status | Code | When |
|---|---|---|
| 400 | UNKNOWN_LOCATION | The location is not one we serve. The body lists every valid value in valid_values. |
| 400 | UNKNOWN_SECTOR | Unknown sector, with the valid ones listed. |
| 400 | UNKNOWN_FREQUENCY | A frequency the source does not publish. |
| 400 | INVALID_LIMIT | limit is not an integer between 1 and 500. |
| 400 | INVALID_CURSOR | The cursor is not a period of the requested frequency. |
| 404 | NOT_PUBLISHED_YET | The filters are valid but the source has published nothing for them. A different thing from a bad filter, and said differently. |
| 401 | UNAUTHORIZED | Missing X-Api-Key header, or invalid/revoked key. |
| 429 | RATE_LIMITED | Per-minute limit of your plan exceeded — check Retry-After. |
| 429 | QUOTA_EXCEEDED | Monthly cap reached. Nothing is billed on top — resets on the 1st (UTC). |
| 502 | UPSTREAM_ERROR | The origin API was unreachable — retry shortly. |
Sample — 400 ?location=texa
{
"error": {
"code": "UNKNOWN_LOCATION",
"message": "Unknown location 'texa'. Use a state slug, a two-letter code, or a state name.",
"valid_values": ["alabama", "alaska", "arizona", …]
},
"source": "Source: U.S. Energy Information Administration (August 2026)"
}
Typos are answered, not swallowed: the upstream source replies
200 with an empty array for a state that does not exist, which is
indistinguishable from "no data yet". We validate first, so the two cases stay separate.
Data provenance
The U.S. Energy Information Administration retail sales series
(electricity/retail-sales, form EIA-861M): electricity sold to
ultimate customers, by state and sector. Official US government data, public domain,
refreshed on each monthly release — which lands about three months after the period it
describes. This product is not endorsed by or affiliated with the EIA.
Two things about missing numbers, because they are the source's answer and not ours:
A null means unpublished
Never 0
Customer counts, for example, are null for every month
before 2008 — the EIA did not publish them. A measure that does not exist comes back as
null, never as a zero and never interpolated.
A 0 means no sales
Also real
Where a sector has no activity in a state, the source reports 0
across all four measures — so a price of 0 means there were no
sales to price, not free electricity. Filter on
sales_million_kwh > 0 before averaging.
Example
curl -H "X-Api-Key: sk_live_YOUR_KEY" \ "https://api.synergicapis.com/electricity/retail-sales?location=texas§or=residential"
import requests
r = requests.get(
"https://api.synergicapis.com/electricity/retail-sales",
headers={"X-Api-Key": "sk_live_YOUR_KEY"},
params={"location": "texas", "sector": "residential"},
)
r.raise_for_status()
latest = r.json()["data"][0]
print(latest["period"], latest["price_cents_per_kwh"], "cents/kWh")
const url = new URL("https://api.synergicapis.com/electricity/retail-sales"); url.searchParams.set("location", "texas"); url.searchParams.set("sector", "residential"); const res = await fetch(url, { headers: { "X-Api-Key": "sk_live_YOUR_KEY" } }); const { data } = await res.json(); console.log(data[0].period, data[0].price_cents_per_kwh);
Live demo
Real request against the production API — no key needed here (demo endpoint, 5 requests/minute shared across every demo on the site). Returns the US national total.
Press the button to fetch the latest official figures.