Lago alternative: Usage Lab vs Lago, honestly
Lago is a full open-source billing platform: metering, plans, invoicing, credits and payments. Usage Lab is a small hosted metering API that keeps Stripe as your biller. Here's what each costs, where each fits, and how to move events across.
Every Lago fact below was checked on the official source linked next to it, as of Oct 21, 2026. Where Lago publishes no price we say "Contact sales". Usage Lab is our product; corrections are welcome at canada.site.ops@gmail.com.
Lago facts (as of Oct 21, 2026)
| Topic | Fact | Source |
|---|---|---|
| Open Source | $0 software license under AGPL-3.0, self-hosted. Infrastructure and operating costs are not included. | Lago docs: pricing and packaging |
| Premium on Cloud | Custom quote, on Lago-managed cloud: Contact sales. Lago "does not publish a universal monthly or annual Premium price"; the currency is set in the order form. | Lago docs: pricing, getlago.com/pricing |
| Premium self-hosted | Custom quote: Contact sales. You run the infrastructure. | Lago docs: pricing |
| Licence | GNU Affero General Public License v3.0 (AGPL-3.0), actively maintained on GitHub. | github.com/getlago/lago |
| Billing features | Billable metrics and usage ingestion, plans and subscriptions, invoice generation, credit notes, entitlements, prepaid credits, progressive billing, and payments integration. Some items, such as tax integrations and automatic dunning, are Premium add-ons. | getlago.com/pricing |
| Credits and wallets | Prepaid credits in wallets: up to 6 active wallets per customer, with top-ups, scoping and expiry. | Wallets docs |
| Stripe | A native Stripe integration collects payments automatically when Lago generates invoices. Lago is the biller; Stripe takes the payments. | Stripe integration docs |
| Events | transaction_id, external_subscription_id, code, timestamp and properties. transaction_id is used for deduplication; the ClickHouse pipeline keys on transaction_id + timestamp. | Ingesting usage, Events API |
Usage Lab pricing for comparison: Free CA$0 (100k events, 3 meters a month), Pro CA$12/month (2M events, 10 meters), Ultra CA$36/month (20M events, 100 meters). See pricing.
When Lago is the better fit
- You want a full billing platform. Lago does plans, subscriptions, invoices, credit notes, taxes and dunning. Usage Lab only meters; Stripe or your code bills.
- Open source that you host yourself. AGPL-3.0, free to run on your own infrastructure. Usage Lab is hosted only and not open source.
- Credits and wallets. Prepaid credits, top-ups and wallet alerts are built in. Usage Lab has none.
- Entitlements, progressive billing and finance workflows. Quotes, revenue analytics and CRM/accounting integrations (some are Premium).
- High-volume pipelines. Kafka, Kinesis and S3 delivery, plus a ClickHouse event store.
Where Usage Lab fits
- Stripe already bills. You just need trustworthy usage numbers in front of it. Stripe push (beta, Pro and Ultra) sends retry-safe monthly deltas to your own Stripe Billing Meters.
- A small team that wants it hosted. Nothing to deploy or upgrade: get a key (no card) and send events.
- Flat, published CA$ pricing. CA$0, 12 or 36 a month: no quote, no share of billing volume.
- Explainable rollups. Monthly per-customer totals with event counts, recomputed from the raw ledger, plus CSV.
- Strict retry dedup and late events. A required idempotency key, a 409 if the same key arrives with a different body, and late events accepted up to 30 days back. See idempotent usage events and late events.
Usage Lab is early access and small: no invoicing, credits, wallets, entitlements or SLAs.
Migration: Lago events to Usage Lab events
| Lago event | Usage Lab event | Notes |
|---|---|---|
code | meter | Create one Usage Lab meter per billable metric code (sum, count or unique_count). |
external_subscription_id | subscription | The customer or subscription you bill. Totals are per subscription per month. |
transaction_id | idempotency_key (or the Idempotency-Key header) | Keep the same id so a retry counts once. Usage Lab never replaces an event: the same key with a different body returns 409. |
timestamp | timestamp | Unix seconds work as-is, and so does ISO 8601. Up to 30 days late is accepted. |
properties[field_name] | value | The field your sum metric aggregates. Count metrics need no value; unique count uses unique_value. |
rest of properties | properties | Stored with the event, not aggregated. |
Lago event (the "event" object of Lago's POST /api/v1/events). Input format, not a command.
{
"transaction_id": "inf_2026-10_sub8832_00001",
"external_subscription_id": "sub_8832",
"code": "llm_tokens",
"timestamp": 1790812800,
"properties": { "tokens": 1500, "model": "gpt-4o-mini" }
}
The same event for Usage Lab (one item of POST /api/v1/usage {"events": [...]}). Not a command.
{
"meter": "llm_tokens",
"subscription": "sub_8832",
"idempotency_key": "inf_2026-10_sub8832_00001",
"timestamp": 1790812800,
"value": 1500,
"properties": { "model": "gpt-4o-mini" }
}
This script creates the meters, converts example Lago events, sends them as one batch, sends the batch again to show the retry counts once, and prints this month's totals. It needs only Python's standard library and USAGE_LAB_KEY (a free key from /signup/). Save it as lago_to_usage_lab.py and run python3 lago_to_usage_lab.py.
# lago_to_usage_lab.py: Python 3.8+, standard library only. Needs USAGE_LAB_KEY in the environment.
# Re-sends Lago-style usage events to Usage Lab. Safe to re-run: same transaction_id = counted once.
import json, os, sys, urllib.error, urllib.request
from datetime import datetime, timezone
API = "https://usage-lab.pages.dev/api/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["USAGE_LAB_KEY"], "Content-Type": "application/json",
"User-Agent": "lago-to-usage-lab/1.0"} # any descriptive User-Agent; see the note below the sample
# Your Lago billable metrics, re-declared: code -> (Usage Lab meter, aggregation, field_name)
METRICS = {
"llm_tokens": ("llm_tokens", "sum", "tokens"), # Lago sum_agg on properties.tokens
"api_requests": ("api_requests", "count", None), # Lago count_agg: no field needed
}
def call(method, path, body=None):
data = None if body is None else json.dumps(body).encode()
req = urllib.request.Request(API + path, data=data, headers=HEADERS, method=method)
try:
with urllib.request.urlopen(req, timeout=20) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.load(e)
def to_usage_lab(ev):
meter, _agg, field = METRICS[ev["code"]] # code -> meter
props = dict(ev.get("properties") or {})
out = {"meter": meter,
"subscription": ev["external_subscription_id"], # external_subscription_id -> subscription
"idempotency_key": ev["transaction_id"]} # transaction_id -> idempotency key
if ev.get("timestamp") is not None:
out["timestamp"] = ev["timestamp"] # unix seconds work as-is (ISO 8601 too)
if field:
out["value"] = float(props.pop(field)) # properties[field_name] -> value
if props:
out["properties"] = props # the rest stays as properties
return out
# 1. Create the meters (409 meter_exists on a re-run is fine)
for _code, (key, agg, _field) in METRICS.items():
status, body = call("POST", "/meters", {"key": key, "aggregation": agg, "name": key})
if status not in (201, 409):
sys.exit(f"create meter {key}: HTTP {status} {body}")
# 2. Example events, as you would have sent them to Lago's POST /api/v1/events
now = datetime.now(timezone.utc)
month = now.strftime("%Y-%m")
ts = int(datetime(now.year, now.month, 1, tzinfo=timezone.utc).timestamp())
lago_events = [
{"transaction_id": f"inf_{month}_sub8832_00001", "external_subscription_id": "sub_8832",
"code": "llm_tokens", "timestamp": ts, "properties": {"tokens": 1500, "model": "gpt-4o-mini"}},
{"transaction_id": f"inf_{month}_sub8832_00002", "external_subscription_id": "sub_8832",
"code": "llm_tokens", "timestamp": ts, "properties": {"tokens": 620, "model": "gpt-4o-mini"}},
{"transaction_id": f"req_{month}_sub9001_00001", "external_subscription_id": "sub_9001",
"code": "api_requests", "timestamp": ts, "properties": {"route": "/v1/search"}},
]
# 3. Send as one batch, then re-send it: the retry is counted once
batch = {"events": [to_usage_lab(e) for e in lago_events]}
for attempt in ("first send", "retry"):
status, body = call("POST", "/usage", batch)
if status >= 400:
sys.exit(f"{attempt}: HTTP {status} {body}")
print(attempt, body["summary"])
# 4. Read this month's totals per subscription
for key in ("llm_tokens", "api_requests"):
status, body = call("GET", f"/meters/{key}/rollups?month={month}")
if status != 200:
sys.exit(f"rollups {key}: HTTP {status} {body}")
for r in body["rollups"]:
print(key, r["subscription"], r["quantity"])
Example output, not a command.
first send {'created': 3, 'duplicate': 0, 'conflict': 0}
retry {'created': 0, 'duplicate': 3, 'conflict': 0}
llm_tokens sub_8832 2120
api_requests sub_9001 1
User-Agent note: the Cloudflare edge in front of *.pages.dev rejects Python's default Python-urllib User-Agent (error 1010) before the request reaches us. Any descriptive User-Agent works, including AI agents, bots and scripts, which are welcome.
To move history, export events from Lago, feed them through to_usage_lab() in batches of up to 100, and backfill only the last 30 days. Keep invoices and older usage in Lago or your warehouse.
More comparisons
- Usage metering compared (Stripe meters, Orb, Lago, Metronome, OpenMeter)
- Usage Lab vs OpenMeter (Kong)
- Usage Lab vs UsageBox
FAQ
Is Lago open source?
Yes. As of Oct 21, 2026 the getlago/lago repository on GitHub is licensed under AGPL-3.0 and actively maintained. Lago Open Source is a $0 software license that you self-host; you pay for and run the infrastructure.
How much does Lago cost?
As of Oct 21, 2026: Lago Open Source is a $0 software license under AGPL-3.0 (self-hosted, infrastructure not included). Lago Premium, on Lago Cloud or self-hosted, is a custom quote: Lago does not publish a fixed price, so it is Contact sales.
Does Lago have a hosted cloud?
Yes. Lago Premium is available on Lago-managed Cloud and as a self-hosted deployment. The free open-source edition is self-hosted only.
Can I move my Lago events to Usage Lab?
Yes. Map code to meter, external_subscription_id to subscription, transaction_id to the idempotency key, timestamp to timestamp (unix seconds work as-is), the billable metric's field_name in properties to value, and keep the rest as properties. The Python sample on this page runs as-is.
When should I stay on Lago?
When you need a full billing platform: invoicing, credit notes, prepaid credits and wallets, plans and subscriptions, dunning, taxes, or an open-source biller you host yourself. Usage Lab only meters usage and leaves billing to Stripe.
What does Usage Lab cost?
Free: CA$0 for 100,000 events and 3 meters a month. Pro: CA$12 a month for 2M events and 10 meters. Ultra: CA$36 a month for 20M events and 100 meters. Flat prices, never a percentage of your billings.
- Playground: validate a usage event in your browser
- Usage event validator: check event JSON and idempotency keys
- Drop-in LLM metering helper + margin calculator
- Usage-based pricing calculator (LLM tokens or events)
- Live end-to-end demo
- API docs
- Quickstart: meter an AI app
- Meter Vercel AI SDK, OpenAI and Anthropic tokens (input + output)
- Code examples
- Usage metering compared: Usage Lab vs Stripe meters, Orb, Lago, Metronome, OpenMeter
- OpenMeter alternative: Usage Lab vs OpenMeter (Kong)
- Metering usage for Stripe usage-based billing
- Deduping usage events with idempotency keys
- Backfilling late usage events: replays, late windows and adjustments
- 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
- Stripe Billing Meters vs a usage ledger: when you need both