A practical guide to the free hourly US grid carbon intensity API, with working code for two use cases: backtesting demand response and timing EV charging. It is a timing and analysis tool, not a source of Scope 2 reporting factors.
GET /api/intensity?zip=94105&hours=24 for up to 168 hours (7 days) for the balancing authority serving that ZIP. Data lags up to about a day; it is not real-time. For live or forecast intensity, WattTime and Electricity Maps offer commercial APIs.
ba_basis says which rule applied. 46 balancing authorities are mapped; 45 currently report to EIA-930 (WACM has not reported since April 2, 2026, and returns a 404 that says so).Fuel factors (direct, operational CO₂e; not lifecycle):
| Fuel | EIA code | kg CO₂e per MWh | Source (EPA eGRID2023) |
|---|---|---|---|
| Coal | COL | 1,018 | US coal output rate (USCC2ERT, 2,244.545 lb/MWh) |
| Natural gas | NG | 408 | US gas output rate (USGC2ERT, 900.028 lb/MWh) |
| Petroleum | OIL | 708 | US oil output rate (USOC2ERT, 1,560.637 lb/MWh) |
| Other | OTH | 579 | US all-fossil output rate (USFSC2ERT); EIA-930 does not say what "other" burns |
| Geothermal | GEO | 28 | Geothermal plants' CO₂e over their net generation (60.7 lb/MWh) |
| Nuclear / hydro / wind / solar / biomass | - | 0 | No combustion; biomass CO₂ is biogenic |
Because these are US-average rates applied to in-region generation only (imports are not counted), hourly values do not add up to the annual eGRID subregion factor. Use them to compare hours, not to report Scope 2.
A lot, depending on the grid. Hour-of-day averages over the 7 days (168 hours) ending April 16, 2026, 03:00 UTC:
| Grid | Cleanest hour (local) | Dirtiest hour | Swing | Week avg |
|---|---|---|---|---|
| CAISO (California) | 2pm PT @ 0.024 62% solar, 16% wind | 3am PT @ 0.106 27% wind, 26% gas, 24% hydro | 4.5× | 0.066 |
| ERCOT (Texas) | noon CT @ 0.189 34% solar, 27% wind | 8pm CT @ 0.305 42% gas, 38% wind, 13% coal | 1.6× | 0.257 |
| NYISO (New York) | 5pm ET @ 0.313 49% gas, 24% hydro | midnight ET @ 0.338 50% gas, 23% other | 1.1× | 0.327 |
Values in kg CO₂e per kWh. Three findings:
| Hour (PT) | Avg intensity (kg CO₂e/kWh) | Pattern |
|---|---|---|
| midnight–6am | 0.102–0.106 | No solar; wind, hydro, nuclear and gas |
| 7am | 0.061 | Solar ramping up |
| 8am–4pm | 0.024–0.034 | Solar peak |
| 5pm | 0.037 | Solar fading |
| 6pm | 0.058 | Solar drops out |
| 7pm–11pm | 0.083–0.095 | Evening; batteries discharge (excluded as storage) |
| Hour (UTC) | Intensity (kg CO₂e/kWh) | Fuel mix |
|---|---|---|
| 22:00 Apr 15 (3pm PT, cleanest) | 0.019 | 66% solar · 12% wind · 4% gas |
| Day avg (24 hours) | 0.054 | - |
| 13:00 Apr 15 (6am PT, dirtiest) | 0.100 | 7% solar · 16% wind · 24% gas |
Say your ops team moved a 1 MWh (1,000 kWh) batch job in ERCOT from 8pm local (01:00 UTC) to 11am local (16:00 UTC). How much CO₂e did that avoid?
import requests
API = "https://emission-factors.com/api/intensity"
def intensity_at(ba, hour_utc):
"""Intensity for a specific past hour (within the last 168)."""
r = requests.get(API, params={"ba": ba, "hours": 168})
hours = {h["hour_utc"]: h["intensity_kg_co2e_per_kwh"] for h in r.json()["hourly"]}
return hours.get(hour_utc)
dirty = intensity_at("ERCO", "2026-04-16T01:00Z") # 0.345 kg/kWh
clean = intensity_at("ERCO", "2026-04-15T16:00Z") # 0.167 kg/kWh
kwh = 1000
savings = kwh * dirty - kwh * clean
print(f"Carbon avoided: {savings:.0f} kg CO2e ({(1 - clean/dirty)*100:.0f}% less)")
# Output: Carbon avoided: 178 kg CO2e (52% less)
On that day: 178 kg CO₂e avoided, 52% less. Across the week the hour-of-day averages are closer (noon 0.189 vs 8pm 0.305 kg/kWh), about 116 kg per 1 MWh run, so a 250-run-per-year job avoids roughly 29 tCO₂e a year at that week's pattern.
A 20-vehicle fleet in San Francisco charging 100 kWh per vehicle (2 MWh a day). When is the grid cleanest?
import requests
from collections import defaultdict
# cleanest_hours does this in one call, in local time:
r = requests.get("https://emission-factors.com/api/cleanest-hours",
params={"zip": "94105", "duration": 4})
print(r.json()["cleanest_window"])
# or build the hour-of-day profile yourself from the hourly series (UTC):
r = requests.get("https://emission-factors.com/api/intensity",
params={"zip": "94105", "hours": 168})
by_hour = defaultdict(list)
for h in r.json()["hourly"]:
by_hour[int(h["hour_utc"][11:13])].append(h["intensity_kg_co2e_per_kwh"])
for hour in sorted(by_hour):
print(f"{hour:02d}:00 UTC {sum(by_hour[hour]) / len(by_hour[hour]):.3f}")
Over the April week above, charging at noon to 3pm (about 0.024 kg/kWh) instead of midnight (0.102) means about 48 kg instead of 204 kg CO₂e a day: roughly 156 kg a day, or 57 tCO₂e a year at that pattern. Utility time-of-use rates often price midday as peak, which is exactly California's cleanest time.
In Texas, midday is also cleanest but the gap is smaller (about 0.19 vs 0.31 kg/kWh in the evening). In New York there is essentially no carbon-optimal hour. Use your own grid's data rather than assuming.
/api/calculate). Hourly values use US-average fuel rates and in-region generation only.The annual eGRID factor (e.g. CAMX 0.195 kg/kWh) is EPA's generation-weighted rate for a subregion over a year, from plant-level data, and is the standard input for GHG Protocol Scope 2 location-based reporting. The hourly intensity here applies US-average fuel rates to each hour's in-region fuel mix, so it shows how the grid varies through the day but is not comparable to, or a substitute for, the eGRID factor.
In the April week above, California's 9am–3pm generation averaged 59–65% solar, with wind, hydro and nuclear making up most of the rest; none of those burn fuel. On April 15 the 3pm PT hour was 66% solar and 4% gas, giving 0.019 kg/kWh.
46 Lower-48 balancing authorities are mapped (45 currently report to EIA-930). A ZIP maps to one of them through its utility, then its state and eGRID subregion, then its state; you can also call ?ba=CISO directly.
No. The API is free and keyless - no signup, no header, no auth token. Just call the endpoint. Full API docs.
curl "https://emission-factors.com/api/intensity?zip=94105&hours=24"