Files
company-haness/_sandbox/evidence/probilling/stripe-usage-billing.md
T

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:

  1. a licensed base price (fixed committed fee, usage_type unset/licensed, has quantity), and
  2. a metered price linked to a Meter (usage_type=metered, no quantity), 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 (default value).
  • customer_mapping.type (enum) — by_id.
  • customer_mapping.event_payload_key (string) — payload key holding the customer id (default stripe_customer_id).
  • event_time_window (nullable enum, optional) — hour | day for 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's event_name.
  • payload (object, required) — must contain the meter's customer_mapping.event_payload_key (default stripe_customer_id) and value_settings.event_payload_key (default value).
  • identifier (string, optional) — idempotency / dedup key. Uniqueness is enforced within a rolling 24-hour window; a repeated identifier is 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_events returns v2.billing.meter_event and does synchronous validation; same identifier 24h-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.
  • product or product_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_schemeper_unit (with unit_amount) or tiered.
  • For tiered overage: tiers_mode = graduated | volume, and tiers[] = {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-Key request 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-Key header to make retries safe (S6).