Contact
EngineeringOct 8, 2026ยท23 min read

Usage-Based Billing for SaaS: Metering, Stripe and Pitfalls

KT
Keplaris TeamOct 8, 2026
Usage-Based Billing for SaaS: Metering, Stripe and Pitfalls

Usage-based billing charges each SaaS customer for a measured unit of use, such as API calls, compute seconds or messages. To add it, define one billable metric, record every usage event exactly once in your own ledger, send those events to your billing provider (on Stripe, Metronome for new integrations, or Billing Meters if you already bill on them), and reconcile the two before invoices finalize.

The price is the easy part; counting is the hard part. Retries, late events and month boundaries decide whether the invoice is right. And older how-tos still show Stripe's usage-record API, which Stripe removed in API version 2025-03-31.basil.

Stripe completed its Metronome acquisition in January 2026, and its docs now point new usage-based integrations to Metronome, while Billing Meters stay fully supported for existing users. In our local Postgres test, replaying about 5% of events (473 of 10,000) overbilled usage by 4.47% until one UNIQUE constraint took the error to zero.

At Keplaris we build this layer for clients as part of our API and SaaS development work: Stripe plans, usage metering, proration, dunning and webhooks. For this post we ran four ledger pitfalls on a local Postgres with synthetic data and read every Stripe behavior from Stripe's docs on 2026-10-08. We're not a billing vendor, so we have nothing to sell you in the build vs buy section.

How to implement usage-based billing in a SaaS (the short version)

Seven steps, in order:

  1. Choose the pricing model. Hybrid base fee plus overage is our default for B2B, because it keeps a predictable floor. On Stripe, that's a Metronome case (see below).
  2. Choose one billable metric the customer understands and you can count exactly, such as successful API calls.
  3. Write each usage event to your ledger in the same transaction as the work, keyed by the request ID under a UNIQUE constraint.
  4. Aggregate by when usage happened (occurred_at), in UTC, over the subscription's billing period.
  5. Send usage events to Stripe Billing Meters or Metronome from an outbox worker, using the ledger's key as the event identifier.
  6. Reconcile before invoices finalize, comparing ledger totals with what your billing provider aggregated, before the period closes.
  7. Add alerts, caps and credits so no customer is surprised by an invoice.

What usage-based billing is, and when it beats seats

Usage-based billing prices a measured quantity instead of access. The terms this post uses: a billable metric is the unit you charge for; a usage event records one quantity of it for one tenant at one time; the ledger is your own table of usage events. In Stripe Billing Meters, which Stripe now calls basic usage-based billing, a meter defines the aggregation (sum, count or last value) and a meter event is a usage event sent to it. Aggregated over the billing period and priced by rating (the step that turns aggregated usage into money), usage becomes an invoice line item. The entitlement record holds what the tenant's plan allows.

Usage pricing fits when your cost grows with usage (API calls, compute, AI tokens, messages), when customer value tracks the same unit, and when customers can predict their volume. Seats fit better when value tracks people, usage is spiky, or buyers need a fixed budget line. The middle ground is a hybrid: a fixed fee for the predictable part, metered overage for the rest.

Which usage-based pricing model to choose

Choose by how much revenue predictability you need and how much bill shock customers will tolerate. Stripe's pricing models page lists fixed fee and overage, pay as you go, and credit burndown, with tiered pricing alongside.

ModelHow the invoice is computedRevenue predictabilityBill-shock riskOn Stripe Billing Meters
Per unit (pay as you go)Quantity times unit priceLowHighMetered price on a meter
Graduated tiersEach tier's units at that tier's price, summedLow to mediumMediumtiers_mode=graduated
Volume tiersAll units at the price of the tier the total lands inLow to mediumMediumtiers_mode=volume
PackageUsage divided into blocks of N, roundedLowMediumtransform_quantity (not compatible with tiers)
Hybrid base plus overageFlat fee, overage meteredHigh floorLow to mediumA licensed price plus a metered price
Prepaid creditsUsage draws down a prepaid balanceHigh (cash up front)LowCredit grants on metered prices

