Methodology
Every dollar figure published from this dataset is produced by the code in
scripts/, from the sources named below, using the assumptions stated here.
Where we model rather than measure, this document says so.
Sources
| Source | What it provides | Access | License |
|---|---|---|---|
| OpenEI URDB | Every US electricity tariff on file: rate structures, tier ladders, TOU schedules, fixed and minimum charges | Bulk CSV, no API key | Public / CC-BY-style attribution to OpenEI |
| EIA-861 | Utility service territory (state, county) and residential revenue/sales for benchmark average prices | Annual ZIP, no API key | US Government public domain |
| NREL PVWatts | Modeled solar production by location and array configuration | REST API, free key | US Government public domain |
The NREL developer host moved from developer.nrel.gov to developer.nlr.gov
on 2026-05-29. Code targets the current host.
Scope
We publish active residential tariffs only: sector == Residential and no
enddate. A rate with an end date has been superseded and would misprice a
current bill. At the 2026-09 snapshot that is 6,357 rates across 2,589
utilities in 52 states and territories.
Reading a URDB rate correctly
Three properties of the URDB format are easy to get wrong. All three are
handled in scripts/tariff.py and covered by tests in tests/test_tariff.py.
1. adj is additive to rate. The price actually charged for a tier is
rate + adj. The adj field carries fuel and rider adjustments and is
populated on roughly 4,700 tier cells. Georgia Power's TOU-RD-11 off-peak
energy charge is 0.01467 + 0.04454 = 0.05921 $/kWh; reading rate alone
understates it by a factor of four.
2. Tier ceilings are not always monthly. A tier max is expressed in
either kWh (per billing month) or kWh daily, in which case the monthly
threshold is max x days in month and therefore differs between January and
February. About 1,100 tier cells use the daily form.
3. Charges use mixed periods. Fixed and minimum charges appear as
$/month, $/day or $/year, and are normalized to a monthly figure
($/day x 365 / 12) before anything is compared across utilities.
What we refuse to estimate
A tariff is marked not billable and is excluded from every published dollar figure when it cannot be priced from consumption alone:
- it carries demand charges, which need the customer's peak kW (94 rates);
- its tier ceilings are indexed to demand or motor rating (
kWh/kW,kWh/hp); - it is time-of-use but its schedule matrix is missing or malformed;
- it has no energy rate structure at all.
At the 2026-09 snapshot, 6,013 of 6,357 rates (94.6%) are billable. The remainder are published as reference pages showing the tariff, with no estimated bill.
Rate vintage — the most important caveat on this site
A tariff in the URDB with no enddate is not necessarily in force. It means nobody
recorded an end date. NREL re-verifies roughly 150 utilities a year — about 70% of
US electricity load — and everything outside that set keeps whatever it was last
filed with, indefinitely.
The effect is large. Of the 6,357 active residential tariffs in the 2026-09 snapshot:
| Filed | Tariffs | Median deviation from what the utility actually collected |
|---|---|---|
| 2025–2026 | 637 | +6.9% |
| 2023–2024 | 111 | +0.8% |
| 2015–2019 | 701 | −16.5% |
| before 2015 | 1,717 | −16.8% |
| undated | 123 | −20.4% |
The comparison is against EIA-861 residential revenue divided by residential sales for the same utility — what that utility genuinely billed, per kWh, on average. Recent filings land within a few percent. Filings more than a decade old run about 17% low, which is roughly what a decade of price increases would predict.
Only 154 of 2,589 utilities have any tariff filed in 2024 or later.
What we do about it
Every published figure carries its filing year, and rankings are computed only across tariffs filed since 2020. Ranking across all vintages would put stale data at the top of every "cheapest" table, because being out of date and being cheap look identical in the arithmetic.
The effect on a real state is stark. In Georgia, the cheapest residential tariff is $614/year across all filings — from a 2018 filing — and $1,224/year across those filed since 2020. Ranking without regard to vintage would have published the wrong answer by a factor of two.
Older tariffs remain visible in the calculator, labeled with their filing year and behind a filter that is on by default, because the tariff itself is still a useful reference. They are never ranked and never given their own page.
What this means for scope
Applying the floor, we publish 911 tariffs from 183 utilities out of 6,357 cataloged. That is a much smaller site than the raw catalog would support, and deliberately so — the alternative is thousands of pages quoting a decade-old filing as a current price.
Those 183 utilities are not a random sample: they are the ones NREL re-verifies, which are the large ones, covering roughly 70% of US electricity load. So the coverage loss in pages is far larger than the coverage loss in people.
The improvement is measurable. Against EIA's actual residential revenue per kWh for the same utility:
| Basis | Tariffs | Median deviation |
|---|---|---|
| all vintages | 3,334 | −13.2% |
| filed since 2020 | 793 | +4.9% |
The remaining spread is expected and not a defect: a single tariff priced at 1,000 kWh a month legitimately differs from a utility's blended average across every residential tariff and every consumption level. The systematic bias is what the vintage floor removes.
Which utilities are maintained, and which are not, is published at /coverage/.
Load profile — this part is modeled
Splitting consumption across time-of-use periods requires knowing when
electricity is used, which the URDB does not contain. We apply a documented
synthetic residential profile (default_load_shape() in scripts/tariff.py):
a 12x24 month-by-hour weighting with an overnight trough, a morning ramp, an
evening peak, and seasonal amplification of the afternoon peak in summer and
winter.
This is a model, not metered data. For a flat or tiered rate it has no effect on the result. For a time-of-use rate it materially affects the answer, and a household whose usage is unusually concentrated on- or off-peak will see a different bill. Every TOU figure we publish is labeled accordingly.
Period allocation also counts real weekday and weekend days per month, because most TOU schedules price weekends entirely off-peak.
Solar payback
Production
Production comes from NREL PVWatts v8 for one representative location per state — the largest metropolitan area — modeling a south-facing roof-mounted array at 20 degrees tilt, standard module efficiency and PVWatts' default 14% system losses. Output is stored normalized per kW and scales linearly with system size.
This is a state-level figure. Solar resource varies substantially within a state, and shading, orientation and roof pitch move it further. It is not a survey of anyone's roof.
Timing, and why it is modeled rather than fetched
Netting solar against consumption needs hourly production, but PVWatts' hourly endpoint is heavily rate limited. We therefore take monthly totals from PVWatts — authoritative, including local cloud and haze — and derive the hourly distribution from solar geometry, which is deterministic, scaling it so each month's hours sum exactly to the PVWatts monthly figure.
The geometry runs in local clock time, not solar time. This matters more than it sounds: solar noon coincides with clock noon only on a timezone's central meridian, and Atlanta sits 9.4 degrees west of the Eastern meridian, putting solar noon at about 12:38. A model built in solar time peaks over an hour early, which on a time-of-use tariff assigns production to the wrong price period.
The modeled shape is validated against a genuine 8,760-hour PVWatts pull for
Atlanta, held in tests/fixtures/. Applying the clock-time correction cut mean
monthly deviation from 0.183 to 0.080, and the modeled peak hour now matches the
real one in nine months of twelve and is within an hour in the rest. The residual
is weather asymmetry — summer afternoons cloud over more than mornings — which
geometry cannot capture and the PVWatts monthly totals already account for in
aggregate.
Netting
Production and consumption are netted hour by hour, not monthly. In each hour, the smaller of the two is self-consumed, any surplus is exported, and any shortfall is imported and priced at that hour's tariff period. Monthly netting would materially overstate the saving on a time-of-use tariff, because solar produces at midday and households consume in the evening.
Exports — the largest uncertainty
Roughly half of a typical system's output is exported rather than used on site, so the credit for exported kWh often matters more than the tariff does.
- Where the tariff files a sell rate, we use it and report a single figure.
- Where it does not, but the utility reports residential net metering to EIA-861, we report a range spanning no export credit at all to full retail netting. Which applies depends on state rules we do not hold, and asserting either bound as the answer would be a guess.
- Where neither is true, exports are credited at zero and we say so.
753 of 2,589 utilities report residential net-metering installations.
Federal tax credit
Section 25D was terminated on December 31, 2025. A residential system purchased in 2026 receives no federal tax credit — not a reduced one. The business credit (48E) remains available through 2027 for leased and power-purchase-agreement systems, claimed by the system's owner rather than the householder. Published figures therefore include no federal credit, and no state or utility incentive.
Not modeled
State and utility incentives, SRECs, panel degradation, future rate increases, maintenance, inverter replacement, property value effects, and battery storage. Solar payback is also computed only against tariffs filed since 2020, for the reasons in the vintage section above — a twenty-year decision priced on a decade-old filing would be indefensible.
Benchmark bills
Bills are computed at 500, 1,000 and 2,000 kWh per month held constant
across the year. These are reference loads for comparing tariffs, not a
prediction of any particular household's bill. The reported effective rate is
annual total / annual kWh, so it includes fixed charges — which is why a
tariff with a low energy rate and a high monthly charge can rank worse than it
first appears.
Data quality: we flag, we do not silently correct
URDB is crowd-maintained and contains transcription errors. We never rewrite an
upstream value, because a published figure must be traceable to its source.
Anomalies are flagged instead (scripts/quality.py), and rates flagged as
likely errors are excluded from rankings while remaining visible on their own
page with a caveat.
Two flags are advisory, because the underlying values are frequently real:
- negative prices — genuine for load-control credit riders, such as utility-controlled water heating or grain drying;
- prices above $1.00/kWh — genuine for remote Alaskan utilities generating from diesel, where $1–3/kWh occurs.
Three flags indicate a likely upstream error:
- peer unit mismatch — a fixed charge whose units disagree with the same
utility's other rates. Georgia Power files
0.4603as$/dayon three residential schedules and$/monthon a fourth; taken literally the outlier understates that rate's fixed charge roughly thirtyfold. San Diego Gas & Electric has the same defect on TOU-DR-1. - tier spike — one tier priced more than 5x the median of its own ladder, which usually means a fixed or demand charge was typed into an energy tier.
- implausible fixed charge — above $200/month for a residential meter.
At the 2026-09 snapshot, 24 of 6,013 billable rates (0.40%) carry a
likely-error flag. Suspected errors are listed in data/corrections.json and
are reported upstream to OpenEI.
Reproducibility
bash
pip install -r requirements.txt
python3 scripts/fetch.py # download URDB + EIA-861 into .cache/
python3 scripts/build.py # normalize, join, price, emit data/
python3 scripts/validate.py # gate the result
python3 -m pytest tests/ -q
data/manifest.json records the SHA-256 of every source file used, so any
published figure can be traced back to the exact bytes it came from.
Update cadence
A GitHub Actions workflow re-runs the pipeline monthly and appends one row per
tariff to data/history/benchmarks-YYYY-MM.csv. This series is append-only and
cannot be reconstructed after the fact — it is the reason the dataset becomes
more valuable with age.