Four steps to your first carbon-aware inference.
Create an account and key, dispatch an /api/inference call, read the response. The examples show the exact request shape — replace {{api}} with this site's base URL and you're live.
Step 1
Get an API key.
Every terminal request uses a personal cai_… API key. Keys are scoped to your account, revocable in one click from the dashboard, and never shown twice after creation. Signed-in browser sessions remain supported.
Already shipping? Sign in and head straight to /dashboard/api-keys.
Authentication
Create the key while signed in, copy the one-shot reveal, and send it as a bearer token. Terminal calls need only that key; unknown or revoked keys are rejected.
Authorization: Bearer YOUR_API_KEYStep 2
Make an inference call.
POST a JSON body to /api/inference with the model id, an optional prompt, the token budget, and a latency preference. Both examples below hit the same endpoint — pick the shape that matches your stack.
First call · curl
Replace the base URL and YOUR_API_KEY with your values. The bearer key authenticates both the inference and the savings lookup, and each response is scoped to that key.
# The API key is the only credential needed for terminal calls
curl --request POST "https://your-carbonlayer-domain/api/inference" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Draft a status update for a 12% emissions drop in Marseille.",
"model": "claude-sonnet",
"tokens": 800,
"preferLowLatency": false
}'
# Read savings for the same API-key scope
curl --request GET "https://your-carbonlayer-domain/api/savings" \
-H "Authorization: Bearer YOUR_API_KEY"fetch (signed-in browser)
Same contract from a signed-in browser session. Set the API base URL via process.env.NEXT_PUBLIC_APP_URL and keep the full key out of committed client code. The browser session and the matching bearer key are both sent to the route.
const res = await fetch(`${API}/api/inference`, {
method: 'POST',
credentials: 'include',
headers: {
'content-type': 'application/json',
authorization: 'Bearer cai_…',
},
body: JSON.stringify({
prompt: 'Draft a status update for a 12% emissions drop in Marseille.',
model: 'claude-sonnet',
tokens: 800,
preferLowLatency: false,
}),
});
const data = await res.json();
console.log(data.carbonGrams, data.waterMl);Step 3
Read the response.
The wire shape is the full InferenceDispatch — the dispatched device, energy, carbon and water figures in grams and millilitres, and a row of provenance chips. Every per-inference number carries a carbonSource / waterSource / energySource tag of measured or modeled; the per-device carbon / water intensity carries its own *IntensitySource on the device block.
Response shape
- response— the model's text (string).
- carbonGrams · carbonSource: modeled — grams of CO₂e attributed to this dispatch (number). The chip is modeled because every published number is a per-inference slice of facility totals, not a wattmeter reading.
- waterMl · waterSource: modeled — millilitres of cooling + process water attributed to this dispatch. Same modeled chip.
- device.carbonIntensity · carbonIntensitySource: measured — grams of CO₂e per kWh on the live grid mix the dispatcher chose right now (electricityMaps / wattTime, or the synthetic fallback). The chip is measured — a provider signal, not a derived attribution.
- device.waterIntensity · waterIntensitySource: measured — litres of water per kWh for the chosen site's cooling and process loops. The chip is measured — a per-site engineering coefficient, not a model.
- model — the model id the dispatcher actually ran (one of
claude-haiku, claude-sonnet, claude-opus, cl-compact-v1). - id — server-minted id for this dispatch (round-trips through
GET /api/savings).
Provenance
- Measured means a direct provider or facility input. In this response,
device.carbonIntensitySourceanddevice.waterIntensitySourcelabel the corresponding intensity values; treat them as measured when their source is evidenced. - Derived means arithmetic conversion, combination, or rollup of other values — not a new measurement. For example,
device.waterIntensityMlPerKwhcan be a unit conversion. Client-side totals remain derived while retaining the source context of their inputs; the wire response does not emit aderivedtag. - Modeled means an allocation, estimate, baseline, savings comparison, or proxy. The top-level
carbonSource,waterSource, andenergySourcemap respectively tocarbonGrams/baselineCarbonGrams/savedCarbonGrams,waterMl/baselineWaterMl/savedWaterMl, andenergyKwh. This endpoint returns them as modeled.
The root source (live or synthetic) and optional degraded flag describe the grid-data path or fallback. They are additional context, not replacements for metric source fields: synthetic or degraded inputs must not be presented as measured readings. The current MetricSource contract is two-state (measured | modeled); derived is a reading rule for calculations and rollups, not a new response value or schema change.
Use these source fields to pin down which side of the boundary each value sits on. The methodology page has the full definition: /docs/training-carbon-math.
{
"id": "cm_8f4e8a14_3b21_4e7c",
"createdAt": "2026-08-11T12:00:00.000Z",
"model": "claude-sonnet",
"tokens": 800,
"response": "12% drop quarter over quarter — Marseille now sits 38% below the regional average.",
"device": {
"id": "dev_overseasnorway",
"name": "Oslo Harbour Hydro Cluster",
"region": "eu-north-1",
"country": "Norway",
"lat": 59.91,
"lng": 10.75,
"carbonIntensity": 68,
"renewableMix": 0.97,
"waterIntensity": 0.32,
"latencyMs": 78,
"savedWaterMl": 0,
"waterIntensityMlPerKwh": 320,
"carbonIntensitySource": "measured",
"waterIntensitySource": "measured"
},
"energyKwh": 0.216045,
"carbonGrams": 14.691,
"baselineCarbonGrams": 129.627,
"savedCarbonGrams": 114.936,
"waterMl": 69.134,
"baselineWaterMl": 388.881,
"savedWaterMl": 319.747,
"renewablePct": 0.97,
"latencyMs": 78,
"carbonSource": "modeled",
"waterSource": "modeled",
"energySource": "modeled",
"source": "live",
"degraded": false
}Model ids
Pass any of these into the model field. The enum is the single source of truth — the page is rendered from the same zod contract the handler validates against.
[
"claude-haiku",
"claude-sonnet",
"claude-opus",
"cl-compact-v1"
]Need a deeper read on savings, devices, or scheduled jobs? GET /api/savings rolls everything up by region. GET /api/network lists the live device fleet.
Step 4
Rate limits & fair use.
The dispatcher is meant to be called frequently — the carbon math gets better the more it sees — so the Free tier is generous on purpose. Today the production playground runs on a fair-use envelope with light throttling.
Free tier
60 dispatches / minute / caller
Across a sliding one-minute window. Burst above that and the handler returns 429 with a retry hint; the dashboard surfaces your last 60 seconds.
Per-request cap
4 KB prompt · 50–4000 tokens
Larger prompts are rejected at the edge before they reach the dispatcher — keep your payload tight and the carbon math stays honest.
Fair use
Noisy callers get load-aware throttling
As call volume rises, the dispatcher shifts away from "cheapest device wins" toward load-aware scheduling so a single caller doesn't starve smaller-region capacity.
Healthcare vertical
Make emissions and water disclosure easier to audit.
Read the healthcare disclosure guide for the proposed Scope 1, Scope 2, Scope 3, and water fields around a vertical inference card.
No SDK, no surprises — copy the request shape, then hit POST.
The signed-in playground and the terminal examples use the same endpoints and return the same carbon + water numbers. Use the bearer key from a terminal; browser sessions remain supported for in-app calls.
CarbonLayer · Sustainable AI infrastructure that runs efficiently, wherever you need it.
Questions? Reach out through our contact form.