Usage-based billing
Meter events from your system, include free units, and price usage by tier on each subscription invoice.
Usage-based (metered) billing charges customers for what they consume. You define a Meter that turns raw events into a quantity, attach a metered Price to it, and send usage events as things happen. At the end of each billing period Borga aggregates the events, subtracts included units, prices the rest against the tiers and adds a line to the subscription invoice.
Define a meter
A meter names the event it counts and how to aggregate it.
Node.jsts// Count API requests const requests = await borga.meters.create({ name: "API requests", event_name: "api_request", aggregate_type: "count", unit_label: "requests", }); // Sum tokens, priced per million const tokens = await borga.meters.create({ name: "LLM tokens", event_name: "llm_completion", aggregate_type: "sum", aggregate_property: "metadata.total_tokens", unit_label: "tokens", unit_multiplier: 1_000_000, });aggregate_typeResult for the period countNumber of matching events sumSum of aggregate_propertyacross eventsmax,min,avgHighest, lowest or mean value of aggregate_propertyuniqueNumber of distinct values of aggregate_property, for example active usersaggregate_propertyis a path into the event's metadata such asmetadata.total_tokens.filterrestricts which events count, for example{ "metadata.tier": "pro" }.unit_multiplierdivides the aggregate before pricing so you can store raw tokens and price per million.Create a metered price
A metered price references the meter and prices its quantity, optionally with free
included_unitsand graduated or volume tiers.Node.jstsconst price = await borga.prices.create({ product: "prod_4Gh6Ij8Kl0Mn2Op4Qr6St8Uv", type: "recurring", currency: "ISK", recurring: { interval: "month", usage_type: "metered" }, meter: "mtr_1Zx3Cv5Bn7Mq9We1Rt3Yu5Io", included_units: 10_000, // first 10k requests are free tiers_mode: "graduated", tiers: [ { up_to: 100_000, unit_amount: 2 }, // 2 kr. per request { up_to: null, unit_amount: 1 }, // 1 kr. above 100k ], });graduatedtiers price each band separately (the first 100k at one rate, the rest at another).volumetiers price the whole quantity at the rate of the band it lands in. Combine a metered price with a fixed price in one subscription for base-fee-plus-overage plans.Send usage events
Report events as they happen, singly or in batches of up to 1000. Identify the customer by Borga id or by your own
external_customer_id, which matchesCustomer.external_id.Node.jsts// Single event await borga.usageEvents.create({ event_name: "api_request", customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz", idempotency_key: "req_01J9X2K7M3", // dedupes retries metadata: { endpoint: "/v1/chat" }, }); // Batches of up to 1000, identified by your own customer ids await borga.usageEvents.createBatch([ { event_name: "llm_completion", external_customer_id: "user_12345", timestamp: "2026-09-07T12:00:00Z", idempotency_key: "cmpl_a1", metadata: { total_tokens: 640 }, }, { event_name: "llm_completion", external_customer_id: "user_12345", timestamp: "2026-09-07T12:00:02Z", idempotency_key: "cmpl_a2", metadata: { total_tokens: 1024 }, }, ]);curlbashcurl https://api.borga.is/v1/events \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d '{ "event_name": "api_request", "customer": "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz", "idempotency_key": "req_01J9X2K7M3", "metadata": { "endpoint": "/v1/chat" } }'Always set
idempotency_keyto something derived from the event, such as your request id. Repeated keys are dropped and counted in the response'sduplicates, so retrying a batch never double-counts.timestampdefaults to now and may not be more than 60 seconds in the future.Show customers their usage
Read a customer's live balance for the current period to build usage dashboards or warnings.
Node.jstsconst balance = await borga.meters.balance( "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz", "mtr_1Zx3Cv5Bn7Mq9We1Rt3Yu5Io", ); console.log(balance.consumed_units, "of", balance.included_units); console.log("overage:", balance.overage_units);GET /v1/customer-state/:customerIdreturns every active subscription and meter balance for a customer in one call, andGET /v1/meters/:id/quantitiesreturns the aggregate for an arbitrary period.
Included units and rollover
included_units on a price (or on a subscription item, which overrides the price) are free each period. Set credit_rollover: true on the item to carry unused units into the next period, for data or message bundles.
When a customer has used 80% or more of their included units, Borga emits usage.high_watermark once per period so you can warn them before overage starts.
At period end
The billing cycle reads the meter for the period, applies included and rolled-over units, prices the remainder and adds a metered line to the invoice with the unit label. Usage in the new period starts from zero (plus any rollover). Events that arrive after the period closed count towards the period they are timestamped in only if that period is still open, so send events promptly.
Reference
- Meters API
- Usage events API
- Prices API for tiers and included units