The last column is the Billing Meters path; Metronome models the same ideas with rate cards, commits and credits, per Stripe's Metronome concepts table.

Graduated and volume tiers bill the same usage differently. Stripe's tiered pricing docs use tiers of 7 USD per unit for units 1 to 5, 6.5 USD for 6 to 10, and 6 USD from 11. At 6 units, volume bills all six at 6.5 USD (39 USD); graduated bills five at 7 USD plus one at 6.5 USD (41.5 USD). At 20 units it's 120 USD vs 127.5 USD. Because a volume tier's price applies to the whole quantity, Stripe warns "the total might decrease," so check your tier edges.

For most B2B SaaS, Keplaris recommends hybrid base plus overage: the base fee gives finance a number to forecast, and the overage covers heavy users. That's our design judgment, not a market statistic. On Stripe, adding usage to a flat-rate subscription is what Stripe's docs call "the clearest signal to use Metronome rather than build on Billing Meters."

How to choose a billable metric

A good billable metric passes four tests: the customer understands it and can predict it; it moves with the value you deliver and with your cost; it can't be gamed cheaply; and you can count it exactly from your own system.

For an API product, "API calls" charges for failures; "successful API calls" is fairer and as easy to count. "Compute seconds" tracks your cost but is hard to predict. "Active records" needs a last-value aggregation, not a sum.

Write down what counts. Do 4xx client errors count? Do 5xx errors, which are your fault? Does a 429 from your rate limiter? Our default is to bill only successful work and record the status code on every usage event anyway, so the rule can change later without losing history. AI tokens are a valid metric too, since they track a real cost; we cover the margin side in adding AI agents to a SaaS product.

How to meter usage reliably: events, idempotency and a usage ledger

Keep your own Postgres ledger as the source of truth, with Stripe downstream. Each usage event gets an idempotency key from the request that caused it (the request ID, not a timestamp), stored under a UNIQUE constraint and inserted with ON CONFLICT DO NOTHING, so a retry can't count twice.

CREATE TABLE usage_events (
  id                 bigserial   PRIMARY KEY,
  tenant_id          text        NOT NULL,
  metric             text        NOT NULL,
  quantity           numeric     NOT NULL,
  idempotency_key    text        NOT NULL UNIQUE,
  occurred_at        timestamptz NOT NULL,               -- when the usage happened
  recorded_at        timestamptz NOT NULL DEFAULT now(), -- when we learned about it
  sent_to_billing_at timestamptz                         -- null until billing has it
);

INSERT INTO usage_events (tenant_id, metric, quantity, idempotency_key, occurred_at)
VALUES ($1, 'api_calls', $2, $3, $4)
ON CONFLICT (idempotency_key) DO NOTHING;

Three choices carry most of the weight:

  • Write the ledger row in the same transaction as the work. If the API call commits, its usage commits; if it rolls back, so does the usage. The ledger doubles as a transactional outbox that a separate worker drains to billing.
  • Store both occurred_at and recorded_at. Bill on when usage happened; use when it was recorded to catch late arrivals.
  • Match the aggregation to the metric. Sum suits quantities such as tokens. Count suits events that are one unit each. Last value suits state, such as stored records at period end. Billing Meters offer exactly these three.

Test 1: what a retry does to the total

We ran this on a local Postgres (PGlite 0.5.8, Postgres compiled to WebAssembly, on Node 24). From a fixed random seed, the script generated 10,000 usage events of 1 to 5 units for three tenants, then gave each event a 5% chance of being replayed, as a client would after a timeout: 473 events. The same 10,473 inserts went into a table without the unique key and one with it.

TableRows replayedBilled unitsCorrect unitsError
No unique key47331,53830,190+1,348 units (4.47% overbilled)
UNIQUE plus ON CONFLICT DO NOTHING47330,19030,190None; all 473 replays ignored

