CarbonBench
A public leaderboard and open API ranking AI models, providers and regions by carbon per million tokens, next to cost and speed.
Ran in spring 2026. No longer maintained. The findings are published in five essays.
| Role | Year | Kind | Try it |
|---|---|---|---|
| Concept, data design, engineering | 2026 | AI, climate | The five essays |
| 85 | models in the April 2026 dataset |
| 9 | cloud regions across the US, EU and Asia Pacific |
| 4 vs 50+ | grams of CO2 per million tokens for the same model in two regions |
The problem
The carbon cost of an AI inference call changes by an order of magnitude depending on where and when you make it. Pricing pages do not show this.
What I built
- A leaderboard. Every model, provider and region ranked by carbon per million tokens, next to cost and speed. Each row opened a 24-hour carbon curve for its region.
- An open API. One endpoint answered one question: what is the lowest-carbon way to run this model right now? It returned the best option, four alternatives, and the saving against the dirtiest.
- An hourly pipeline. A scheduled job recalculated every model, provider and region score from live grid data.
How it is structured
In the code
These are short excerpts from Beach-Bum/CarbonBench, which is private.
export function calculateCarbonPerMTokens(
whPer1000Tokens: number,
gco2PerKwh: number
): number {
const kwhPerMTokens = (whPer1000Tokens / 1000) * 1000
return kwhPerMTokens * gco2PerKwh
}
export function calculateBlendedCostPerMTokens(
inputCostPerMToken: number,
outputCostPerMToken: number,
inputOutputRatio: number = 0.5
): number {
return (inputCostPerMToken * inputOutputRatio) +
(outputCostPerMToken * (1 - inputOutputRatio))
}Where the numbers come from
| Input | Source | Refreshed |
|---|---|---|
| Energy per token | The Hugging Face AI Energy Score results: GPU energy for text generation, measured on an Nvidia A100 | When the import script is run |
| Grid carbon | Electricity Maps, the latest carbon intensity for each cloud region's grid zone | Every hour |
| Price | Provider price lists, entered by script | By hand. A weekly job is scheduled but was never built |
The calculation is one multiplication: watt-hours per thousand tokens times grams of CO2 per kilowatt-hour gives grams of CO2 per million tokens. The units cancel, so no conversion factor is hiding in it.
The data model
Next.js 14 with Prisma on Postgres. Eight tables. Models, their energy benchmarks, regions, hourly grid readings, daily roll-ups of those readings, provider prices, the combined scores, and email sign-ups. There is exactly one score for each model, region and provider, and it is overwritten in place; that rule is the fix described under "What broke".
Three scheduled jobs were defined: grid readings hourly, the clean-up nightly at 03:00, prices weekly.
What the numbers leave out
- Only the GPU. Cooling and the rest of the data centre are not counted, so real emissions are higher than these figures.
- Reference hardware, not the provider's. Energy was measured once per model on an A100. A provider running different chips will differ.
- The grid's average, not the provider's contract. A region's figure is its grid zone at that hour. Renewable purchase agreements are not reflected.
- No automated tests. The project was five commits over a few weeks; the findings are only as good as the three inputs above.
These limits change the absolute numbers. They do not change the finding, because every model in a region is affected the same way: where and when a model runs moves its carbon cost more than which model it is.
What the data showed
- Region beats model. Llama 3.1 8B on Google Cloud in the Netherlands: about 4 grams of CO2 per million tokens. The same model in Virginia at peak: over 50. A 70B model on a clean grid can beat an 8B model on a dirty one.
- Price and carbon are decoupled. Providers charge the same per token regardless of region. Routing for carbon is close to free.
- Time matters. Grid intensity swings 30 to 50% over a day. Batch work that can wait can run at the clean hours.
- It is an indirect emission. Every API call is an emission the customer cannot see. Activity-based accounting needs exactly these conversion factors.
Open questions
The first version wrote a new score row for every model and region on every hourly run and never cleaned up. The database filled and the host shut it down.
The rebuild overwrote scores in place, rolled hourly readings older than seven days into daily averages, and pruned the originals nightly. Steady state: under 5 MB, permanently.