Stripe Billing Meters vs a usage ledger: when you need both
If you bill on Stripe, Billing Meters are the obvious place to send usage. A separate usage ledger isn't always needed. Here's what each one does, using only what Stripe's docs say (checked 2026-10-14), and when running both makes sense.
What Stripe Billing Meters do well
- Billing is built in. A meter attaches to a price, and Stripe aggregates meter events over the billing period and puts the result on the invoice (Stripe: configure a meter).
- Three aggregation formulas: Sum, Count and Last (the most recent value in the period) (Stripe: configure a meter).
- Retry protection. Each meter event can carry an
identifier. Stripe "enforces uniqueness within a rolling period of at least 24 hours" (Stripe API: create meter event). - Room for late events. A timestamp must be "within the past 35 calendar days" and no more than 5 minutes in the future (Stripe API: create meter event).
- Throughput. In live mode the Meter Event endpoint allows 1000 calls per second per account, with one concurrent call per customer per meter. Meter event streams (API v2) go up to 10,000 events per second, live mode only. In a sandbox, meter event calls count toward your global rate limit (Stripe: record usage with the API).
- Error reporting. Invalid events surface as
v1.billing.meter.error_report_triggeredevents (Stripe: record usage with the API).
Worth knowing: Stripe's own usage-recording page now says Billing Meters suit teams already billing with them, and recommends Metronome for new integrations (Stripe: record usage for billing).
Where a separate usage ledger helps
- An audit trail for every event. Stripe stores aggregates for billing. A ledger keeps each raw event (who, what, when, idempotency key) that you can query, export and show to a customer who disputes a bill.
- Unique counts. Stripe's formulas are Sum, Count and Last (source). If you bill on monthly active users or distinct seats, you have to count distinct values yourself before sending a number.
- Rollups you own. Stripe processes meter events asynchronously, so summaries and upcoming invoices "might not immediately reflect recently received meter events" (source). A ledger you can query directly gives your app and dashboard a number right away.
- Late events and corrections. Stripe accepts events up to 35 days old, but says that doesn't guarantee they reach an invoice: usage sent after an invoice finalizes may not be billed (source). Cancelling an event through a Meter Event Adjustment only works for events sent within the last 24 hours, and finalized invoices aren't corrected (source, API). After that window, your fix is a negative quantity. Keeping the original events gives you a record of what really happened.
- Dedup that lasts longer. Stripe's identifier uniqueness is guaranteed for at least 24 hours (source). A ledger can reject a replayed key for as long as it keeps the event.
- Changing billers. If usage lives only in Stripe and you move to Lago, Metronome or your own invoicing, you'd be rebuilding history. A ledger is independent of the biller.
- Showing customers their usage. An in-app usage page can read from your ledger instead of polling Stripe.
When you need both
- Stripe alone is fine if you meter one or two simple sums or counts, send them reliably, rarely correct data and don't need per-event history.
- Use both if you need unique counts, have customers who question their bills, get events late from queues or batch jobs, or might change billers. Keep events in the ledger as the record, and send Stripe one rolled-up quantity per customer per meter. Stripe's docs suggest pre-aggregating if you come near its rate limits (source).
How Usage Lab sits in front of Stripe
Usage Lab is a small usage ledger. You POST /api/v1/usage with an Idempotency-Key. Each event is stored once per key: a replay returns "duplicate": true and a different payload under the same key returns 409. Meters aggregate with sum, count or unique_count. Events up to 30 days late land in the month they happened, and rollups are recomputed from raw events.
Stripe push: dry-run / coming soon. Each rollup already tracks exported_quantity and pending_export_quantity, but Usage Lab doesn't send meter events to Stripe yet. For now, read the rollup and send the quantity yourself.
# Usage Lab: record one event (sum meter "llm_tokens")
curl -X POST https://usage-lab.marc-cf2.workers.dev/api/v1/usage \
-H "Authorization: Bearer $USAGE_LAB_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: chatcmpl-9x2a" \
-d '{"meter":"llm_tokens","subscription":"cus_123","value":1250,"timestamp":"2026-10-14T08:00:00Z"}'
# 201 {"event":{"status":"created",...},"duplicate":false}
# same key + same payload again -> 200 "duplicate": true
# same key + different payload -> 409 idempotency_conflict
# Usage Lab: monthly quantity per subscription (recomputed from raw events)
curl "https://usage-lab.marc-cf2.workers.dev/api/v1/meters/llm_tokens/rollups?month=2026-10&subscription=cus_123" \
-H "Authorization: Bearer $USAGE_LAB_KEY"
Response shape:
{
"meter": { "key": "llm_tokens", "aggregation": "sum", ... },
"month": "2026-10",
"computed_at": "...",
"total_quantity": 1250,
"rollups": [{
"subscription": "cus_123",
"quantity": 1250,
"event_count": 1,
"exported_quantity": 0,
"pending_export_quantity": 1250,
"exported_at": null
}]
}
And this is Stripe's meter event call, copied from Stripe's API reference. Use your own event_name, and a stable identifier so a retry doesn't double count:
# Stripe's API (from Stripe's docs), not Usage Lab
curl https://api.stripe.com/v1/billing/meter_events \
-u "<<YOUR_SECRET_KEY>>" \
-d event_name=ai_search_api \
-d "payload[value]=25" \
-d "payload[stripe_customer_id]={{CUSTOMER_ID}}" \
-d identifier=identifier_123
Try the full flow on the live demo or read the API docs.
Sources
- https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage
- https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage-api
- https://docs.stripe.com/billing/subscriptions/usage-based/meters/configure
- https://docs.stripe.com/api/billing/meter-event/create
- https://docs.stripe.com/api/billing/meter-event-adjustment/create
- Live end-to-end demo
- API docs
- Quickstart: meter an AI app
- Code examples
- Metering usage for Stripe usage-based billing
- Deduping usage events with idempotency keys
- Usage Lab vs UsageBox: an honest comparison
- Metronome and Orb alternatives for small SaaS teams
- How to meter LLM token usage per customer for billing