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:

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.

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:

Three flags indicate a likely upstream error:

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.