Synthetic data on a local Postgres (PGlite), run 2026-10-08. Replays turn almost one for one into overbilled units, and one constraint takes the error to zero.

The same plan data should feed your limits. Read a tenant's quota and its billing from one entitlement record, so the per-tenant rate limits and plan quotas your API enforces never disagree with the invoice.

Stripe usage-based billing: Stripe Meters or Metronome

Stripe now has two usage-based billing paths: Billing Meters and Metronome. Stripe completed its acquisition of Metronome on January 14, 2026, and its usage-based billing docs now say: "Unless you're maintaining an existing Billing Meters integration, use Metronome." The comparison page adds that Stripe will "continue to fully support basic usage-based billing for existing users."

Stripe Billing Meters or Metronome: which should a new integration use?

Metronome, in most cases. Stripe calls it its "primary usage-based billing platform for all new integrations," and calls adding usage-based pricing to existing flat-rate subscriptions "the clearest signal to use Metronome." It also covers what Billing Meters don't: commits and minimums, real-time credit burndown, real-time usage visibility and dimensional pricing. For a hybrid plan, Stripe documents a pattern where Stripe Subscriptions keeps the flat fee and Metronome bills usage on the same Stripe Customer, so the customer gets a subscription invoice from Stripe and a usage invoice from Metronome.

Billing Meters still make sense in two cases: you already bill customers on them, or you depend on Stripe products Metronome doesn't fully support. The comparison page marks Connect, Adaptive Pricing, Workflows and the Stripe Dashboard as not supported with Metronome, and Checkout as limited (it needs custom API calls and webhook configuration).

The ledger and reconciliation design in this post is the same either way; only the sending side changes. With Metronome, your worker sends usage events to Metronome's ingest API instead of Stripe's meter events endpoint, so check Metronome's own ingestion and deduplication rules before reusing the identifier logic below. If you start on Billing Meters and outgrow them, Stripe publishes a Migrate to Metronome guide.

The Billing Meters path, and the limits that shape your worker

On Billing Meters you create a meter, attach a metered price to the subscription, and send meter events. The older path is gone: the basil changelog for API version 2025-03-31.basil removed the UsageRecord create and UsageRecordSummary list endpoints, aggregate_usage on prices and billing_thresholds on subscriptions, and stopped accepting metered prices without a meter. Tutorials that set aggregate_usage predate it; Stripe's migration guide covers the move from usage records to meters.

A meter has an event name, an aggregation formula, a customer mapping and a value key, and per Stripe's meter configuration docs only its display name can change later. The limits that shape your worker, as read on 2026-10-08:

BehaviorWhat Stripe documentsSource
IdempotencyThe identifier is unique "within a rolling period of at least 24 hours"Meter event API
Timestamps"Must be within the past 35 calendar days or up to 5 minutes in the future"Meter event API
Rate limit (live mode)1,000 calls per second per account, one concurrent call per customer per meter; API v2 meter event streams take up to 10,000 events per secondRecording usage
Grace periodInvoices finalize 1 hour after creation by default, up to 72 hours; usage reported in that window still counts on subscription cycle invoicesGrace period

The worker below shows the Billing Meters path, because Stripe fully supports it for existing integrations and its API is small enough to show whole; the same ledger rows would feed Metronome instead. It's illustrative, not production code. It sends unsent ledger rows with stripe.billing.meterEvents.create; we checked the method and parameters against the type definitions in stripe-node 23.0.0, where payload values are strings.

// Illustrative only, Billing Meters path. Outbox worker: sends unsent ledger rows to a Stripe meter.
import Stripe from "stripe";
import { Pool } from "pg";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string);
const db = new Pool();

