Quickstart · CarbonLayer inference API

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.

bash
Authorization: Bearer YOUR_API_KEY

Step 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.

bash
# 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.

js
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.carbonIntensitySource and device.waterIntensitySource label 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.waterIntensityMlPerKwh can be a unit conversion. Client-side totals remain derived while retaining the source context of their inputs; the wire response does not emit a derived tag.
  • Modeled means an allocation, estimate, baseline, savings comparison, or proxy. The top-level carbonSource, waterSource, and energySource map respectively to carbonGrams/baselineCarbonGrams/savedCarbonGrams, waterMl/baselineWaterMl/savedWaterMl, and energyKwh. 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.

json
{
  "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.

json
[
  "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.

Healthcare disclosure guide

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.