OpenMeter alternative: Usage Lab vs OpenMeter (Kong), honestly
OpenMeter is a strong open-source metering and billing platform, and since 2025 it's part of Kong. Usage Lab is a small hosted metering API in front of Stripe. Here's what each costs, where each fits, and how to move CloudEvents across if you want to.
Every OpenMeter and Kong fact below was checked on the official source linked next to it, as of Oct 20, 2026. Where no price is published we say "Contact sales". Usage Lab is our product; corrections are welcome at canada.site.ops@gmail.com.
OpenMeter facts (as of Oct 20, 2026)
| Topic | Fact | Source |
|---|---|---|
| Ownership | Kong announced it acquired OpenMeter on September 3, 2025, planning to integrate it into Kong Konnect and keep it open source. | Kong press release, Kong blog |
| openmeter.io | Still loads; it does not redirect. It's titled "OpenMeter by Kong" and links to "Konnect Metering & Billing". | openmeter.io |
| Cloud tiers | "OpenMeter Cloud is now Konnect Metering & Billing." No tier prices published on that page: "Start for free" or "Contact Sales". | openmeter.io/pricing |
| Konnect price | Plus: "From $25/month plus usage", billed monthly, 30-day free trial. Metering & Billing: ingested events "$20 per-million events" (Plus: up to 10M events); billing volume "0.4% of billing volume", measured in USD (Plus: up to $250,000). Enterprise: custom, billed annually, Contact sales (volume discounts). | konghq.com/pricing (AI Management tab), Konnect Metering & Billing |
| Licence | Apache License 2.0, actively maintained on GitHub. | github.com/openmeterio/openmeter (LICENSE) |
| Self-hosting | You run the API and workers plus Kafka, ClickHouse and PostgreSQL (a Docker Compose stack for local use). | README |
| Events | CloudEvents (specversion, type, id, source, time, subject, data); deduplicated by source + id. | Usage events docs |
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.
Where OpenMeter or Kong is the better fit
- You want to self-host open source. OpenMeter is Apache 2.0, so you can run and change it yourself. Usage Lab is hosted only and not open source.
- Very high volume streaming. OpenMeter is built on Kafka and ClickHouse for real-time aggregation at scale. Usage Lab's largest plan is 20M events a month.
- You already run Kong. Konnect Metering & Billing sits next to your Kong gateway, so API traffic can be metered where it already flows.
- Entitlements and usage limits. OpenMeter has entitlements, plans, subscriptions and invoicing. Usage Lab only meters.
Where Usage Lab is the better fit
- A small team that wants it hosted. No Kafka, ClickHouse or Postgres to run: get a key (no card) and send events.
- Flat CA$ pricing. CA$0, 12 or 36 a month, never a share of your billing volume.
- Explainable rollups. Monthly per-customer totals with event counts, recomputed from the raw ledger, plus CSV.
- Retry dedup you can't forget. An idempotency key is required on every event; the same key with a different body returns 409 instead of double-counting. See idempotent usage events.
- Late events. Accepted up to 30 days back and counted in the month they happened. See backfilling late events.
- Stripe stays your biller. Stripe push (beta, Pro and Ultra) sends retry-safe monthly deltas to your own Stripe Billing Meters.
Usage Lab is early access and small: no SLAs, entitlements, invoicing or SQL queries.
Migration: CloudEvents to Usage Lab events
| OpenMeter (CloudEvents) | Usage Lab event | Notes |
|---|---|---|
type | meter | OpenMeter matches events to meters by type. Create one Usage Lab meter per type (sum, count or unique_count). |
subject | subscription | The customer you bill. Totals are per subscription per month. |
source + id | idempotency_key (or the Idempotency-Key header) | Join them, e.g. chat-api:2026-10-0001, so retries and replays still count once. |
time | timestamp | ISO 8601. Up to 30 days late is accepted. |
data at the meter's valueProperty | value | A number. Numeric strings such as "1500" must be converted. COUNT meters need no value; UNIQUE_COUNT maps to unique_value. |
rest of data | properties | Stored with the event, not aggregated. OpenMeter groupBy dimensions have no direct equivalent. |
specversion | (dropped) | Not needed. |
OpenMeter CloudEvent (input format, not a command).
{
"specversion": "1.0",
"type": "prompt",
"id": "2026-10-0001",
"source": "chat-api",
"time": "2026-10-01T00:00:00Z",
"subject": "customer-1",
"data": { "tokens": "1500", "model": "gpt-4o-mini" }
}
The same event as a Usage Lab event (one item of POST /api/v1/usage {"events": [...]}). Not a command.
{
"meter": "llm_tokens",
"subscription": "customer-1",
"idempotency_key": "chat-api:2026-10-0001",
"timestamp": "2026-10-01T00:00:00Z",
"value": 1500,
"properties": { "model": "gpt-4o-mini" }
}
This script creates the meters, converts example CloudEvents, 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 cloudevents_to_usage_lab.py and run python3 cloudevents_to_usage_lab.py.
# cloudevents_to_usage_lab.py: Python 3.8+, standard library only. Needs USAGE_LAB_KEY in the environment.
# Re-sends OpenMeter-style CloudEvents to Usage Lab. Safe to re-run: same source + 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": "cloudevents-to-usage-lab/1.0"} # urllib's default User-Agent is blocked at the edge
# Your OpenMeter meters, re-declared: CloudEvents type -> (Usage Lab meter, aggregation, valueProperty)
METERS = {
"prompt": ("llm_tokens", "sum", "$.tokens"), # OpenMeter SUM meter on data.tokens
"request": ("api_requests", "count", None), # OpenMeter COUNT meter: no value 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(ce):
meter, _agg, prop = METERS[ce["type"]] # type -> meter
data = dict(ce.get("data") or {})
ev = {"meter": meter,
"subscription": ce["subject"], # subject -> subscription (the customer you bill)
"idempotency_key": ce["source"] + ":" + ce["id"]} # OpenMeter dedupes on source + id; so do we
if ce.get("time"):
ev["timestamp"] = ce["time"] # time -> timestamp (late events land in their month)
if prop:
ev["value"] = float(data.pop(prop[2:])) # data.<valueProperty> -> value (numeric strings OK)
if data:
ev["properties"] = data # the rest of data -> properties (kept, not aggregated)
return ev
# 1. Create the meters (409 meter_exists on a re-run is fine)
for _type, (key, agg, _prop) in METERS.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 CloudEvents, as you would have sent them to POST /api/v1/events on OpenMeter
month = datetime.now(timezone.utc).strftime("%Y-%m")
t0 = month + "-01T00:00:00Z"
cloudevents = [
{"specversion": "1.0", "type": "prompt", "id": month + "-0001", "source": "chat-api", "time": t0,
"subject": "customer-1", "data": {"tokens": "1500", "model": "gpt-4o-mini"}},
{"specversion": "1.0", "type": "prompt", "id": month + "-0002", "source": "chat-api", "time": t0,
"subject": "customer-1", "data": {"tokens": "620", "model": "gpt-4o-mini"}},
{"specversion": "1.0", "type": "request", "id": month + "-0003", "source": "gateway", "time": t0,
"subject": "customer-2", "data": {"method": "GET", "route": "/hello"}},
]
# 3. Send as one batch, then re-send it: the retry is counted once
batch = {"events": [to_usage_lab(ce) for ce in cloudevents]}
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 customer-1 2120
api_requests customer-2 1
To move history, export your events from OpenMeter, feed them through to_usage_lab() in batches of up to 100, and backfill only the last 30 days. Older months stay in OpenMeter or your warehouse.
More comparisons
- Usage metering compared (Stripe meters, Orb, Lago, Metronome, OpenMeter)
- Usage Lab vs UsageBox
- Metronome and Orb alternatives for small SaaS teams
FAQ
Is OpenMeter still open source?
Yes. As of Oct 20, 2026 the openmeterio/openmeter repository on GitHub is licensed under the Apache License 2.0 and is actively maintained. Kong said when it acquired OpenMeter in September 2025 that the project would remain open source.
Did Kong buy OpenMeter?
Yes. Kong announced the acquisition of OpenMeter on September 3, 2025. As of Oct 20, 2026 openmeter.io still loads (it does not redirect) and is branded "OpenMeter by Kong"; its pricing page says OpenMeter Cloud is now Kong Konnect Metering & Billing.
How much does OpenMeter cost?
As of Oct 20, 2026: the open-source version is free under Apache 2.0 and you run the infrastructure. openmeter.io/pricing publishes no tier prices (Start for free or Contact Sales). On konghq.com/pricing, Konnect Plus is from $25/month plus usage, with Metering & Billing at $20 per million ingested events and 0.4% of billing volume (billing volume in USD); Enterprise is custom, contact sales.
Can I move my OpenMeter CloudEvents to Usage Lab?
Yes. Map type to meter, subject to subscription, source + id to the idempotency key, time to timestamp, the meter's value property in data to value, and keep the rest of data as properties. The Python sample on this page does the mapping and runs as-is.
When should I stay on OpenMeter or Kong?
If you want to self-host open source, stream very high event volumes through Kafka and ClickHouse, already run the Kong gateway, or need entitlements and usage limits. Usage Lab has none of those.
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
- 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