export async function sendPendingUsage(batchSize = 50) {
  const client = await db.connect();
  try {
    await client.query("BEGIN");
    const { rows } = await client.query(
      `SELECT u.id, u.idempotency_key, u.quantity, u.occurred_at, t.stripe_customer_id
         FROM usage_events u JOIN tenants t ON t.id = u.tenant_id
        WHERE u.sent_to_billing_at IS NULL
        ORDER BY u.occurred_at
        LIMIT $1
        FOR UPDATE OF u SKIP LOCKED`,
      [batchSize]
    );
    for (const row of rows) {
      await stripe.billing.meterEvents.create({
        event_name: "api_calls",
        identifier: row.idempotency_key, // same key as the ledger
        timestamp: Math.floor(new Date(row.occurred_at).getTime() / 1000),
        payload: {
          stripe_customer_id: row.stripe_customer_id,
          value: String(row.quantity),
        },
      });
      await client.query(
        "UPDATE usage_events SET sent_to_billing_at = now() WHERE id = $1",
        [row.id]
      );
    }
    await client.query("COMMIT");
  } catch (err) {
    await client.query("ROLLBACK");
    throw err; // the next run retries; the identifier makes the retry safe
  } finally {
    client.release();
  }
}

If Stripe accepts an event but the commit fails, the next run resends it with the same identifier, and Stripe drops the duplicate inside its uniqueness window. Sending the real occurred_at puts late usage in the right period. Keep batches small: the worker holds row locks while it waits on the network. If you sent an event that shouldn't count, Stripe's meter event adjustments API cancels a single event by its identifier; cancelling a time range isn't supported yet.

One request, end to end (illustrative, Billing Meters path)

Every value here is hypothetical. A tenant's monthly billing cycle anchor is 00:00:00 UTC on the 1st. At 23:59:58 UTC on September 30, request req_8f2c succeeds and writes one ledger row in the same transaction: idempotency_key = req_8f2c, quantity = 1.

At 00:00:04 on October 1 the worker sends a meter event with identifier: "req_8f2c" and the September timestamp. The call times out, the next run sends the same identifier, and Stripe counts it once. The September invoice was created at midnight, but the default 1-hour grace period still accepts September usage. At 00:30 reconciliation finds ledger and meter event summary equal, and the invoice finalizes at 01:00.

In the variant, the worker crashed at 23:59. At 00:30 reconciliation finds the ledger 412 units ahead, all in unsent rows. It alerts, the worker restarts, and the rows go out with their original timestamps before 01:00. One hour is tight for that, so we'd raise the grace period for metered invoices.

Usage-based billing pitfalls: late, duplicate and out-of-order events

Aggregate by occurred_at, and give each period a fixed close: period end plus grace period. Anything recorded after the close goes to an explicit adjustment, never silently onto the wrong invoice.

Test 2: the same rows, two different months

Grouping the same usage events by UTC or by New York time moved 28.5% of our synthetic units into a different month. Our 10,000 synthetic events ran from 18:00 UTC on September 30 to 08:00 UTC on October 1. Grouped by calendar month in UTC and in America/New_York:

Month in UTCMonth in New YorkUnits
2026-092026-0912,962
2026-102026-098,609
2026-102026-108,619

Synthetic data on a local Postgres (PGlite), run 2026-10-08. September has 12,962 units in UTC and 21,571 in New York time: 8,609 units, 28.5% of the total, change month. Our window straddles the boundary on purpose, so the share is exaggerated, but the effect is real for any tenant active around month-end midnight.

Stripe doesn't bill calendar months anyway. A subscription's period follows its billing cycle anchor, which "uses Coordinated Universal Time (UTC)" and defaults to the creation time, or the trial end if there is a trial. Take period boundaries from Stripe (invoices carry period_start and period_end), not from your own idea of "the month."

Test 3: usage that arrives after the close

A query on recorded_at instead of occurred_at left 112 late September units off the September total in our test. We set September's close at 01:00 UTC on October 1 (period end plus a 1-hour grace period), then inserted 40 synthetic events that happened in September but were recorded up to 6 hours after the close, like a late-syncing client would. They totalled 112 units.

