emission-factors.com API reference
Base URL https://emission-factors.com. Free and keyless: no API key, no signup. Rate limit 2,000 requests/hour per IP (HTTP 429 with retryAfter seconds). Errors are JSON: {"error": "...", "message": "..."}. Full spec: /openapi.json. Summary for agents: /llms.txt. MCP: /mcp-setup.
GET /api/lookup
Look up emission factors for a ZIP code
Query parameters:
zip(required) - 5-digit ZIP; ZIP+4 accepted e.g.94105year- 2024 = eGRID2024 from Cornerstone (EPA code, 2024 inputs; CC-BY-4.0); residual mix only with 2023
Responses:
- 200: Emission factors for the ZIP code. A well-formed ZIP with no eGRID mapping (PO box, territory) returns 200 with candidates: [] and the reason in notes.
- 400: Malformed ZIP (not 5 digits or ZIP+4) or invalid year
- 429: Rate limit exceeded
GET /api/lookup/by-coordinates
Look up emission factors by latitude/longitude
Query parameters:
lat(required) e.g.37.7749lon(required) e.g.-122.4194
Responses:
- 200: Resolved ZIP and emission factors
- 400: Invalid coordinates
- 404: No ZCTA at these coordinates
- 503: Census Geocoder unavailable; retry
POST /api/lookup/batch
Look up emission factors for up to 100 ZIP codes
Responses:
- 200: A bare array of lookup results (default), or {edition, *_source fields, results} with compact or envelope. Always eGRID2023; use /api/lookup?year=2024 for eGRID2024.
- 400: Not an array or {zips}, empty, over 100, a non-string/number entry, or an unknown field
POST /api/calculate
Calculate Scope 2 CO₂e for a ZIP + kWh, location-based and market-based candidates[].co2e_kg is location-based (eGRID). candidates[].market_based.co2e_kg applies the Green-e residual mix to kWh not covered by contractual instruments (renewable_kwh).
JSON body:
zip(string) e.g."94105"kwh(number) e.g.500year(string) - eGRID edition (default 2023); market-based needs 2023renewable_kwh(number) - kWh covered by RECs, PPAs or green tariffs (market-based method). Default 0, max kwh. e.g.100
Responses:
- 200: CO₂e calculation result (location- and market-based)
- 400: Missing or invalid fields, malformed ZIP, invalid year, or unknown fields (e.g. "unit"; kwh is always kWh)
- 404: Well-formed ZIP with no eGRID subregion (PO box, unique ZIP)
POST /api/bulk
Process a CSV file of ZIP codes
Responses:
- 200: Enriched CSV with emission factor columns. Delimiter (comma, semicolon, tab) is detected from the header; the ZIP and kWh columns are detected by name (X-kWh-Column-Detected header).
- 400: No CSV, or no ZIP column
- 413: Over 1 MB
GET /api/rate
Latest monthly average retail electricity rate by sector
Query parameters:
zip(required) - 5-digit ZIP or ZIP+4 e.g.94105
Responses:
- 200: Residential, commercial, industrial, transportation, all-sectors prices from EIA Form 861 (sectors with no sales are omitted)
- 400: Malformed ZIP
- 404: No state mapping (e.g. Puerto Rico, territories)
- 503: EIA API temporarily unavailable; retry (Retry-After header)
GET /api/intensity
Hourly grid carbon intensity (EIA-930 fuel mix derived, data lags up to ~1 day)
Query parameters:
zipba- Balancing authority code (CISO, ERCO, PJM, NYIS, MISO, etc.) e.g.CISOhoursaggregate- Return 24-hour averaged profile across window + cleanest/dirtiest hour + swing ratio
Responses:
- 200: Hourly intensity array or aggregated profile
- 400: Missing ZIP/BA or unknown BA
- 404: ZIP not in EIA-930 coverage (AK, HI, PR)
- 503: EIA-930 API temporarily unavailable; retry (Retry-After header)
GET /api/plant-emissions
Unit-level emissions from EPA CAMD (fossil units >25 MW): per-facility summary from daily data, or raw unit-hour records CAMD publishes by quarter; the default window ends on latest_published_date (last day of the latest quarter that ended 45+ days ago). Queries over the record limit are rejected with the count, never truncated. NOx/SO2 in the summary are lb.
Query parameters:
facility_id- EPA CAMD/ORIS facility code (facility_id or state is required) e.g.3state- US state, 2-letter code or full name (TX or Texas), for a state-wide aggregate e.g.TXdays- Counted back from latest_published_datebegin- Custom window start (with end; overrides days; max 366 days)end- Custom window end (with begin)format- hourly returns raw CAMD unit-hour records (large: use facility_id and a short window)limit- Facilities returned in summary mode, largest CO2 first
Responses:
- 200: Per-facility emissions summary or raw hourly records
- 400: Invalid or missing parameters, an ID or date CAMD rejects, or a query over the record limit
- 503: EPA CAMD API temporarily unavailable (Retry-After header), or the server key is not configured
GET /api/utility-rate
Utility-specific electricity tariff from OpenEI URDB (upgrade from state-average /api/rate)
Query parameters:
zipe.g.94105utilitye.g.Pacific Gas & Electric Coeiaid- EIA utility ID e.g.14328sector- Case-insensitive; RES/COM/IND codes acceptedlimit
Responses:
- 200: Currently-active default tariffs with effective rate, tiers, TOU flag, and source PDF
- 400: No zip, utility, or eiaid provided, or an invalid sector or limit
- 404: Unknown ZIP code
- 503: OpenEI URDB API temporarily unavailable; retry (Retry-After header)
POST /api/compare
Rank 2-25 candidate sites on grid carbon, retail $/kWh and cleanest daily window
JSON body:
zips(array) e.g.["20147","79601","97058"]annual_kwh(number) - Optional; adds annual tCO2e and cost per site e.g.87600000sector(string)duration(integer) - Cleanest-window length in hours
Responses:
- 200: Sites with factors, rates, cleanest window and rank_carbon / rank_cost / rank_combined
- 400: Fewer than 2 or more than 25 ZIPs, or invalid input
- 429: Rate limit reached while fetching per-site data (compare costs about 1 + sites + states + BAs requests)
GET /api/cleanest-hours
Cleanest contiguous N-hour window of the day to run a flexible load (EIA-930, last ~7 days)
Query parameters:
zipe.g.94105ba- Balancing authority code instead of zip, e.g. CISOdurationbetween- Optional local hours the window must fall in (start-end, wrapping past midnight), e.g. 20-06 for overnight EV charging e.g.20-06
Responses:
- 200: cleanest_window and dirtiest_window (UTC and the ZIP's local time), savings %, hours ranked. For a nearly carbon-free or flat grid both windows are null and no_window_reason says why.
- 400: Invalid duration or input
- 404: ZIP not covered by EIA-930 (AK, HI, PR)
- 503: EIA-930 API temporarily unavailable; retry (Retry-After header)
GET /api/fuel-emissions
Scope 1 stationary combustion CO₂e for one fuel (GHG Emission Factors Hub 2026, Cornerstone, AR6; edition=2025 for EPA's, AR5); no fuel lists fuels
Query parameters:
fuele.g.natural_gasquantitye.g.1000unit- Required with fuel: mmBtu, therm, Dth, scf, ccf, mcf, gallon, liter, barrel, short_ton, lb, tonne e.g.thermedition- 2026: Cornerstone Hub (AR6 GWPs); 2025: EPA Hub (AR5)
Responses:
- 200: CO2, CH4, N2O and CO2e (GWPs of the chosen edition)
- 400: Unknown fuel, unit or edition
POST /api/fuel-emissions
Scope 1 CO₂e for several fuels with a total
JSON body:
items(array)edition(string)
Responses:
- 200: Per-item results and total_co2e_kg
- 400: Invalid item
POST /mcp
MCP server (JSON-RPC 2.0 over Streamable HTTP, stateless, keyless): initialize, tools/list, tools/call. 11 tools mirroring these REST endpoints. Setup: https://emission-factors.com/mcp-setup
JSON body:
jsonrpc(string) e.g."2.0"id(integer) e.g.1method(string) e.g."tools/list"params(object)
Responses:
- 200: JSON-RPC response
- 204: Notification accepted
GET /health
Health check
Responses:
- 200: OK
GET /status
Edition, ZIP coverage (counted from the mapping) and the startup self-check result
Responses:
- 200: Status object
GET /methodology
Methodology, data sources, licenses and edition policy (JSON; browsers get the page)
Responses:
- 200: Methodology document
GET /openapi.json
This OpenAPI specification
Responses:
- 200: OpenAPI 3.0 document