Meter usage. Enforce access. Bill customers. Keep the stack yours.
OpenMeter is open-source monetization infrastructure built for AI, API, and usage-based products. It turns high-volume events into real-time usage, powers access enforcement, and handles usage-based billing from pricing and subscriptions through credits and invoices.
API-first and composable, it can own the path from raw usage to invoice—or only the parts your stack is missing—and work with the payment and tax providers you already use.
| Question | Answer |
|---|---|
| What is it best for? | Products with usage-based or hybrid pricing, prepaid credits, or usage limits that need one source of truth for consumption. |
| What is it not? | A bundled operator UI, payment processor, tax engine, or general-purpose accounting system. If you only need fixed recurring subscriptions, a direct payment-provider integration is usually simpler. |
| How mature is it? | Releases are beta and can include breaking changes. The OpenMeter metering engine has run in production for years and processed billions of usage events. Review releases and migration guides when upgrading. |
| Capability | What it covers |
|---|---|
| Meter usage | Ingest CloudEvents, attribute usage to customers, aggregate it in real time, and query it by time window or dimension. |
| Model products and pricing | Define versioned plans, features, add-ons, and flat, recurring, per-unit, tiered, package, or dynamic prices; then assign customer subscriptions. |
| Control access | Calculate feature access and usage-limit balances for your application to enforce, with one-time or recurring entitlement grants. |
| Bill usage | Rate flat and usage-based charges, manage customer credit balances and subscription changes, and run the invoice lifecycle. |
| Integrate | Use the OSS REST API and JavaScript, Python, or Go SDKs, send webhooks, and connect external invoicing providers. |
The local evaluation stack requires Git and
Docker with Compose. Use curl, Node.js
22+, or Python 3.9+ for the examples below.
git clone https://github.com/openmeterio/openmeter.git
cd openmeter/quickstart
docker compose up -d --waitThe stack includes an api_requests_total meter. Choose a client below; each
example sends one request event and queries its metered value.
Bash (curl)
curl -sS -o /dev/null -w '%{http_code}\n' \
-X POST http://localhost:48888/api/v1/events \
-H 'Content-Type: application/cloudevents+json' \
--data-raw '{
"specversion": "1.0",
"type": "request",
"id": "readme-curl-1",
"source": "readme",
"subject": "readme-curl",
"data": { "method": "GET", "route": "/hello" }
}'
response=
for attempt in 1 2 3 4 5 6 7 8 9 10; do
response=$(curl -fsS 'http://localhost:48888/api/v1/meters/api_requests_total/query?subject=readme-curl')
printf '%s' "$response" | grep -q '"value":1' && break
sleep 1
done
printf '%s\n' "$response"TypeScript SDK
Install the OpenMeter TypeScript SDK:
npm install @openmeter/sdk tsxSave as quickstart.ts, then run npx tsx quickstart.ts:
import { OpenMeter } from '@openmeter/sdk'
const openmeter = new OpenMeter({ baseUrl: 'http://localhost:48888' })
const queryUsage = () =>
openmeter.meters.query('api_requests_total', {
subject: ['readme-typescript'],
})
async function main() {
await openmeter.events.ingest({
type: 'request',
id: 'readme-typescript-1',
source: 'readme',
subject: 'readme-typescript',
data: { method: 'GET', route: '/hello' },
})
let usage = await queryUsage()
for (let attempt = 1; usage.data[0]?.value !== 1 && attempt < 10; attempt++) {
await new Promise((resolve) => setTimeout(resolve, 1000))
usage = await queryUsage()
}
if (usage.data[0]?.value !== 1) {
throw new Error('usage was not processed in time')
}
console.log(usage.data[0].value)
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})Python SDK
The Python SDK is in preview, so install it with pre-releases enabled:
python -m pip install --pre openmeterSave as quickstart.py, then run python quickstart.py:
import time
from openmeter import Client
from openmeter.models import Event
with Client(endpoint="http://localhost:48888") as openmeter:
openmeter.events.ingest_event(
Event(
id="readme-python-1",
source="readme",
specversion="1.0",
type="request",
subject="readme-python",
data={"method": "GET", "route": "/hello"},
)
)
for _ in range(10):
usage = openmeter.meters.query_json(
"api_requests_total",
subject=["readme-python"],
)
if usage.data and usage.data[0].value == 1:
break
time.sleep(1)
else:
raise RuntimeError("usage was not processed in time")
print(usage.data[0].value)Event processing is asynchronous. A successful example ends with a metered
value of 1.
Note
This Compose setup is a local evaluation stack. It runs the OpenMeter API and
workers together with Kafka, ClickHouse, PostgreSQL, Redis, and Svix using
latest OpenMeter images; it is not a production topology.
Continue with the full OSS quickstart, try a metering example, add entitlements and usage limits, or model plans and subscriptions.
When you are done, remove the containers and their local data volumes:
docker compose down -vThe core runtime is OpenMeter's API and worker processes plus Kafka, ClickHouse, and PostgreSQL. Redis is optional for distributed deduplication and query progress; Svix is used when webhook delivery is enabled. The architecture guide explains the complete runtime and data flow.
The official Helm chart for Kubernetes is intended for development deployments, not production.
| Need | Go to |
|---|---|
| Ask a question or discuss an approach | GitHub Discussions |
| Report a reproducible bug or request a feature | GitHub Issues |
| Track changes | Releases and migration guides |
| Report a vulnerability | Security policy |
| Build or contribute | Contributing guide and Code of Conduct |
OpenMeter is licensed under the Apache License 2.0.