Free MIT core · Aurora Meter Pro from $99/mo per app

Aurora Meter

Laravel Cashier + OpenMeter, for Phoenix. Real-time usage metering, plan entitlements, and Stripe billing. Count, gate, and bill on the BEAM: an ETS hot path that flushes to Postgres and adds up across every node in your cluster.

lib/my_app/reports.ex
# count usage: an ETS increment, flushed to Postgres
AuroraMeter.track(org, :ai_generations)

# gate + run + meter, atomically
AuroraMeter.with_quota(org, :ai_generations, fn ->
  generate_report()
end)
ETS hot pathPersisted to PostgresCluster-wide totalsMIT core

Three jobs, one library

Every SaaS meters, gates, and bills. On Phoenix you hand-roll all of it.

Meter

Increments hit a lock-free ETS counter: microseconds, no database on the hot path. A flusher persists the totals to Postgres on an interval you set, and every node adds its own deltas, so the stored row is the cluster total and reads converge within a broadcast interval. A broadcaster fans live values over PubSub.

Entitle

Declare plans in a small compile-time DSL, then gate features by plan. `with_quota/4` checks and reserves in one step, so a hard limit holds under concurrency on a node. Across a cluster it is checked against the local view and can overshoot by what the other nodes admitted in one broadcast interval.

Bill

The free core holds a prepaid credit ledger and ships a billing seam; Aurora Meter Pro closes the loop: Stripe checkout, webhook sync, prepaid top-ups, and metered usage reported to Stripe Billing Meters with a per-item idempotency key.

Part 1 · The free core, MIT

Everything you need to count and gate. Free, forever.

The MIT core is the whole metering and entitlements engine: counters on an ETS hot path that flush to Postgres and sum across nodes, the plan DSL and a quota gate that checks and reserves in one step, a prepaid credit ledger with holds and settlement, drop-in LiveView usage components, daily history and telemetry. No email gate, no trial, no expiry.

Live counters

Increments land in ETS and a flusher writes the totals to Postgres on an interval you set, five seconds by default, so the hot path never waits on your database. Anything not yet in an acknowledged flush batch is lost if the node dies, and a database outage makes that window longer than the interval.

Plan DSL + with_quota

Hard caps, metered overage and feature flags, checked and reserved in one step.

Prepaid credit ledger

Grant, hold, settle and release, in integer micro-dollars under a row lock, with an append-only entry for every write. Promotional credit is spent first and expires on its own schedule. Free and MIT, like the rest of the core.

LiveView components

Drop-in usage meters that read the live counters over PubSub.

Postgres, clustered

Every node meters locally and flushes its deltas, so the Postgres row is the cluster total. Reads converge within one broadcast interval, and a hard limit is checked against the local view, so a burst across nodes can overshoot a cap by what the other nodes admitted in that interval.

History and telemetry

Daily history per tenant and per feature, and a telemetry event for every increment, flush, reservation and ledger write, so your own metrics pipeline sees what the meter sees.

$ {:aurora_meter, "~> 1.0"}

Runs on

Minimum supported versions for the free core and for Aurora Meter Pro
Requirement Free core Pro
Elixir 1.15 or later 1.15 or later
Erlang/OTP 25 or later 26 or later
PostgreSQL 13 or later 13 or later
Phoenix LiveView 1.0, for the usage components 1.0, for the dashboards
Oban 2.17, for the optional workers 2.17, for the workers
Igniter 0.8, for the installer only 0.8, for the installer only

LiveView, Oban and Igniter are optional: nothing needs them to meter, gate or hold credit. Pro's Erlang/OTP floor is higher than the core's because the Stripe HTTP stack it resolves requires it.

Measured, not estimated

3,418,407 increments per second
AuroraMeter.Counter.incr/4, eight processes on distinct counter keys, median of five runs. A micro benchmark: there is no database in the path. On the same machine an end-to-end durable write measures about 1,800 a second, three orders of magnitude apart, so read which one you need before you use either.
core docs/evidence/v1/phase-08/08c-results.md, 2026-09-16
42.5 seconds to a first meter
Median of five clean runs, range 41.6 to 44.6 seconds: a new Phoenix application, the dependency added, a cold fetch, the installer, the migration, and a proof that one counter reads back and that the hundredth reservation against a hard limit of 100 is refused. Scripted execution on one machine. Not a human median, not a usability result, not a customer measurement.
docs/evidence/v1/phase-09/09e-quickstart.json, 2026-09-17

Both on WSL Ubuntu 24.04, AMD Ryzen 9 7900X, Elixir 1.20.1, OTP 29, PostgreSQL 16.13. Not generalised to any other machine or toolchain.

