← gpu-pilot · json · source: energy/api/README.md

Utilities Estimator — API

FastAPI service: address in → monthly electric/gas/oil/propane out, with per-input provenance and provider alternatives. · updated 2026-06-12 04:34

TL;DR

  • POST /v1/utilities/estimate — address | lat/lon | zip | attribute overrides → {inputs_used, per-input sources, usage breakdown, monthly/annual by fuel, provider (+PTC), alternatives, confidence, disclaimers}. GET /v1/utilities/providers?county_fips|zip. GET /healthz.
  • Resolution ladder: exact normalized address → zip+street → fuzzy prefix → nearest parcel ≤150 m → attribute/zip fallback → state averages; user overrides always win and are labeled user_override.
  • Demo transcript (port 8096, then stopped): Lyndhurst merged home $274.54/mo high-confidence; Montclair 1907 oil $490.07; MOD-IV-only Manalapan $278.94 medium; propane override $393.13. Not deployed — intended target api.commutingcost.com (Krishna's call).
  • 25 pytest tests green against PostgreSQL (never SQLite), ruff clean; pure-python model (stdlib math only) + asyncpg; config via COMMUTE_DSN env.
  • Open for Krishna: deploy host/port, CJMLS RESO Heating field in reso_etl, GSMLS/NJMLS for full-Jersey coverage, usaddress upgrade if fuzzy match must beat 68.5%, Census key for ACS county fuel priors.

Home Utilities Cost Estimator API

Per-home monthly/annual utility costs (electric, natural gas, heating oil, propane) for any US home — highest accuracy in NJ, where inputs are auto-derived from the 2.54M-home nj_home_attributes backbone (MOD-IV assessment roll + county assessor sqft + CJMLS). Model + data docs: MODEL.md. Rate layer: ../ENERGY.md.

energy/api/
├── app.py                ← FastAPI service (async, Pydantic models)
├── db.py                 ← asyncpg queries: resolution, rates, prices, alternatives
├── model.py              ← pure-python usage model (RECS-2020-calibrated)
├── coefficients.py       ← AUTO-GENERATED fits (calibrate_recs.py)
├── calibrate_recs.py     ← regenerates coefficients from data/recs2020_public_v7.csv
├── etl_nj_attributes.py  ← builds commute.nj_home_attributes from geo DBs
├── addressing.py         ← shared address/municipality normalizer
├── tests/                ← pytest (25 tests; API tests hit PostgreSQL, never SQLite)
└── MODEL.md              ← variables, calibration, validation

Run (demo)

cd /home/krish/gpu-pilot/energy/api
.venv/bin/uvicorn app:app --host 127.0.0.1 --port 8096
# config: COMMUTE_DSN (default postgresql://krish@172.26.1.152:5433/commute)

Not deployed anywhere — intended production target is api.commutingcost.com integration (host/port/systemd = Krishna's call).

Endpoints

POST /v1/utilities/estimate

Body — any of: {"address": "..."}", {"lat":.., "lon":..}, {"zip": "07071"}, plus an optional attributes override block (sqft, year_built, occupants, beds, heating_fuel, water_heater_fuel, home_type, has_ac, room_ac). Precedence: overrides > resolved home > defaults ladder. Every input's provenance comes back in sources.

curl -s -X POST http://127.0.0.1:8096/v1/utilities/estimate \
  -H 'Content-Type: application/json' \
  -d '{"address": "121 Summit Ave, Lyndhurst, NJ 07071"}'

curl -s -X POST http://127.0.0.1:8096/v1/utilities/estimate \
  -H 'Content-Type: application/json' \
  -d '{"zip": "07071", "attributes": {"sqft": 2400, "year_built": 2005,
       "beds": 4, "heating_fuel": "propane"}}'

GET /v1/utilities/providers?county_fips=34013 | ?zip=07042

Serving utilities for the county: EIA-861 bundled residential ¢/kWh, customers, NJ BGS price-to-compare, retail-choice status.

GET /healthz

Demo transcript (2026-06-12, port 8096)

$ curl -s http://127.0.0.1:8096/healthz
{"ok":true,"nj_home_attributes":2540595}

