TL;DR — An emission factor
EFis how much CO₂ a source emits per kWh (gCO₂/kWh). It's the number that turns "how much electricity" into "how much carbon." Two choices decide whether your carbon report is honest or misleading: lifecycle vs combustion-only factors (do you count building the plant and supplying the fuel?), and average vs marginal accounting (are you measuring the whole mix or the one generator your decision actually moves?). The average/marginal choice can flip a conclusion. The professional move: report the transparent average basis and disclose the marginal caveat.
1. Simple explanation
An emission factor is a conversion rate. It answers: "For one kWh made this way, how many grams of CO₂?"
Coal is about 820 gCO₂/kWh. Wind is about 11. That's it — a per-kWh price tag in carbon. Multiply the factor by the kWh, and you get grams of CO₂.
But two subtle questions decide whether your number means anything.
Question 1: What do you count? Do you count only the smoke from the chimney (combustion-only)? Or also the concrete poured to build the plant, the steel, the mining and piping of the fuel, and decommissioning (lifecycle)? For wind and solar there's no chimney at all, so combustion-only would say "zero" — which is wrong, because building the panels and turbines took energy. Lifecycle is the honest, complete count.
Question 2: Average or marginal? If you ask "how dirty is the grid on average right now?" you average the whole mix. But if you ask "if I plug in one more server, which power plant turns up to feed it?" — that's usually a gas peaker, not the average. These two answers can point in opposite directions.
Analogy — a shared dinner bill. Lifecycle vs combustion is like asking whether the bill includes just the food, or also the tip, tax, and the cost of the kitchen. Average vs marginal is the deeper one: the average cost per person is the total bill split evenly — but if you order one more dessert, the marginal cost is just that dessert's price, not the average. Deciding whether to order dessert by looking at the average per-head cost gives you the wrong answer. Same with adding load to a grid: judge it by the marginal plant it turns on, not the average mix.
2. Diagram
ENERGY (kWh) ──×── EMISSION FACTOR (gCO2/kWh) ──=── CARBON (gCO2)
│
┌─────────────────┴──────────────────┐
│ │
WHAT DO YOU COUNT? WHICH FACTOR?
(scope of the factor) (basis of the decision)
│ │
┌────┴─────┐ ┌──────┴───────┐
COMBUSTION LIFECYCLE AVERAGE MARGINAL
only the build + fuel whole-mix the ONE plant
chimney + run + end CI(t) your +load turns on
(wind=0!) (wind=11, honest) transparent often GAS (490)
reproducible truer for decisions
ACCOUNTING FRAME (Scope 2):
LOCATION-BASED = physical grid mix where you plug in (CI of the wire)
MARKET-BASED = contracts/RECs/PPAs you bought (what you paid for)
3. How it works
3.1 The emission-factor table (IPCC AR5 lifecycle)
We use the IPCC AR5 lifecycle medians. "Lifecycle" = cradle-to-grave: construction, fuel supply, operation, decommissioning.
| Fuel | EF (gCO₂/kWh) | Why that number |
|---|---|---|
| Coal | 820 | High carbon fuel + combustion dominates |
| Oil | 650 | Liquid fossil, peaker/backup |
| Gas | 490 | Burns cleaner than coal; still fossil |
| Other | ≈230 | Biomass/mixed/unknown fallback |
| Solar | 48 | No combustion — this is panel manufacturing |
| Hydro | 24 | Construction + some reservoir methane |
| Nuclear | 12 | Tiny lifecycle footprint |
| Wind | 11 | Lowest at scale — mostly turbine build |
3.2 Why lifecycle differs from combustion-only
Combustion-only counts just the CO₂ from burning fuel at the plant. It has one fatal flaw:
Combustion-only says: SOLAR = 0, WIND = 0, NUCLEAR = 0 (no fuel burned!)
Lifecycle says: SOLAR = 48, WIND = 11, NUCLEAR = 12 (build + supply chain)
Combustion-only makes renewables look perfectly clean, which overstates their benefit and hides the real (small) footprint of manufacturing panels and turbines. Lifecycle is the honest basis for comparing sources, because it counts the carbon that a smokestack-only view ignores. For fossil fuels the two are closer (combustion dominates), but for renewables and nuclear the entire footprint is lifecycle — so mixing bases in one report is a classic error.
| Combustion-only | Lifecycle (AR5) | |
|---|---|---|
| Counts | Only burning at the plant | Build + fuel supply + run + end-of-life |
| Renewables/nuclear | ~0 (misleading) | 11–48 (honest) |
| Fossil fuels | Slightly lower | Slightly higher |
| Use for | Regulatory stack emissions | Comparing sources fairly |
Rule: never mix bases in one calculation. Pick lifecycle or combustion-only and label it.
3.3 Average vs marginal — the single most important nuance
This is the concept interviewers use to separate people who repeat carbon numbers from people who understand them.
| Average emission factor | Marginal emission factor | |
|---|---|---|
| Question it answers | "How dirty is the whole mix right now?" | "If I add/remove load, which plant responds?" |
| How computed | Weighted average of all fuels (CI(t)) | The generator on the margin (often gas) |
| Best for | Reporting total footprint, transparency | Decisions: should I add/shift this load? |
| Data source | EIA fuel mix, ISO feeds | Electricity Maps, WattTime |
| Risk | Under/over-credits a marginal decision | Harder to measure; model-dependent |
The average CI is the whole-mix intensity from the Why Grid Carbon Intensity Varies Hour to Hour article. But when you decide to run one more job, you don't spread it across the average mix — you turn up whatever plant is on the margin, which is very often a gas turbine (~490), because the cheap clean baseload (wind, nuclear) is already fully dispatched.
3.4 How a conclusion can FLIP (see §4.2 for numbers)
Imagine a grid that is 80% nuclear + 20% gas one hour. The average CI is low (~110), so an average-basis report says "the grid is clean, running load now is fine." But the nuclear is baseload — it's already maxed. Any extra load spins up gas at 490. On a marginal basis, adding load here is 4–5× dirtier than the average suggests. A decision that looks green on average is actually dirty on the margin. Same hour, opposite verdict.
3.5 Scope 2: location-based vs market-based
The GHG Protocol requires companies to report Scope 2 (purchased electricity) two ways:
LOCATION-BASED = your kWh × the CI of the physical grid you're plugged into
(reflects the actual electrons — the wire's real mix)
MARKET-BASED = your kWh × the factor of what you CONTRACTED for
(RECs, PPAs, green tariffs — "I bought wind power")
A company can buy Renewable Energy Certificates (RECs) and report ~0 market-based emissions while its physical grid (location-based) is still burning gas at 6 p.m. Both are legitimate and required — they answer different questions ("what flowed through my wire" vs "what did I pay to support"). Reporting only the flattering one is greenwashing.
3.6 Where the data comes from
| Source | What it gives | Basis |
|---|---|---|
| EIA Open Data API | Hourly fuel mix by balancing authority (US) | Average (you compute CI) |
| ISO feeds (ISO-NE, PJM, CAISO…) | Real-time generation mix | Average |
| Electricity Maps | CI + marginal signal, many countries | Average and marginal |
| WattTime | Marginal operating emissions rate (MOER) | Marginal |
| IPCC AR5 | The EF per fuel | Lifecycle factors |
4. The math
4.1 Emission factor → carbon
The core identities:
carbon (gCO2) = energy (kWh) × EF (gCO2/kWh)
CI_average(t) = Σ_f gen_f(t)·EF_f / Σ_f gen_f(t) (whole-mix, gCO2/kWh)
marginal carbon(Δload)= Δload (kWh) × EF_marginal (the plant that responds)
4.2 Worked example — the conclusion flip
Grid this hour: NUCLEAR 8000 MWh + GAS 2000 MWh. You want to add a 100 kWh job.
Average basis (whole mix):
CI_avg = (8000·12 + 2000·490) / (8000 + 2000)
= (96,000 + 980,000) / 10,000
= 1,076,000 / 10,000 = 107.6 gCO2/kWh → looks CLEAN
job carbon (average basis) = 100 kWh × 107.6 = 10,760 gCO2 ≈ 10.8 kg
Marginal basis (nuclear is maxed baseload; extra load spins up gas):
EF_marginal = 490 (gas, the responding unit)
job carbon (marginal basis) = 100 kWh × 490 = 49,000 gCO2 = 49.0 kg
The flip:
marginal / average = 49.0 / 10.8 ≈ 4.5×
Average basis says: "grid is clean (107.6), your job ≈ 10.8 kg — go ahead."
Marginal basis says: "your job actually spins up GAS — it's ≈ 49 kg, 4.5× worse."
Same hour, same job, opposite guidance. This is why you disclose the basis. For a decision (add/shift load), the marginal number is the more honest one; for a total footprint report, the transparent average is standard. Report the average, flag the marginal caveat.
5. Real code
A factors table plus a function that annotates a CI result with its accounting basis — so no number leaves your pipeline without a label.
"""Emission factors + an annotator that stamps every CI number with its basis.
The point: a carbon figure is meaningless without (scope, basis, marginal caveat).
"""
from dataclasses import dataclass
# IPCC AR5 LIFECYCLE emission factors (gCO2/kWh). Lifecycle, not combustion-only.
EMISSION_FACTORS_LIFECYCLE = {
"COAL": 820, "OIL": 650, "GAS": 490, "OTHER": 230,
"SOLAR": 48, "HYDRO": 24, "NUCLEAR": 12, "WIND": 11,
}
# The fuel usually 'on the margin' (responds to +/- load) on most US grids.
DEFAULT_MARGINAL_FUEL = "GAS"
@dataclass
class CarbonEstimate:
value_gco2_per_kwh: float # the CI or EF number
basis: str # "average" or "marginal"
scope2_method: str # "location-based" or "market-based"
lifecycle: bool # True = includes build+fuel supply
caveat: str # human-readable honesty note
def __str__(self) -> str:
life = "lifecycle" if self.lifecycle else "combustion-only"
return (f"{self.value_gco2_per_kwh:.1f} gCO2/kWh "
f"[{self.basis}, {life}, Scope2:{self.scope2_method}] — {self.caveat}")
def annotate_ci(ci_value: float,
basis: str = "average",
scope2_method: str = "location-based",
lifecycle: bool = True,
marginal_fuel: str = DEFAULT_MARGINAL_FUEL) -> CarbonEstimate:
"""Wrap a raw CI number with its accounting basis and an honest caveat."""
if basis == "average":
caveat = (f"whole-mix AVERAGE; a decision to ADD load likely displaces the "
f"MARGINAL unit ({marginal_fuel}="
f"{EMISSION_FACTORS_LIFECYCLE[marginal_fuel]}), which can differ a lot")
elif basis == "marginal":
caveat = ("MARGINAL rate for add/shift decisions; not the whole-mix average, "
"and model-dependent (source: Electricity Maps / WattTime)")
else:
raise ValueError(f"basis must be 'average' or 'marginal', got {basis!r}")
return CarbonEstimate(ci_value, basis, scope2_method, lifecycle, caveat)
if __name__ == "__main__":
# Report the transparent average, but ALWAYS surface the marginal caveat.
avg = annotate_ci(107.6, basis="average")
mrg = annotate_ci(490.0, basis="marginal")
print(avg)
print(mrg)
# 107.6 gCO2/kWh [average, lifecycle, Scope2:location-based] — whole-mix AVERAGE; a
# decision to ADD load likely displaces the MARGINAL unit (GAS=490), ...
# 490.0 gCO2/kWh [marginal, lifecycle, Scope2:location-based] — MARGINAL rate for
# add/shift decisions; not the whole-mix average, ...
The load-bearing idea: a bare float like 107.6 is dangerous. CarbonEstimate forces every number to carry its basis, scope, lifecycle flag, and a caveat — so downstream readers can't quietly treat an average as if it were marginal.
6. Real-world example
Scenario: a cloud team reports its data-center carbon — and gets challenged in review.
The team runs 100,000 kWh/month in a region whose average CI is 107.6 gCO₂/kWh (nuclear-heavy). They report:
| Report | Basis | Number | Monthly carbon |
|---|---|---|---|
| First draft | Average, location-based | 107.6 | 10.76 tonnes |
| Sustainability audit | Marginal (added load → gas 490) | 490 | 49.0 tonnes |
| Market-based (bought RECs) | Contracted wind (~11) | 11 | 1.1 tonnes |
Same 100,000 kWh, THREE legitimate numbers: 1.1 t, 10.76 t, 49.0 t — a ~45× spread.
None is a lie. Each answers a DIFFERENT question:
market-based (1.1 t) = "what did we pay to support?" (RECs/PPAs)
location-based avg = "what's the wire's mix?" (transparent default)
marginal (49 t) = "what did our EXTRA load turn on?" (decision-honest)
The honest report leads with the location-based average (10.76 t) as the transparent basis, states the market-based (1.1 t) separately per GHG Protocol, and discloses that on a marginal basis new load displaces gas (~49 t) — so growth decisions should use the marginal signal. That disclosure is what turns a defensible report into a trustworthy one, and it's exactly the sensitivity-analysis mindset from Backtesting, Baselines & Sensitivity Analysis: show how the answer moves when you change the assumption.
7. Interview questions companies actually ask
Q1 [easy] (Amazon, Google) "What is an emission factor?"
A A conversion rate: gCO2 emitted per kWh for a given source. Multiply kWh by the factor to
get carbon. Coal ~820, gas ~490, solar ~48, wind ~11 (IPCC AR5 lifecycle).
Q2 [easy] (Microsoft) "Why isn't wind's emission factor zero?"
A Because we use LIFECYCLE factors. Wind burns no fuel (combustion-only = 0), but building
the turbine — steel, concrete, transport — has a footprint. Lifecycle honestly counts it
at ~11 gCO2/kWh. Combustion-only would wrongly say 0.
Q3 [medium] (Google, Salesforce) "Lifecycle vs combustion-only — when does the choice matter
most?"
A Most for renewables and nuclear, where combustion-only is ~0 but lifecycle is 11-48 —
a huge relative gap. For fossil fuels the two are close (burning dominates). Never mix
bases in one report; pick one and label it.
Q4 [medium] (WattTime, Electricity Maps) "Average vs marginal emission factors — what's the
difference and which do you use for a scheduling decision?"
A Average = the whole-mix intensity right now. Marginal = the one generator that responds
when you add/remove load (often gas). For a DECISION to add or shift load, marginal is
more honest, because your extra kWh spins up the marginal plant, not the average mix.
Q5 [medium] (Amazon, Meta) "Give an example where average and marginal give opposite advice."
A An 80% nuclear / 20% gas hour: average CI ~108 looks clean, so 'run now' seems green. But
nuclear is maxed baseload; extra load spins up gas at 490 — ~4-5x dirtier on the margin.
Average says 'go', marginal says 'this is actually dirty'. Same hour, opposite verdict.
Q6 [medium] (Deloitte, sustainability roles) "Explain Scope 2 location-based vs market-based."
A Location-based = your kWh × the physical grid's CI (the actual electrons). Market-based =
your kWh × what you contracted for (RECs/PPAs/green tariffs). GHG Protocol requires BOTH;
reporting only the flattering market-based number while the wire burns gas is greenwashing.
Q7 [hard] (Google, climate-tech) "A team buys RECs and reports ~zero emissions. Is the grid
any cleaner? How do you audit this?"
A Not necessarily. RECs zero out the MARKET-BASED number but the LOCATION-BASED (physical)
grid may be unchanged, and on a MARGINAL basis new load still turns on gas. Audit by
reporting all three bases and checking whether the RECs are additional (funded new clean
capacity) or just paper claims on existing generation.
Q8 [hard] (research/lab roles) "How do you keep a carbon report intellectually honest?"
A Lead with the transparent, reproducible average location-based number; state the
market-based number separately per GHG Protocol; and disclose the marginal caveat for any
add/shift decision. Label every figure with (basis, lifecycle-or-combustion, scope).
Never let a bare number travel without its assumptions — and show a sensitivity range.
8. When to use / tradeoffs
USE AVERAGE (whole-mix CI) when:
✓ reporting total footprint of existing consumption
✓ you need transparency + reproducibility (EIA/ISO data, auditable)
USE MARGINAL when:
✓ DECIDING to add, shift, or defer a flexible load
✓ you have a marginal signal (Electricity Maps / WattTime)
USE LIFECYCLE factors when:
✓ comparing energy SOURCES fairly (default; AR5)
USE COMBUSTION-ONLY only when:
✓ a regulation specifically demands stack emissions — and label it
HONEST LIMITS:
✗ Marginal is model-dependent and harder to measure than average.
✗ Market-based (RECs) can mask a dirty physical grid — disclose location-based too.
✗ Factor sets differ (AR5 vs AR6, national inventories) — cite your source and year.
✗ 'Other/unknown' fuels fall back to ~230, an approximation.
✗ No single number is 'the' truth — report the basis and a sensitivity range.
9. Summary + related articles
- An emission factor = gCO₂ per kWh for a source;
carbon = kWh × EF. - Use lifecycle (AR5) factors to compare sources fairly; combustion-only wrongly zeroes renewables.
- Average vs marginal is the biggest nuance: average = whole mix; marginal = the plant your decision actually moves (often gas). It can flip the conclusion (worked ~4.5× flip).
- Scope 2 must be reported location-based (physical grid) and market-based (contracts/RECs).
- Data: EIA / ISO for average, Electricity Maps / WattTime for marginal, IPCC AR5 for factors.
- Intellectual honesty: report the transparent average, disclose the marginal caveat, label every number's basis, show a sensitivity range.
Related: Why Grid Carbon Intensity Varies Hour to Hour · Measuring the Energy & Carbon of AI (the A/E Metric) · Backtesting, Baselines & Sensitivity Analysis · Weighted Averages & Aggregation
Resources
- GHG Protocol Scope 2 Guidance (location- vs market-based) — https://ghgprotocol.org/scope-2-guidance
- IPCC AR5 WG3 Annex III (lifecycle emission factors) — https://www.ipcc.ch/report/ar5/wg3/
- EIA Open Data API — https://www.eia.gov/opendata/
- Electricity Maps (average + marginal) — https://www.electricitymaps.com/
- WattTime — marginal emissions (MOER) — https://www.watttime.org/
- EPA on RECs and market-based claims — https://www.epa.gov/green-power-markets/renewable-energy-certificates-recs