Guarantees

What it promises, and what takes that away.

A promise written without the condition that makes it true is not a promise. So every row here carries both, in the same words as the package's own documentation, and the list below it is what Aurora Meter deliberately does not claim.

One flush batch has at most one durable effect

I01

The flush receipt's primary key is written inside the same transaction as the counter and history deltas, so a batch redelivered after a lost response applies its deltas a single time.

The limit. Counters live in ETS until a batch is acknowledged, so everything not yet in one is lost if the node stops, and a database outage makes that window as long as the outage. Deleting flush receipts while a node could still retry, or writing the counter tables by hand, takes the rest away.

Credit is conserved, per wallet

I10, I11

Every wallet write serialises on that wallet's balance row lock and sufficiency is checked under the same lock, so the balance equals the sum of every transaction amount after every step. A refusal writes nothing and does not roll your own transaction back.

The limit. Conservation is per wallet: there is no cross-wallet total the ledger defends. Writing the ledger tables directly goes round the lock.

Scheduled work is at least once, with effects that are safe to repeat

I16

No scheduled entry point assumes it runs a single time. A flush with nothing pending writes nothing, a due grant expires once however often the sweeper runs, and a grant is idempotent per reference.

The limit. Your host owns the scheduler. Aurora Meter ships no job runner, and it cannot make a job of yours idempotent.

A recorded event is one persisted fact per identity

I06, I07

One persisted fact per tenant and event id, one projection effect and one export intent. A conflicting payload under an identity already used is reported rather than quietly accepted.

The limit. Only for events recorded with an identity. A counter increment is not an event: it carries no identity, it is not deduplicated, and it is not what usage reporting reads.

Usage export is at least once, with a key the provider deduplicates on

I15

Each send carries an identifier Stripe deduplicates on, so a retry of the same window does not bill those units twice. A send whose answer never arrives leaves the item uncertain rather than being resent past the provider's deduplication horizon.

The limit. Aurora Meter never reports that a provider invoice was finalised or paid. It reports what it sent and what it heard back, and raises an item for every difference so a person can resolve it.

Not claimed

Host job execution
Scheduled work is at least once. Nothing assumes a job runs a single time, and your host owns the scheduler.
Strict global quotas
A hard cap is enforced against the local view, so a burst across nodes can overshoot it by what the other nodes admitted within one broadcast interval. There is no cluster-wide lock on a quota decision.
Buffered counts
A count is durable once its flush batch is acknowledged, and not before. The window is everything not yet in one, and it grows for as long as the database is unreachable. Mark the feature durable when losing a count is not acceptable.
Stripe invoice finalisation
Aurora Meter reports what it sent to Stripe and what it heard back. Whether an invoice is finalised or paid is Stripe's answer, not ours.
Unknown provider outcomes
An item whose answer never arrived is surfaced for a person rather than replayed blindly, and reconciliation raises a difference rather than resolving it.
  • The package README The README shipped in the version on offer, with its own guarantees section.
  • HexDocs The full correctness index: every invariant with its prerequisites, its known limits and the test that holds it.
  • Upgrading to 1.0 What 1.0 starts refusing, why the work happens on the transition release before you install it, the schema steps that have an order, the wallet migration that has no rollback, and the four things that do not change at all.

The showcase

Live usage, rendered where your team already looks.

Aurora Meter ships LiveView components that read the live ETS counters and update over PubSub. Drop them into an admin dashboard and every plan's usage is visible in real time. This is a mock of that surface.

Usage this period

Scale plan · resets in 12 days

Live
AI generations 7,420 / 10,000
API calls 128,900 / 250,000
Seats 18 / 25
Exports 47 / 50

Get started

Installed in one command. Metering in two calls.

With Igniter in your deps the installer writes the config, adds Aurora Meter to your supervision tree after the repo and PubSub, creates a starter plans module and generates the migration. Pass --dry-run to see the diff without writing anything. Without Igniter, the task prints the steps instead of doing them.

terminal
# mix.exs: {:aurora_meter, "~> 1.0"}, 
{:igniter, "~> 0.8", only: [:dev]}
$ mix deps.get $ mix igniter.install aurora_meter $ mix ecto.migrate
lib/my_app/reports.ex
# count usage
AuroraMeter.track(org, :ai_calls)

# gate + run + meter in one atomic step
AuroraMeter.with_quota(org, :ai_calls, fn ->
  generate_report()
end)

Go deeper

The thinking behind the library.

Long-form writing on usage-based billing in Phoenix, from the ETS hot path to invoicing overage through Stripe. For the hands-on walkthroughs that build a product line by line, the guides are the place to start.