== POST /v1/utilities/estimate {"address": "121 Summit Ave, Lyndhurst, NJ 07071"}
  match: exact_address | source: merged | confidence: high
  usage: 8150 kWh | 825 therms | 0 gal oil | 0 gal propane
  monthly: {"electric": 172.35, "gas": 102.19, "oil": 0.0, "propane": 0.0, "total": 274.54}
  provider: Public Service Elec & Gas Co 24.64 c/kWh | ptc: 10.938
  alternatives: [{"provider": "Public Service Elec & Gas Co", "rate_cents_kwh": 20.417,
                  "vs_provider": "Rockland Electric Co", "benchmark_basis": "in-county",
                  "savings_usd_yr": 51.0, "electric_choice": true}]

== POST /v1/utilities/estimate {"address": "23 Grenada Place, Montclair, NJ 07042"}
  match: exact_address | source: merged | confidence: high
  usage: 9313 kWh | 0 therms | 604 gal oil | 0 gal propane
  monthly: {"electric": 196.23, "gas": 0.0, "oil": 293.85, "propane": 0.0, "total": 490.07}
  provider: Public Service Elec & Gas Co 24.64 c/kWh | ptc: 10.938
  alternatives: [{"provider": "Jersey Central Power & Lt Co", "rate_cents_kwh": 15.647,
                  "vs_provider": "Public Service Elec & Gas Co", "benchmark_basis": "in-county",
                  "savings_usd_yr": 444.0, "electric_choice": true}]

== POST /v1/utilities/estimate {"address": "72 Old Queens Blvd, Manalapan, NJ"}
  match: exact_address | source: modiv | confidence: medium
  usage: 8533 kWh | 794 therms | 0 gal oil | 0 gal propane
  monthly: {"electric": 180.21, "gas": 98.73, "oil": 0.0, "propane": 0.0, "total": 278.94}
  provider: Public Service Elec & Gas Co 24.64 c/kWh | ptc: 10.938
  alternatives: [{"provider": "Jersey Central Power & Lt Co", ... "savings_usd_yr": 407.0}]

== POST /v1/utilities/estimate {"zip": "07071", "attributes": {"sqft": 2400,
       "year_built": 2005, "beds": 4, "heating_fuel": "propane"}}
  match: None | source: None | confidence: high
  usage: 9877 kWh | 0 therms | 0 gal oil | 582 gal propane
  monthly: {"electric": 207.81, "gas": 0.0, "oil": 0.0, "propane": 185.32, "total": 393.13}
  provider: Public Service Elec & Gas Co 24.64 c/kWh | ptc: 10.938

$ curl -s 'http://127.0.0.1:8096/v1/utilities/providers?zip=07042'
  county: 34013 Essex
  Public Service Elec & Gas Co 20.417 c/kWh, ptc: 10.938, choice: true
  Jersey Central Power & Lt Co 15.647 c/kWh, ptc: 11.327, choice: true

Rebuild / refresh

.venv/bin/python calibrate_recs.py        # refit on new RECS vintage -> coefficients.py
.venv/bin/python etl_nj_attributes.py     # rebuild nj_home_attributes (idempotent truncate+load, ~75 s)
.venv/bin/python -m pytest tests/ -q      # 25 tests (PostgreSQL)
.venv/bin/ruff check . && .venv/bin/ruff format --check .

Open items (Krishna)

  • Deploy target/port for production (suggest behind api.commutingcost.com nginx; demo used 8096).
  • reso_etl: map CJMLS RESO Heating/Cooling fields — lifts heating-fuel coverage from 2.4% (remarks regex) to ~100% of listings.
  • GSMLS + NJMLS feeds for full-Jersey MLS coverage (CJMLS is Central-Jersey-centric; Bergen/Essex homes resolve via MOD-IV only today).
  • Address normalization: stdlib normalizer is deliberate (zero deps); usaddress (ML parser) is the upgrade if fuzzy match rate needs to climb above 68.5%.
  • Census API key for ACS B25040 county/tract heating-fuel priors.
  • Forward-looking prices: PSE&G BGS 2026 PTC is loaded; gas supply (BGSS) and oil futures are not.

Comments