@meeticarus/sdk
v0.0.3
Published
Carbon emissions SDK for AI inference and network data transfer. Zero dependencies, works out of the box with bundled emission factors.
Downloads
50
Maintainers
Readme
@meeticarus/sdk
Zero-dependency SDK for calculating carbon emissions from AI inference and network data transfer — works out of the box with bundled emission factors, no account required.
Contents
- Installation
- Quick start
- Examples
- API reference
- How it works
- Supported models and regions
- Get an Icarus account
- License
Installation
npm install @meeticarus/sdk
# or
pnpm add @meeticarus/sdk
# or
yarn add @meeticarus/sdkZero runtime dependencies. Ships ESM + CJS with full TypeScript types.
Quick start
Option A — from raw token counts (synchronous, no dependencies):
import { trackAI } from "@meeticarus/sdk";
const result = trackAI({
provider: "openai",
model: "gpt-4o",
inputTokens: 500,
outputTokens: 200,
});
console.log(result.kgCO2e); // ~0.0000466 kg CO₂e
console.log(result.energyWh); // ~0.364 Wh
console.log(result.confidence); // "medium"Option B — wrap a live provider call (token counts extracted automatically):
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { withEmissions } from "@meeticarus/sdk/ai";
// anthropic() reads ANTHROPIC_API_KEY from the environment automatically
const { result, emission, latencyMs } = await withEmissions(() =>
generateText({
model: anthropic("claude-3-7-sonnet-20250219"),
prompt: "Hello",
}),
);
console.log(emission.kgCO2e); // ~0.0000466 kg CO₂e
console.log(emission.confidence); // "medium"
console.log(latencyMs); // wall-clock time for the API callNo Icarus account needed — both options use bundled emission factors out of the box. Option B requires your existing provider API key (OpenAI, Anthropic, etc.) to make the AI call, but nothing extra for emissions tracking.
Examples
Track AI emissions
Calculate the carbon footprint of a single AI call from raw token counts:
import { trackAI } from "@meeticarus/sdk";
const result = trackAI({
provider: "openai",
model: "gpt-4o",
inputTokens: 1_000,
outputTokens: 500,
});
console.log(result.kgCO2e); // e.g. 0.000412 — total emissions in kg CO₂e
console.log(result.energyWh); // total energy consumed in Wh (after PUE)
console.log(result.confidence); // "high" | "medium" | "low"
console.log(result.methodology); // "model-specific" | "median-fallback" | "size-class-fallback"Region-aware tracking
Pass a region to use the appropriate grid carbon intensity for the electricity powering the inference:
import { trackAI } from "@meeticarus/sdk";
// UK grid (0.128 kg CO₂e/kWh — relatively clean, predominantly renewable)
const ukResult = trackAI({
provider: "openai",
model: "gpt-4o",
inputTokens: 1_000,
outputTokens: 500,
region: "uk",
});
// French grid (0.041 kg CO₂e/kWh — predominantly nuclear)
const frResult = trackAI({
provider: "openai",
model: "gpt-4o",
inputTokens: 1_000,
outputTokens: 500,
region: "fr",
});
// Supported: "us", "uk" (default), "eu", "de", "fr"
// Unknown regions fall back to the UK factor.Unknown models with sizeHint
If a model isn't in the bundled factor table, the SDK falls back to a median energy profile. Provide a sizeHint to pick the closest size-class fallback instead:
import { trackAI } from "@meeticarus/sdk";
// claude-opus-4 isn't in the table yet — use sizeHint for a better estimate
const result = trackAI({
provider: "anthropic",
model: "claude-opus-4",
inputTokens: 2_000,
outputTokens: 800,
sizeHint: "large", // "small" | "medium" | "large"
});
console.log(result.methodology); // "size-class-fallback"
console.log(result.confidence); // "low" — always "low" for fallbacks
// sizeHint guidance:
// "small" — lightweight / fast-tier (e.g. Claude Haiku, Gemini Flash, GPT-4.1 nano)
// "medium" — mid-tier frontier (e.g. Claude Sonnet, GPT-4o-mini, GPT-4.1 mini)
// "large" — flagship / reasoning (e.g. Claude Opus, GPT-4o, GPT-4.1)Fetch live factors from the Icarus API
Bundled factors are updated with each SDK release. To always use the latest data without upgrading the package, fetch directly from the Icarus platform using an API key from your Icarus account:
Note: An Icarus API key is only needed here. Every other feature —
trackAI,withEmissions,trackHTTP, and all provider wrappers — works entirely offline with bundled factors.
import { createClient, trackAI } from "@meeticarus/sdk";
const client = createClient({
apiKey: process.env.ICARUS_API_KEY,
});
// Fetches once and caches for 24 hours by default
const factors = await client.fetchFactors();
const result = trackAI({
provider: "openai",
model: "gpt-4o",
inputTokens: 1_000,
outputTokens: 500,
factors, // use the live factors from the API
});You can force a cache refresh:
const freshFactors = await client.fetchFactors({ force: true });Or configure a custom TTL (in milliseconds):
const client = createClient({
apiKey: process.env.ICARUS_API_KEY,
ttl: 6 * 60 * 60 * 1000, // refresh every 6 hours
});
// Set ttl: 0 to disable caching entirely
const noCacheClient = createClient({
apiKey: process.env.ICARUS_API_KEY,
ttl: 0,
});Merge custom factors
Use createFactors() to layer your own overrides on top of the bundled defaults. Useful for region-specific utilities, internal energy measurements, or models not yet in the default table:
import { createFactors, trackAI } from "@meeticarus/sdk";
const factors = createFactors({
// Replace the US grid intensity with your utility's measured value
gridIntensity: [
{
region: "us",
kgCO2ePerKWh: 0.21, // e.g. Pacific Northwest hydro mix
source: "Your utility 2025 annual disclosure",
validFrom: "2025-01-01",
},
],
// Add a model-specific factor for an internal fine-tuned model
aiModels: [
{
provider: "acme",
model: "acme-7b-fine-tuned",
whPerInputToken: 1.1e-4,
whPerOutputToken: 2.2e-4,
pue: 1.18,
source: "Internal measurement Q1 2025",
confidence: "high",
validFrom: "2025-01-01",
},
],
});
const result = trackAI({
provider: "acme",
model: "acme-7b-fine-tuned",
inputTokens: 500,
outputTokens: 300,
region: "us",
factors,
});Merge semantics:
aiModels— custom entries replace any default with the sameprovider+model; all other defaults are kept.gridIntensity— custom entries replace any default with the sameregion; all others kept.http— replaces the default entirely when provided.
Inspect the bundled factor tables
import { defaultFactors } from "@meeticarus/sdk";
// List all covered AI models
defaultFactors.aiModels.forEach((f) => {
console.log(`${f.provider}/${f.model} — confidence: ${f.confidence}`);
});
// List bundled grid regions
defaultFactors.gridIntensity.forEach((g) => {
console.log(`${g.region}: ${g.kgCO2ePerKWh} kg CO₂e/kWh`);
});Reading the result
Every trackAI() call returns an EmissionResult with the full audit trail:
import { trackAI } from "@meeticarus/sdk";
const result = trackAI({
provider: "openai",
model: "gpt-4o",
inputTokens: 1_000,
outputTokens: 500,
region: "uk",
});
// Top-level figures
result.kgCO2e; // total greenhouse gas emissions in kg CO₂e
result.energyWh; // total energy consumed in Wh (after PUE)
result.scope; // GHG Protocol scope — AI inference is always "3.1"
// Auditability
result.methodology; // "model-specific" | "median-fallback" | "size-class-fallback"
result.confidence; // "high" | "medium" | "low"
result.factorSource; // citation for the emission factor used
// Verbatim inputs recorded on the result
result.inputs.provider; // "openai"
result.inputs.model; // "gpt-4o"
result.inputs.inputTokens; // 1000
result.inputs.outputTokens; // 500
result.inputs.region; // "uk"
// Full calculation breakdown
result.breakdown.inputEnergyWh; // energy from input tokens (pre-PUE)
result.breakdown.outputEnergyWh; // energy from output tokens (pre-PUE)
result.breakdown.rawEnergyWh; // inputEnergyWh + outputEnergyWh
result.breakdown.pue; // data center overhead multiplier
result.breakdown.gridRegion; // "uk"
result.breakdown.kgCO2ePerKWh; // grid intensity applied
// Warnings — present when the model was not found in the factor table
if (result.warnings) {
console.warn(result.warnings[0]);
// e.g. "No emission factor found for 'anthropic/claude-future' — it may be a
// newer model not yet in the bundled data. Pass sizeHint ('small' | 'medium' |
// 'large') for a better estimate, or use createClient().fetchFactors()..."
}Provider wrappers
If you're calling a provider API directly, the withEmissions() wrappers handle token extraction automatically. Install the wrapper for your provider — they are peer dependencies so you only install what you use.
Vercel AI SDK (recommended — works with any provider)
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { withEmissions } from "@meeticarus/sdk/ai";
const { result, emission, latencyMs } = await withEmissions(() =>
generateText({ model: openai("gpt-4o"), prompt: "Hello" }),
);
console.log(`${emission.kgCO2e.toExponential(2)} kg CO₂e (${latencyMs}ms)`);Provider is inferred automatically from the model name — switching from openai("gpt-4o") to anthropic("claude-3-7-sonnet-20250219") requires no changes to your emissions tracking code.
For streamText, use trackAIResult() after the stream completes:
import { streamText } from "ai";
import { trackAIResult } from "@meeticarus/sdk/ai";
const stream = streamText({ model: openai("gpt-4o"), prompt: "Hello" });
for await (const chunk of stream.textStream) {
/* ... */
}
const emission = trackAIResult({
usage: await stream.usage,
response: await stream.response,
});OpenAI
import { withEmissions } from "@meeticarus/sdk/openai";
const { result, emission, latencyMs } = await withEmissions(() =>
openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
}),
);
console.log(`${emission.kgCO2e.toExponential(2)} kg CO₂e (${latencyMs}ms)`);
// result is the unmodified OpenAI responseThe model name is extracted from the response and normalised — "gpt-4o-2024-08-06" → "gpt-4o" — for accurate factor lookup.
Anthropic
import { withEmissions } from "@meeticarus/sdk/anthropic";
const { result, emission } = await withEmissions(() =>
anthropic.messages.create({
model: "claude-3-7-sonnet-20250219",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
}),
);
console.log(`${emission.kgCO2e.toExponential(2)} kg CO₂e`);Date suffixes are stripped automatically — "claude-3-7-sonnet-20250219" → "claude-3-7-sonnet".
Google Gemini
import { withEmissions } from "@meeticarus/sdk/google";
// Works with both @google/generative-ai and @google/genai
const { result, emission } = await withEmissions(
() => model.generateContent("Hello"),
{ model: "gemini-2.0-flash" }, // recommended — not always in the response
);
console.log(`${emission.kgCO2e.toExponential(2)} kg CO₂e`);
// Gemini Flash carries confidence: "high" — first-party energy data from GooglePass model in options — unlike OpenAI and Anthropic, the Gemini response does not always include the model name. If omitted, the wrapper falls back to response.modelVersion when available, then to a median estimate.
All wrappers accept the same optional second argument:
withEmissions(fn, {
region: "uk", // grid region (default: "uk")
factors, // live factors from createClient().fetchFactors()
// Google only:
model: "gemini-2.0-flash",
sizeHint: "large", // fallback size class if model is unknown
});Track HTTP emissions
Calculate the carbon footprint of network data transfer from raw byte counts. Useful for measuring the emissions from API responses, file downloads, or streaming:
import { trackHTTP } from "@meeticarus/sdk/carbon";
// Basic: bytes transferred → kgCO2e using the base data-transfer factor
const result = trackHTTP({ bytes: 1_500_000 }); // 1.5 MB
console.log(result.kgCO2e); // emissions in kg CO₂e
console.log(result.energyWh); // energy consumed in Wh
console.log(result.methodology); // "data-transfer"
console.log(result.scope); // "3.11" (use of sold products)Use the category option to select a workload-specific energy profile:
// Heavy server-side compute — 2× the base factor
const heavyResult = trackHTTP({
bytes: 1_500_000,
category: "compute-heavy",
region: "uk",
});
// Lighter options
trackHTTP({ bytes: 1_500_000, category: "cdn" }); // 0.06 kWh/GB (base)
trackHTTP({ bytes: 1_500_000, category: "api" }); // 0.07 kWh/GB
trackHTTP({ bytes: 1_500_000, category: "database" }); // 0.07 kWh/GB
trackHTTP({ bytes: 1_500_000, category: "streaming" }); // 0.08 kWh/GB
trackHTTP({ bytes: 1_500_000, category: "compute-heavy" }); // 0.12 kWh/GBTracked fetch
createFetchWithEmissions() wraps the standard fetch and automatically attaches an emission property to every response. The response body remains fully readable after the emission is calculated:
import { createFetchWithEmissions } from "@meeticarus/sdk/http";
const fetch = createFetchWithEmissions({ region: "uk", category: "api" });
const response = await fetch("https://api.example.com/data");
console.log(response.emission.kgCO2e); // kg CO₂e for this response
console.log(response.emission.inputs.bytes); // bytes measured
const data = await response.json(); // body still readable as normalByte size is measured from the Content-Length response header when present; otherwise the response body is read once to measure size (the original body stream remains unconsumed).
Inject a custom fetch implementation for testing:
const trackedFetch = createFetchWithEmissions({
region: "us",
fetch: myMockFetch,
});API reference
trackAI(options): EmissionResult
Calculate emissions for a single AI inference call. Synchronous, browser-safe.
| Option | Type | Required | Default | Description |
| -------------- | -------------------------------- | -------- | ---------------- | ----------------------------------------------------------------------------------- |
| provider | string | Yes | — | Provider identifier, e.g. "openai", "anthropic" |
| model | string | Yes | — | Model identifier, e.g. "gpt-4o" |
| inputTokens | number | Yes | — | Number of input (prompt) tokens |
| outputTokens | number | Yes | — | Number of output (completion) tokens |
| region | string | No | "uk" | Grid region for carbon intensity lookup. Unknown regions fall back to the UK factor |
| sizeHint | "small" \| "medium" \| "large" | No | — | Size-class fallback when model is unknown |
| factors | Factors | No | defaultFactors | Override the entire factor table. Use createFactors() to merge with defaults |
createClient(options): IcarusClient
Create an authenticated client for fetching live emission factors from the Icarus API.
| Option | Type | Required | Default | Description |
| --------- | -------- | -------- | ------------------------------ | ---------------------------------------------------- |
| apiKey | string | Yes | — | Icarus API key (sent as Authorization: Bearer) |
| baseUrl | string | No | "https://www.meeticarus.com" | Override for self-hosted deployments |
| ttl | number | No | 86400000 (24h) | Cache lifetime in milliseconds. 0 disables caching |
client.fetchFactors(options?): Promise<Factors>
Fetches the latest Factors from GET /api/v1/factors/ai. Results are cached in memory for the configured ttl. Pass { force: true } to bypass the cache and re-fetch.
withEmissions(fn, options?) — provider wrappers
Exported from @meeticarus/sdk/openai, @meeticarus/sdk/anthropic, and @meeticarus/sdk/google.
| Provider | Import | Peer dependency |
| ------------- | --------------------------- | --------------------------------- |
| Vercel AI SDK | @meeticarus/sdk/ai | ai >= 4.0.0 |
| OpenAI | @meeticarus/sdk/openai | openai >= 4.0.0 |
| Anthropic | @meeticarus/sdk/anthropic | @anthropic-ai/sdk >= 0.20.0 |
| Google Gemini | @meeticarus/sdk/google | @google/generative-ai >= 0.21.0 |
Returns { result, emission, latencyMs }. All peer dependencies are optional — install only the ones you use.
createFactors(overrides?): Factors
Merge custom factors with defaultFactors. Returns defaultFactors unchanged when called with no arguments.
import { createFactors } from "@meeticarus/sdk";
const factors = createFactors({
gridIntensity: [...],
aiModels: [...],
http: { kWhPerGB: 0.05, source: "custom" },
});trackHTTP(options): EmissionResult
Calculate emissions for a network data transfer. Synchronous, browser-safe. Exported from @meeticarus/sdk/carbon.
| Option | Type | Required | Default | Description |
| ---------- | ---------------------------------------------------------------- | -------- | ---------------- | ----------------------------------------------------------------------------------- |
| bytes | number | Yes | — | Total bytes transferred |
| category | "cdn" \| "api" \| "database" \| "streaming" \| "compute-heavy" | No | — | Workload category for a more accurate energy estimate (see category profiles below) |
| region | string | No | "us" | Grid region for carbon intensity lookup. Unknown regions fall back to the US factor |
| factors | Factors | No | defaultFactors | Override the entire factor table. Use createFactors() to merge with defaults |
Category profiles
| Category | kWh/GB | Multiplier | Use case |
| --------------- | ------ | ---------- | -------------------------------------------------- |
| (default) | 0.06 | 1.0× | Generic data transfer (Shift Project / IEA 2022) |
| cdn | 0.06 | 1.0× | Static assets from a CDN cache |
| api | 0.07 | 1.17× | REST or GraphQL API responses |
| database | 0.07 | 1.17× | Database query results |
| streaming | 0.08 | 1.33× | Continuous audio/video streams |
| compute-heavy | 0.12 | 2.0× | Heavy server-side compute (rendering, ML proxying) |
createFetchWithEmissions(options?)
Returns a fetch-compatible function that measures response size and attaches emission: EmissionResult to every response. Exported from @meeticarus/sdk/http.
| Option | Type | Required | Default | Description |
| ---------- | ---------------------------------------------------------------- | -------- | ------------------ | ------------------------------------------------ |
| region | string | No | "us" | Grid region for carbon intensity lookup |
| category | "cdn" \| "api" \| "database" \| "streaming" \| "compute-heavy" | No | — | Workload category (see category profiles above) |
| factors | Factors | No | defaultFactors | Override the entire factor table |
| fetch | typeof globalThis.fetch | No | globalThis.fetch | Custom fetch implementation (useful for testing) |
defaultFactors
The full bundled Factors object: { aiModels, gridIntensity, http }. Import to inspect covered models, regions, sources, and confidence tiers.
import { defaultFactors } from "@meeticarus/sdk";
// same as:
import { defaultFactors } from "@meeticarus/sdk/carbon";How it works
Each trackAI() call runs a three-step calculation:
rawEnergyWh = (inputTokens × whPerInputToken) + (outputTokens × whPerOutputToken)
energyWh = rawEnergyWh × pue
kgCO2e = (energyWh / 1000) × kgCO2ePerKWhwhPerInputToken/whPerOutputToken— model-specific energy per token, derived from published measurements or a size-class fallback profile.pue(Power Usage Effectiveness) — data center overhead multiplier that accounts for cooling and infrastructure. Attached to each model factor (varies by provider/data center).kgCO2ePerKWh— grid carbon intensity for the specified region.
Confidence tiers
| Tier | Meaning |
| -------- | ----------------------------------------------------------------------- |
| high | First-party data published by the provider |
| medium | Peer-reviewed research (e.g. Jegham et al. 2025, arXiv:2505.09598) |
| low | Statistical fallback — median or size-class profile from published data |
Methodologies
| Methodology | When used |
| --------------------- | ---------------------------------------------------------------- |
| model-specific | Exact provider/model found in the factor table |
| size-class-fallback | Model not found; sizeHint provided |
| median-fallback | Model not found; no sizeHint — uses the overall median profile |
| data-transfer | HTTP byte-count tracking via trackHTTP() or tracked fetch |
Supported models and regions
Bundled AI models
| Provider | Models |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| OpenAI | gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o1, o1-mini, o3, o3-mini, o4-mini |
| Anthropic | claude-3-7-sonnet (other Claude models use sizeHint fallback) |
| Google | gemini-1.5-flash, gemini-2.0-flash, gemini-2.5-flash |
| DeepSeek | deepseek-r1, deepseek-v3 |
| Meta | llama-3.1-8b, llama-3.1-70b, llama-3.3-70b |
Models not listed fall back automatically to the median profile. Use sizeHint for a better estimate.
Bundled grid regions
| Region | Grid intensity | Source |
| -------------- | ----------------- | ------------------------------------------------- |
| us | 0.386 kg CO₂e/kWh | EPA eGRID 2024 / IEA 2025 |
| uk (default) | 0.128 kg CO₂e/kWh | DEFRA/DESNZ 2025 |
| eu | 0.175 kg CO₂e/kWh | IEA Electricity 2025 |
| de | 0.331 kg CO₂e/kWh | IEA Emissions Factors 2025 |
| fr | 0.041 kg CO₂e/kWh | IEA Emissions Factors 2025 |
| cn | 0.6 kg CO₂e/kWh | IEA / Jegham et al. 2025 — China national average |
Unknown regions fall back to the US factor. Add any region via createFactors().
Get an Icarus account
trackAI() works immediately with no account using the bundled factor tables, which are updated with each SDK release.
To always use the latest factors without upgrading the SDK, connect to the Icarus API:
- Sign up at www.meeticarus.com
- Create your organisation during onboarding
- Generate an API key from your dashboard settings
- Pass it to
createClient():
const client = createClient({ apiKey: process.env.ICARUS_API_KEY });
const factors = await client.fetchFactors();
const result = trackAI({ ..., factors });For enterprise pilots or bespoke integrations, reach out at [email protected].
License
MIT — see LICENSE.