QuerySeptember units
Naive: recorded_at before period end12,962
Correct: occurred_at before period end13,074
Late rows: occurred_at in September, recorded_at after the close40 rows, 112 units

Synthetic data on a local Postgres (PGlite), run 2026-10-08. The naive query pushes late units into October; the occurred_at query counts them in a September invoice that has already finalized. The third query is your late-event detector: run it after every close, then bill those units on the next invoice as a labelled adjustment or absorb them with a credit note.

Out-of-order arrival doesn't change sum or count, but for last-value metrics the latest occurred_at must win, not the last event received.

Plan changes and proration mid-period

Metered usage isn't prorated. Stripe's prorations docs say "Usage-based billing isn't subject to proration"; only the licensed part, such as a base fee or seats, gets a prorated credit and debit. What happens to usage already recorded depends on the billing mode, per Stripe's usage billing setup docs:

  • Flexible billing mode: after a mid-cycle price swap, pre-swap usage is rated at the old price and later usage at the new one. With proration_behavior, none drops the pre-swap amount, create_prorations adds it to the next invoice, and always_invoice bills it at once.
  • Classic billing mode: pre-swap usage "is ignored entirely on future invoices." In Stripe's example, 50 events before a January 16 switch and none after produce a 0 USD invoice. Report the usage again on the new price, or reset billing_cycle_anchor to now, which closes the period at the old price.

The grace period docs add a trap: if an item's price changes mid-cycle, "the grace period doesn't capture late usage," in both modes. A mid-cycle upgrade plus a late event means usage on no invoice at all, and only reconciliation catches it.

In your own system, the upgrade webhook updates the entitlement record that the product and rate limiter read. Stripe's Entitlements fire entitlements.active_entitlement_summary.updated when a customer's active entitlements change, and Stripe recommends persisting them internally. They cover feature access, not quantities, so quotas stay in your entitlement record.

How to prevent bill shock: alerts, caps and credits

Show customers their usage before the invoice does; a customer who learns about overage from an invoice tends to dispute it.

  • Usage dashboards from the ledger. Your ledger is current; Stripe's comparison page marks real-time usage visibility as not supported on basic usage-based billing. Build the in-app usage view from your own data.
  • Threshold alerts. Stripe's usage alerts send a billing.alert.triggered webhook when a customer crosses a usage threshold on a meter. The documented type is one-time per customer, with at most 25 alerts per meter and customer; spend and credit balance alerts were in preview when we checked. Alerts only notify; the cut-off is your code.
  • Caps tied to the rate limiter. Warn at a soft cap, enforce a hard cap at your API, and let customers set their own spend limit.
  • Prepaid credits. Stripe's billing credits apply only to metered prices reporting through Meters, and only when an invoice finalizes; customers "can exceed their balance during the cycle," per the comparison page. If credits must stop usage in real time, enforce the balance yourself or use a billing platform with real-time burndown, such as Metronome.

Reconciliation: proving the invoice matches the ledger

Before invoices finalize, compare the ledger total per customer, meter and billing period with what Stripe aggregated, and alert on any difference. Stripe's meter event summaries endpoint returns a customer's aggregated value for a time range; in stripe-node it's stripe.billing.meters.listEventSummaries(meterId, { customer, start_time, end_time }).

Start and end times "must be aligned with minute boundaries," while a default billing cycle anchor carries seconds, so set it with billing_cycle_anchor_config or expect edge differences. Summaries also update asynchronously, so run the job early in the grace period and again just before finalization.

Test 4: a reconciliation report

A reconciliation query found a 186-unit gap across three synthetic tenants and split it into never-sent rows and late rows. We simulated a worker that sent every September row recorded before the close but "crashed" on each row with a 0.5% chance (23 rows). A billed table stands in for Stripe's meter event summaries. The 40 late rows from Test 3 were never sent either.

