Per-feature, per-user cost attribution for LLM API calls. One line of code. Zero vendor lock-in. Free and open source.
The Problem
Most teams have one API key, one invoice, and zero visibility into which features or users drive cost.
How It Works
Wrap your existing API calls. That's it. No proxies, no config servers, no infrastructure changes.
const response = await meter(
() => client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: '...' }]
}),
{ feature: 'chat', userId: 'user_123' }
);
[llm-cost-meter] chat — $0.0105 (1,500 tokens, 820ms)
Feature Calls Total Cost
chat 312 $3.78
article-summarizer 843 $3.29
tag-classifier 129 $0.02
TOTAL 1,284 $7.09
Features
Detects OpenAI and Anthropic automatically from response shape
meterStream() tracks cost from streaming responses
Drop-in createExpressMiddleware() for your routes
Built-in pricing for GPT-4o, Claude Opus, o1, o3, and more
Charts, filters, drill-down, CSV export at localhost:3000
Group by feature, user, model. Export CSV, JSON.
POST events to Slack, Zapier, or any URL. Single or batch mode.
Export cost metrics to Datadog, New Relic, Honeycomb, Grafana.
Daily cost limits per feature. Get notified when thresholds are exceeded.
Code Examples
No SDK rewrites. No config files. Just wrap your calls.
import { meter, configure } from 'llm-cost-meter';
configure({ adapters: ['console', 'local'] });
const response = await meter(
() => openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Hello' }]
}),
{ feature: 'chat', userId: req.user.id }
);
import { meterStream } from 'llm-cost-meter';
const stream = await meterStream(
() => openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Write a poem' }],
stream: true
}),
{ feature: 'chat', userId: 'user_123' }
);
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}
// Cost tracked automatically when stream ends
import { createExpressMiddleware } from 'llm-cost-meter';
app.post('/api/chat',
createExpressMiddleware({ feature: 'chat' }),
async (req, res) => {
const response = await req.meter(() =>
client.messages.create({ ... })
);
res.json(response);
}
);
import { configureBudget, WebhookAdapter, OTelAdapter } from 'llm-cost-meter';
// Budget alerts — get notified when costs exceed limits
configureBudget({
rules: [
{ feature: 'chat', dailyLimitUSD: 50,
onExceed: (rule, spent) =>
sendSlack(`Chat exceeded $${rule.dailyLimitUSD}/day! Spent: $${spent.toFixed(2)}`)
},
{ feature: '*', dailyLimitUSD: 200,
onExceed: (rule, spent) => pageOncall('Global LLM budget exceeded')
},
]
});
// Webhook — POST events to Slack, Zapier, or any URL
configure({
adapters: ['local', new WebhookAdapter({
url: 'https://hooks.slack.com/services/...',
batchSize: 10, flushIntervalMs: 5000
})]
});
// OpenTelemetry — export to Datadog, New Relic, Honeycomb
configure({ adapters: ['local', new OTelAdapter()] });
Comparison
No proxies. No gateways. No infrastructure to maintain.
| Feature | API Keys per Service | AI Gateways | llm-cost-meter |
|---|---|---|---|
| Cost per feature | ✗ No | ✗ No | ✓ Yes |
| Cost per user | ✗ No | ✗ No | ✓ Yes |
| Cost per call | ✗ No | ✓ Yes | ✓ Yes |
| Cross-provider | ✗ No | ● Partial | ✓ Yes |
| No vendor lock-in | ✓ Yes | ✗ No | ✓ Yes |
| Setup time | None | Hours | 2 minutes |
| Budget alerts | ✗ No | ● Partial | ✓ Yes |
| OpenTelemetry export | ✗ No | ● Partial | ✓ Yes |
| Webhook notifications | ✗ No | ✗ No | ✓ Yes |
| Price | Free | $$$ | Free |
import { meter, configure } from 'llm-cost-meter';
configure({ adapters: ['console', 'local'] });
const response = await meter(
() => openai.chat.completions.create({ model: 'gpt-4o', messages }),
{ feature: 'chat', userId: user.id }
);