WritingHow to Implement Metered Billing with Stripe: A Step-by-Step Guide — Clixo
5 min readmetered-billing, stripe, saas-billing, usage-based-pricing

How to Implement Metered Billing with Stripe: A Step-by-Step Guide

A practical how-to guide for implementing metered billing with Stripe — event ingestion, aggregation, pricing config, and invoice delivery for SaaS teams.

You built a product that charges based on usage — API calls, seats, tokens, records processed — and now you need the billing system to keep up. Stripe's metered billing tools are capable, but the documentation makes it look simpler than it is in production. Here is a real-world walkthrough.

How Metered Billing Works in Stripe

Stripe separates the concern of counting usage from the concern of charging for it. You report usage events throughout the billing period; Stripe aggregates them and includes the total on the invoice at period end.

The core objects involved are:

  • Product — what you are selling (e.g., "API Compute Units")
  • Price — the pricing config attached to the product, set to usage_type: metered
  • Subscription — the customer's active subscription referencing the metered price
  • Usage Record — the events you push during the billing period

Setting the price's aggregate_usage field controls how Stripe calculates the billable total: sum adds every event value, max takes the peak, and last_during_period takes the final reported value.

Step 1: Create a Metered Price in Stripe

When creating the price via API or Dashboard, set:

usage_type: metered
billing_scheme: per_unit   # or tiered
aggregate_usage: sum

For tiered pricing — where the per-unit rate drops at higher volumes — use billing_scheme: tiered and define your tiers. Tiered metered pricing requires you to also set tiers_mode to either graduated (each tier rate applies only to units in that tier) or volume (the rate of the tier reached applies to all units).

Step 2: Subscribe the Customer

Create a subscription with the metered price as a line item. The subscription's items[0].id is the subscription_item_id you will reference every time you report usage.

Store this ID in your database against the customer record. You will need it on every usage report call.

Step 3: Report Usage Events

Post usage to Stripe via the Usage Records API:

POST /v1/subscription_items/{subscription_item_id}/usage_records
{
  "quantity": 42,
  "timestamp": 1700000000,
  "action": "increment"
}

action: increment adds to the running total. action: set replaces the total — useful for gauge-style metrics like active seats.

High-Volume Aggregation

At scale, sending one API call per user action is a fast path to Stripe rate limits and unnecessary latency. Accumulate usage in your application layer — Redis is a practical choice — and flush aggregated totals on a short interval (every minute or every hour depending on your volume). A single batch flush per interval keeps you well inside rate limits and reduces per-event overhead.

Step 4: Give Customers Real-Time Visibility

Customers on metered plans get anxious when they cannot see their running total. Fetch the current period's usage from Stripe and surface it in your product dashboard:

GET /v1/subscription_items/{subscription_item_id}/usage_record_summaries

This endpoint returns period-to-date totals. Show this data alongside the projected cost so customers can make informed decisions before the invoice arrives.

Step 5: Handle Invoicing and Webhooks

Stripe finalizes the invoice at the end of the billing period. Subscribe to invoice.created and invoice.finalized webhooks to trigger any pre-charge logic — for example, flagging unusually large invoices for a human review step before Stripe attempts to collect.

Also listen to invoice.payment_failed so your dunning flow can kick in immediately. Failed payments on metered invoices require the same retry and notification logic as any subscription payment failure.

Common Implementation Pitfalls

  • Timestamp drift: Usage records with future timestamps are rejected. Always use server-side UTC timestamps, never client-supplied values.
  • Missing idempotency keys: If your flush job retries on failure, duplicate events will inflate the customer's bill. Set an idempotency key derived from your internal event batch ID.
  • Delayed reporting: Stripe's billing period closes at the subscription's anchor date. Events reported after the period closes are applied to the next invoice. Build monitoring that alerts if usage reports fall behind.
  • No usage floor: Some metered plans carry a minimum monthly commitment. Model this as a flat-fee line item on the same subscription, not as a price floor on the metered price — it gives you cleaner reporting and a clearer customer invoice.

Reconciliation

Automated reconciliation is not optional at any meaningful scale. Compare your internal usage logs against the usage record summaries Stripe returns. Run this daily. Revenue mismatches compound quickly and are expensive to dispute retroactively.

If you are building metered billing infrastructure from scratch and want to get it right the first time, start a build conversation with Clixo. We design and ship billing systems that handle the edge cases before they become customer complaints.