WITH ledger_sum AS (
  SELECT tenant_id,
         sum(quantity) AS ledger_units,
         count(*) FILTER (WHERE sent_to_billing_at IS NULL AND recorded_at <= $2) AS never_sent,
         count(*) FILTER (WHERE sent_to_billing_at IS NULL AND recorded_at >  $2) AS late_after_close
  FROM usage_events
  WHERE occurred_at >= $3 AND occurred_at < $1  -- $3 period start, $1 period end, $2 close
  GROUP BY tenant_id
)
SELECT l.tenant_id, l.ledger_units, coalesce(b.billed_units, 0) AS billed_units,
       l.ledger_units - coalesce(b.billed_units, 0) AS diff,
       l.never_sent, l.late_after_close
FROM ledger_sum l LEFT JOIN billed b USING (tenant_id)
ORDER BY l.tenant_id;
TenantLedger unitsBilled unitsDifferenceNever sent (rows)Late after close (rows)
tenant_a4,5344,459751214
tenant_b4,3944,32866713
tenant_c4,1464,10145413

Synthetic data on a local Postgres (PGlite), run 2026-10-08. We planted these mismatches, so what matters is the report's shape: it splits the 186-unit gap by cause. The 23 never-sent rows get re-sent inside the grace period; the 40 late rows go to next period's adjustment.

Sales tax (Stripe Tax is one option) and ASC 606 revenue recognition also touch usage invoices. Both are out of scope here; talk to your accountant.

Build or buy: Billing Meters, Metronome or another billing platform?

On Stripe, Metronome is the default for new usage-based integrations, and Billing Meters stay fully supported for existing users on pay-as-you-go or flat fee plus overage pricing. Independent billing platforms earn their place when you want to self-host or keep billing logic outside Stripe. Keep your own ledger either way; a vendor holds a second copy of your usage data, not the first. Keplaris builds the ledger and Stripe integration for clients and sells no billing platform.

Facts come from each vendor's own site, docs, licence file or press release, read on 2026-10-08, not hands-on use. Rows after Stripe are alphabetical, not ranked.

OptionWhat it does, per its own docsHosting and licenceWhen it fits
Stripe Billing MetersMeters aggregate meter events per period; metered prices bill them on subscriptionsHosted. Billing is 0.7% of Billing volume on pay as you go; Meters are "part of Billing pricing, with up to 100M events per month included" (pricing)You already bill on them, or need Connect, Adaptive Pricing, Workflows or the Dashboard; pay as you go or flat fee plus overage without commits, minimums or ramps
Lago"Open Source Metering and Usage Based Billing API"; connects to Stripe, Adyen or GoCardlessSelf-host (Docker) or Lago Cloud; core under AGPLv3You want to self-host billing logic and can meet AGPL terms
MetronomeMetering, rating, credits and contracts; pushes finalized invoices to Stripe through the Invoicing APIHosted; a Stripe product since January 2026; no price listed on Stripe's Billing pricing pageStripe's default for new usage-based integrations; also commits, minimums, ramps or real-time burndown
OpenMeter"Metering and Billing for AI, API and DevOps," with entitlements and usage limitsApache 2.0 open source; acquired by Kong on September 3, 2025, and the hosted service is now offered as Kong Konnect Metering and Billing, powered by OpenMeterYou want a permissive licence, or already run Kong
Orb"Usage-based, seat-based, and hybrid billing," including custom SQL metrics; syncs invoices to Stripe for paymentHosted; we found no self-hosted option in its docsMany metrics or frequent pricing changes on a hosted platform

When to start metering (and when a flat plan is enough)

With a handful of paying customers, one plan, or no cost that grows with usage, don't meter yet. Charge a flat plan and log usage events to a ledger anyway; after a few months it tells you which billable metric tracks value and cost.

