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:
- Create a Meter — defines what to measure
- Create a Usage Price — defines how much to charge per unit, attached to a product
- Subscribe a customer — links the customer to the price
- 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:
| Formula | Description | Example |
|---|---|---|
| count | Counts events (ignores value) | API calls made |
| sum | Adds up the value field | GB transferred |
| last | Takes the newest value (gauge) | Active seats |
| max | Takes the largest value | Peak 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:
| Model | Description | Required Fields |
|---|---|---|
| PER_UNIT | Flat rate per unit | unit_amount_cents |
| PER_PACKAGE | Charge per package of N units | package_amount_cents, package_size |
| TIERED_GRADUATED | Each tier's rate applies only to units in that tier (tax-bracket style) | tiers[] |
| TIERED_VOLUME | The final tier's rate applies to ALL units | tiers[] |
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_daysis 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
}
}'| Field | Required | Description |
|---|---|---|
| event_name | Yes | Must match a meter's event_name |
| identifier | Recommended | Unique string for deduplication (prevents double-billing on retries) |
| payload.customer_id | Yes | Must be a real customer in your account |
| payload.value | For sum/max/last | Numeric 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:
- Aggregates the meter events for this customer over the cycle window
- Applies the pricing model (per-unit, package, or tiered) to get a charge amount
- Applies credit grants if the customer has prepaid credit
- Charges the customer's default payment method
- Generates an invoice with the usage line items
- Fires webhooks:
invoice.created→invoice.paid(orinvoice.payment_failed)
You can preview the estimated usage charge mid-cycle:
GET /api/v1/billing/usage/estimate?customer_id=cus_xxx&subscription_id=sub_xxxUpdate 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