Usage Billing API

The complete API flow for setting up usage-based billing. This page covers how to create meters, attach usage pricing to products, subscribe customers, and report usage — all via API.

Overview

Usage-based billing lets you charge customers based on how much they consume — API calls, seats, GB transferred, AI tokens, etc. — instead of or in addition to a fixed recurring fee.

The flow has four steps:

  1. Create a Meter — defines what to measure
  2. Create a Usage Price — defines how much to charge per unit, attached to a product
  3. Subscribe a customer — links the customer to the price
  4. Ingest meter events — your app reports usage as it happens

HL Hunt handles the rest: at the end of each billing cycle, it aggregates the usage, applies the pricing model, and charges the customer automatically.

Authentication: All API calls require a secret API key in the Authorization: Bearer header. Use sk_test_* for sandbox and sk_live_* for production.

Step 1: Create a Meter

A meter defines what you're tracking. It has an event_name (what your app sends when reporting usage) and an aggregation formula (how to roll up events into a billable quantity).

Meters do not contain pricing — they only track usage. Pricing is attached in Step 2.

curl -X POST https://payments.hlhunt.org/api/v1/billing/meters \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Active Seats",
    "event_name": "seat_active",
    "default_aggregation": { "formula": "last" }
  }'

Aggregation options:

FormulaDescriptionExample
countCounts events (ignores value)API calls made
sumAdds up the value fieldGB transferred
lastTakes the newest value (gauge)Active seats
maxTakes the largest valuePeak concurrency

Save the id from the response — you'll need it in Step 2.

Step 2: Create a Usage Price

The usage price connects the meter to a product and defines the pricing model — per-unit, package, or tiered. You can create the product and price in one call:

curl -X POST https://payments.hlhunt.org/api/v1/products \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Seat Add-on",
    "prices": [{
      "type": "usage",
      "interval": "month",
      "unit_amount": 0,
      "usage_price": {
        "meter_id": "meter_xxx",
        "model": "TIERED_GRADUATED",
        "tiers": [
          { "upTo": 1, "unitAmountCents": 0, "flatAmountCents": 0 },
          { "upTo": null, "unitAmountCents": 1000, "flatAmountCents": 0 }
        ]
      }
    }]
  }'

This creates three objects in one call:

  • A Product ("Seat Add-on")
  • A Price (type: usage, interval: month)
  • A UsagePrice config (model: TIERED_GRADUATED, tiers: first seat free, $10/seat after)

Supported pricing models:

ModelDescriptionRequired Fields
PER_UNITFlat rate per unitunit_amount_cents
PER_PACKAGECharge per package of N unitspackage_amount_cents, package_size
TIERED_GRADUATEDEach tier's rate applies only to units in that tier (tax-bracket style)tiers[]
TIERED_VOLUMEThe final tier's rate applies to ALL unitstiers[]

Save the price_id from the response — you'll need it in Step 3. You can also add a usage price to an existing product via POST /api/v1/prices.

Step 3: Subscribe a Customer

Create a subscription that includes the usage price. You can mix recurring and usage prices in the same subscription — for example, a $149/month base plan plus a usage-based seat add-on:

curl -X POST https://payments.hlhunt.org/api/v1/subscriptions \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_xxx",
    "items": [
      { "price_id": "price_base_plan", "quantity": 1 },
      { "price_id": "price_seat_usage", "quantity": 1 }
    ],
    "default_payment_method": "pm_xxx"
  }'
  • Recurring prices are charged immediately (unless trial_days is set)
  • Usage prices contribute $0 at creation — they're billed at cycle close based on aggregated usage

A subscription can have multiple usage prices (e.g. seats + API calls + storage), each linked to a different meter.

Step 4: Ingest Meter Events

From your application, send an event every time a customer consumes a unit. The customer_id must match a real customer in your account.

curl -X POST https://payments.hlhunt.org/api/v1/billing/meter-events \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "event_name": "seat_active",
    "identifier": "seat-cus_xxx-2026-01-15",
    "payload": {
      "customer_id": "cus_xxx",
      "value": 3
    }
  }'
FieldRequiredDescription
event_nameYesMust match a meter's event_name
identifierRecommendedUnique string for deduplication (prevents double-billing on retries)
payload.customer_idYesMust be a real customer in your account
payload.valueFor sum/max/lastNumeric value (ignored for count aggregation)

Always send an identifier — duplicate events with the same identifier are deduped within 24h, preventing overbilling from network retries.

What Happens at Billing Cycle Close

When the subscription's billing period ends (e.g. end of month), HL Hunt automatically:

  1. Aggregates the meter events for this customer over the cycle window
  2. Applies the pricing model (per-unit, package, or tiered) to get a charge amount
  3. Applies credit grants if the customer has prepaid credit
  4. Charges the customer's default payment method
  5. Generates an invoice with the usage line items
  6. Fires webhooks: invoice.created → invoice.paid (or invoice.payment_failed)

You can preview the estimated usage charge mid-cycle:

GET /api/v1/billing/usage/estimate?customer_id=cus_xxx&subscription_id=sub_xxx

Update Pricing Dynamically

You can change the pricing model, tiers, or per-unit rate at any time without creating a new price. This lets external applications adjust pricing on the fly — e.g. changing the per-seat rate from $10 to $15:

curl -X PATCH https://payments.hlhunt.org/api/v1/prices/price_xxx \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "usage_price": {
      "model": "TIERED_GRADUATED",
      "tiers": [
        { "upTo": 1, "unitAmountCents": 0, "flatAmountCents": 0 },
        { "upTo": 5, "unitAmountCents": 800, "flatAmountCents": 0 },
        { "upTo": null, "unitAmountCents": 1500, "flatAmountCents": 0 }
      ]
    }
  }'

The change takes effect immediately — the next usage estimate and the next invoice will use the new pricing. All fields are optional; only include what you want to change.

Common Patterns

Free tier + paid overage

Use TIERED_GRADUATED with the first tier at $0:

"tiers": [
  { "upTo": 1000, "unitAmountCents": 0, "flatAmountCents": 0 },
  { "upTo": null, "unitAmountCents": 5, "flatAmountCents": 0 }
]

First 1,000 units free, $0.05/unit after.

Base plan + usage add-on

Create two prices on the same subscription — a recurring base plan plus a usage price:

"items": [
  { "price_id": "price_base_149", "quantity": 1 },
  { "price_id": "price_api_usage", "quantity": 1 }
]

$149/month base + metered API calls on top. The base plan charges immediately; usage charges appear on the next invoice.

Prepaid credits

Issue a credit grant that's consumed before the customer's card is charged:

curl -X POST https://payments.hlhunt.org/api/v1/billing/credit-grants \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_xxx",
    "amount": { "monetary": { "value": 10000, "currency": "USD" } },
    "category": "promotional",
    "name": "Welcome credit"
  }'

Next Steps

  • Pricing Models — detailed breakdown of per-unit, package, and tiered pricing
  • Recording Usage — meter event ingestion details and best practices
  • Billing Flow — how billing cycles work end-to-end
  • Alerts — set up threshold notifications
  • Webhooks — event delivery for usage billing