That matches how we scope a SaaS MVP, where billing complexity is an early cut, and keeps a line item small in what a SaaS MVP costs in 2026. When usage pricing becomes necessary after product-market fit, it usually comes with pulling entitlements and metering out of product logic.

What this post doesn't prove

  • Every Stripe behavior comes from Stripe's docs, read on 2026-10-08. We didn't test it in a Stripe sandbox or a Metronome account, and the docs change.
  • The four tests used synthetic data on a local Postgres, not production, client or Keplaris product data. Keplaris doesn't run metered billing on a product of its own.
  • The vendor comparison comes from public sources, not hands-on evaluation.

If you want the ledger, the Stripe integration and the reconciliation job built as one system, the Keplaris API and SaaS development team designs and builds that layer, from usage events to the webhooks that keep each entitlement record current.

Frequently asked questions

What is usage-based billing in SaaS?

Usage-based billing charges a customer for a measured unit of what they used in a billing period, such as API calls, compute seconds, messages or tokens, instead of (or on top of) a flat fee per plan or per seat. Your system records each usage event, the events are aggregated over the billing period (summed, counted or taken as the last value), and the total is priced into an invoice line item. Stripe's docs list fixed fee and overage, pay as you go, and credit burndown as the common usage-based models.

How does Stripe usage-based billing work now that usage records are gone?

Stripe removed the legacy usage-record APIs in API version 2025-03-31.basil: the UsageRecord create and UsageRecordSummary list endpoints, the aggregate_usage parameter on prices, and support for metered prices without a meter. The replacement is Billing Meters, which Stripe now calls basic usage-based billing: you create a meter with an event name and an aggregation formula (sum, count or last), attach a metered price to a subscription, and send meter events. Stripe completed its acquisition of Metronome in January 2026, and its docs now say to use Metronome for new usage-based integrations, including adding usage to existing flat-rate subscriptions, while Billing Meters stay fully supported for existing users.

How do you stop usage from being billed twice?

Give every usage event an idempotency key derived from the request that caused it, store it in your own ledger under a UNIQUE constraint, and insert with ON CONFLICT DO NOTHING so retries are ignored. Send the same value as the event identifier to your billing provider; Stripe Billing Meters enforce uniqueness within a rolling period of at least 24 hours. In our local Postgres test, replaying 473 of 10,000 synthetic events inflated the total by 4.47% without the unique key and left it exact with it.

Should a SaaS MVP launch with usage-based pricing?

Usually not. If you have only a few paying customers, one plan, or no cost that grows with usage, charge a flat plan and log usage events to a ledger anyway. The ledger is cheap to keep, and after a few months it tells you which billable metric tracks value and cost, so you can price usage on real data instead of guesses.

Should I use Stripe Billing Meters, Metronome, or a platform like Orb or Lago?

On Stripe, use Metronome for a new integration: Stripe's docs say to use it unless you're maintaining an existing Billing Meters integration. Billing Meters stay fully supported for existing users, cover pay-as-you-go and flat fee plus overage pricing, and include up to 100 million meter events a month in Billing pricing, but Stripe's comparison says they don't support commits, minimum spend, ramp schedules, dimensional pricing or real-time credit burndown. Independent billing platforms such as Lago, OpenMeter and Orb fit when you want to self-host (Lago offers that) or keep billing logic outside Stripe. Keep your own usage ledger either way, because no vendor replaces your source of truth.

Can I migrate from Billing Meters to Metronome later?

Yes. Stripe publishes a Migrate to Metronome guide for existing basic usage-based billing integrations, in five steps: scope the migration, design it, set up Metronome, migrate existing customers, then test and deploy. A usage ledger you own makes the move easier, because the same rows can feed Metronome instead of Stripe's meter events endpoint.

Next articleSaaS API Rate Limiting: Per-Tenant Limits, Tiers and 429s

Get in touch.

Whether you have questions or just want to explore what's possible, we're here to help.