8.1 KiB
Stripe Usage-Based / Metered Billing — Official API Reference
Local reference distilled from official Stripe documentation for the Pro hybrid (committed base + metered overage) billing slice.
Cited: 2026-07-07 (Asia/Seoul). API surface: Billing Meters (v1) + Meter Events (v1/v2). Note: Stripe now surfaces Metronome as its recommended platform for new high-volume integrations, but the first-party Billing Meters API documented here is fully supported and is the correct primitive for a self-serve metered-overage subscription. Sources below.
Sources (official URLs)
| # | Topic | URL |
|---|---|---|
| S1 | Usage-based billing overview | https://docs.stripe.com/billing/subscriptions/usage-based |
| S2 | Create a Billing Meter (POST /v1/billing/meters) |
https://docs.stripe.com/api/billing/meter/create |
| S3 | Create a Meter Event (POST /v1/billing/meter_events) |
https://docs.stripe.com/api/billing/meter-event/create |
| S4 | Meter Event object v2 (dedup window) (POST /v2/billing/meter_events) |
https://docs.stripe.com/api/v2/billing/meter-events/object |
| S5 | Create a Price (metered / tiered) (POST /v1/prices) |
https://docs.stripe.com/api/prices/create |
| S6 | Create a Subscription (multi-item) (POST /v1/subscriptions) |
https://docs.stripe.com/api/subscriptions/create |
| S7 | Recording usage | https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage |
| S8 | Advanced usage-based (flat fee + overage) | https://docs.stripe.com/billing/subscriptions/usage-based/advanced/compare |
Data model (end to end)
Billing Meter ──defines──▶ event_name + aggregation (sum/count/last)
▲ │
│ recurring.meter │ meter events (usage) reference event_name
│ ▼
Metered Price ──item──▶ Subscription ◀──item── Licensed base Price (committed fee)
│ │
tiered/per_unit ▼
End of billing period: aggregated usage → invoice line item
The hybrid = one Subscription carrying two items:
- a licensed base price (fixed committed fee,
usage_typeunset/licensed, hasquantity), and - a metered price linked to a Meter (
usage_type=metered, noquantity), whose graduated tiers make the included allotment cost 0 and only bill the overage per unit.
1. Create a Billing Meter — POST /v1/billing/meters (S2)
A Meter specifies how to aggregate meter events over a billing period.
Key parameters:
display_name(string, required) — internal name, not shown to customers.event_name(string, required, max 100 chars) — links meter to its events.default_aggregation.formula(enum, required) —sum|count|last.value_settings.event_payload_key(string) — payload key holding the numeric value (defaultvalue).customer_mapping.type(enum) —by_id.customer_mapping.event_payload_key(string) — payload key holding the customer id (defaultstripe_customer_id).event_time_window(nullable enum, optional) —hour|dayfor pre-aggregated events.
Python signature:
stripe.billing.Meter.create(
display_name="Pro API Calls",
event_name="pro_api_call",
default_aggregation={"formula": "sum"},
value_settings={"event_payload_key": "value"},
customer_mapping={"type": "by_id", "event_payload_key": "stripe_customer_id"},
)
# -> billing.meter { id: "mtr_...", status: "active", ... }
2. Record a Meter Event — POST /v1/billing/meter_events (S3, S4)
Reports one usage event. Aggregated into invoice line items at end of period.
Key parameters:
event_name(string, required) — must match a Meter'sevent_name.payload(object, required) — must contain the meter'scustomer_mapping.event_payload_key(defaultstripe_customer_id) andvalue_settings.event_payload_key(defaultvalue).identifier(string, optional) — idempotency / dedup key. Uniqueness is enforced within a rolling 24-hour window; a repeatedidentifieris not counted twice. If omitted, Stripe generates one. Recommend a globally unique id (UUID / deterministic business key). (S4)timestamp(unix seconds, optional) — must be within the past 35 calendar days or up to 5 minutes in the future; defaults to now. (S4)
Python signature:
stripe.billing.MeterEvent.create(
event_name="pro_api_call",
payload={"stripe_customer_id": "cus_123", "value": "25"},
identifier="pro_api_call:cus_123:2026-07-07T09:00Z:req_abc", # dedup within 24h
timestamp=1751878800, # optional
)
v2 equivalent
POST /v2/billing/meter_eventsreturnsv2.billing.meter_eventand does synchronous validation; sameidentifier24h-uniqueness rule. (S4)
3a. Create a metered Price — POST /v1/prices (S5)
Link a price to the meter and choose per-unit or tiered.
Key parameters:
currency(required) — e.g.usd.productorproduct_data(one required).recurring.interval(required) —day|week|month|year.recurring.usage_type=metered(required for usage billing).recurring.meter=<meter id>(required for metered) — ties price to the Meter from step 1.billing_scheme—per_unit(withunit_amount) ortiered.- For tiered overage:
tiers_mode=graduated|volume, andtiers[]={up_to, unit_amount}.
Graduated overage example (first N units free/included, rest billed):
stripe.Price.create(
currency="usd",
product="prod_pro",
recurring={"interval": "month", "usage_type": "metered", "meter": "mtr_..."},
billing_scheme="tiered",
tiers_mode="graduated",
tiers=[
{"up_to": 10000, "unit_amount": 0}, # included allotment (covered by base fee)
{"up_to": "inf", "unit_amount": 2}, # $0.02/unit overage above the allotment
],
)
3b. Create the base (committed) Price — POST /v1/prices (S5)
Fixed committed fee = a licensed recurring price (no usage_type=metered):
stripe.Price.create(
currency="usd", product="prod_pro",
unit_amount=2000, # $20.00 committed base
recurring={"interval": "month"}, # licensed (default usage_type)
)
4. Create the hybrid Subscription — POST /v1/subscriptions (S6)
One subscription, two items. Metered items must NOT include quantity.
stripe.Subscription.create(
customer="cus_123",
items=[
{"price": "price_base", "quantity": 1}, # licensed committed fee
{"price": "price_metered"}, # metered overage — no quantity
],
idempotency_key="sub-create:cus_123:pro-v1", # safe retry (request header)
)
- Metered usage from the previous period is charged alongside the fixed base for the new period on a single invoice at each renewal. (S6, quantities doc)
- Use the
Idempotency-Keyrequest header to safely retry create requests (returns the same object).
Overage calculation (concept)
Overage is computed by Stripe, not the app: it aggregates meter events per customer per period via
default_aggregation.formula, then applies the metered price's tiers. With graduated tiers where
tier-1 (up_to = included_allotment) has unit_amount = 0, the customer pays only for units above
the allotment:
billed_overage = max(0, aggregated_usage - included_allotment) * overage_unit_amount
invoice_total = committed_base_fee + billed_overage # single invoice at renewal
App-side we only emit events and (optionally) mirror a usage estimate for UX; the ledger of record is Stripe's aggregation.
Idempotency & correctness rules (official)
- Meter events: set a deterministic
identifier; dedup is enforced for a rolling 24h window (S4). Choose one identifier per real-world usage unit so retries/at-least-once delivery never double-count. - Timestamp: only accepted within past 35 days / +5 min; late or clock-skewed events are rejected (S4).
- Write APIs (Meter/Price/Subscription create): pass the
Idempotency-Keyheader to make retries safe (S6).