Start here

Usage-Based Billing in Elixir and Phoenix: A Complete Guide

A complete guide to usage-based billing in Elixir and Phoenix: metering events on the ETS hot path, reporting usage to Stripe, and enforcing plan quotas.

Read the guide

How the hot path works

Phoenix Usage Metering in Real Time with ETS

Build real-time Phoenix usage metering with ETS: count API calls on an in-memory hot path, enforce plan quotas, and flush to Postgres for billing.

Read the guide

Gating by plan

Plan-Based Feature Gating and Entitlements in Phoenix

Phoenix feature gating done right: model plan entitlements as data, enforce in contexts and LiveView, cache with ETS, and handle overrides.

Read the guide

Getting paid for overage

Stripe Metered Billing in Elixir: Report Usage to Stripe Billing Meters

Stripe metered billing in Elixir: create Billing Meters, report usage idempotently from Oban, scale with the meter event stream, and reconcile the totals.

Read the guide

Versus a hosted meter

OpenMeter Alternative for Elixir: Metering Usage on the BEAM

Need an OpenMeter alternative for Elixir? An honest comparison of both options, plus a BEAM-native metering pipeline built with ETS, Oban, Postgres and Stripe.

Read the guide

Coming from Laravel

Laravel Cashier for Phoenix: Stripe Subscriptions and Usage Billing in Elixir

Laravel Cashier for Phoenix: build Stripe subscriptions, webhooks, trials, plan gating and usage billing in Elixir, with real code and an honest comparison.

Read the guide

Part 2 · Aurora Meter Pro

From live counters to invoices, in one place.

The free core counts, gates and holds prepaid credit. Pro is the revenue and operations layer on top: hosted dashboards for usage, balance and spend, Stripe billing sync, metered-usage reporting, prepaid top-ups and auto top-up, a per-request audit log, historical rollups with CSV export, quota alerts, and reconciliation. Here is what ships.

app.yoursaas.com/admin/usage

Usage & billing

Scale plan · billing period Jul 1 to Jul 31

Last 6 months ▾ Export CSV

Active subscriptions

128

▲ +6

MRR

$19,072

▲ +11%

Metered revenue

$4,210

▲ +23%

Events reported

1.24M

this period

Metered usage by month

AI generations (thousands)

Feb Mar Apr May Jun Jul

Included allowances

AI generations 7,420 / 10,000
API calls 128,900 / 250,000
Seats 18 / 25
Exports 47 / 50

Live meter events

streaming
Time Customer Feature Qty Stripe
12:04:31 Acme Corp ai_generations 3 reported ✓
12:04:29 Globex api_calls 128 reported ✓
12:04:27 Initech exports 1 reported ✓
12:04:24 Umbrella ai_generations 7 reported ✓
12:04:21 Hooli api_calls 64 reported ✓

Stripe billing sync

Checkout and a self-contained webhook verifier keep subscriptions in lockstep. Metered overage lands on the invoice automatically.

Invoice · Acme Corp Paid
Scale plan$149.00
AI generations overage (2,400)$24.00
API callsincluded
Total$173.00

Quota alerts

Threshold alerts are deduplicated per customer, per feature, per period, so a customer hovering at 80 percent gets one email rather than forty. A handler that fails is retried, which can produce a second alert for the same crossing.

Acme Corp at 90% of AI generations

9,000 / 10,000 this period

Globex exceeded the API calls hard cap

250,000 / 250,000, new calls blocked

Initech reached 75% of exports

38 / 50 this period

Historical rollups & export

Month and day rollups persist for reporting, with one-click CSV export for finance.

The four most recent monthly rollups in the simulated dashboard, in units
Jul 2026 120,000 units
Jun 2026 94,000 units
May 2026 78,000 units
Apr 2026 61,000 units
Download usage.csv

Reconciliation

A background pass compares local counters with what Stripe holds and raises an item for every difference, so nothing diverges unnoticed. A person resolves the item; the pass reports, it does not rewrite your ledger.

Reconciled, no differences found

Last run 2 minutes ago · 128 subscriptions · 1.24M events compared

Pricing

Start free. Upgrade when Stripe is charging.

One feature set, licensed per application. Every Pro plan starts with a 14-day free trial.

Aurora Meter Free · MIT
$0 forever · count, gate and bill
Aurora Meter Pro per app
$99/mo or $990/yr per app
Aurora Meter Pro Unlimited any number of apps
$2490/yr · pays for itself at 3 apps
Choose your plan

14-day refund on your first invoice, no questions asked · cancel anytime · Licence terms · Terms