Usage-based billing is in early access (beta)
Read this guide to see how it works, then apply to join the program. We'll reach out if you meet the program requirements.
Usage-based billing lets you include prepaid usage with subscription plans, then report usage to Paddle as customers use your app. Paddle grants usage when customers subscribe and renew, keeps a running balance for each customer, and tells you when a balance runs out.
It's a good fit for AI apps, APIs, and other products where plans come with an allowance, like 10,000 API calls, 1 million tokens, or 500 document exports.
What are we building?
In this tutorial, we'll set up a Pro plan for an API product. The plan includes 10,000 API calls each month. Every time a customer calls your API, your app reports it to Paddle, and Paddle draws down the customer's balance.
We'll learn how to:
- Define what you measure using a usage balance unit and a meter.
- Add an allowance to a price, so customers get usage when they subscribe.
- Report usage to Paddle and read customer balances.
- Restrict access or prompt an upgrade when a customer runs out.
How it works
Usage balance units, meters, and allowances
Usage-based billing uses three catalog entities:
- Usage balance units describe what customers hold a balance of, like API calls, tokens, or credits. In the Paddle dashboard, they're called units.
- Meters turn usage events into usage. Each meter has an
action, likeapi_call, and draws down one usage balance unit. When you report usage for an action, every meter for that action draws down its unit. - Allowances on prices say how much of a usage balance unit a customer gets each billing period. A price can include allowances for more than one unit.
Each meter also has a rate: how much of its unit each unit of usage draws down. Rates let multiple features share one balance at different costs. For example, if customers hold a balance of credits, a chat_message meter could have a rate of 1 and a report_export meter a rate of 25, so each export draws down 25 credits. Rates can be decimals, like 0.001 to draw down one credit for every 1,000 tokens.
flowchart LR
price["Price<br>allowances"] -- "grants each<br>billing period" --> balance["Usage balance<br>one per customer, per unit"]
events["Usage events<br>action + quantity"] -- "matched by action" --> meter["Meter<br>action → unit, at a rate"]
meter -- "draws down" --> balance
unit["Usage balance unit<br>what customers hold"] -.->|granted by| price
unit -.->|drawn down by| meter
Usage balances
When a customer subscribes to a price with an allowance, Paddle creates a usage balance for them automatically. There's one usage balance for each customer and usage balance unit. If a customer has more than one subscription that includes the same unit, their allowances are combined into one balance.
Each usage balance reports:
used: how much of the allowance the customer has used this billing period.remaining: how much of the allowance is left. It counts down to0and never goes negative.overage: usage recorded afterremainingreached0.
During early access (beta), Paddle never blocks usage or bills for overage. When a balance runs out, Paddle keeps recording usage as overage, and you decide whether to keep providing the feature.
Usage balances aren't the same as customer credit balances, which hold money from adjustments.
Billing periods
Allowances follow the subscription's billing period. When a subscription renews, unused usage from the previous period expires, and Paddle grants the full allowance for the new period and clears any overage. Unused usage doesn't roll over.
sequenceDiagram
participant Customer
participant App as Your app
participant API as Paddle API
participant WH as Paddle webhooks
Note over Customer,WH: Customer subscribes
Customer->>API: Completes checkout for the Pro plan
API->>API: Creates a usage balance with 10,000 API calls
Note over Customer,WH: Customer uses the app
Customer->>App: Makes an API call (uses usage)
App->>API: POST /usage-events
API-->>App: 202 Accepted
API->>API: Draws down the usage balance
App->>API: GET /customers/{customer_id}/usage-balances
API-->>App: used, remaining, overage
Note over Customer,WH: Balance runs out
WH->>App: usage_balance.depleted
App->>Customer: Restricts access or prompts an upgrade
Note over Customer,WH: Subscription renews
API->>API: Expires unused API calls, grants 10,000 more, clears overage
Early access (beta) limitations
Usage-based billing is in early access (beta), so fields and behavior may change before general release. Some features aren't available yet:
| Not available during early access (beta) | What to know |
|---|---|
| Trials | You can't add allowances to a price with a trial period. |
| One-time prices | Allowances are for recurring prices only. |
| Manually-collected subscriptions | Paddle rejects manually-collected transactions and subscriptions for prices with allowances. |
| Prorated plan changes | Use full_immediately, full_next_billing_period, or do_not_bill when changing plans. |
| Top-ups | Customers get more usage when their subscription renews or when they change plan. |
| Billing for overage | Paddle records usage beyond the allowance but doesn't bill for it. Overage clears when the subscription renews. |
| Batched usage events | Send one usage event per request. |
| Other aggregation types | Meters add up the quantity of each usage event. |
| Other balance thresholds | Paddle sends a webhook when a balance runs out. There's no webhook for other thresholds, like 80% used. |
| Custom expiry | Unused usage expires at the end of each billing period. |
| Reporting | There are no usage reports yet. Use the API to get usage balances. |
If you need any of these, or more specialized usage billing, you can use a third-party usage billing provider with Paddle instead. The provider tracks and prices usage, and you bill for it through Paddle, for example with one-time charges.
Overview
Set up usage-based billing in six steps:
- Create a usage balance unit and meter
Define what customers hold a balance of, and the action that draws it down. - Add an allowance to a price
Set how much usage a plan includes each billing period. - Let customers subscribe
Open a checkout as normal. Paddle creates a usage balance when the subscription activates. - Report usage
Send a usage event to Paddle each time a customer uses a metered feature. - Check balances and restrict access
Get usage balances and handleusage_balance.depletedto decide what customers can do. - Handle renewals and plan changes
Learn what happens to balances when subscriptions renew, change, pause, or cancel.
Before you begin
- Join early access (beta)
Usage-based billing is available to accounts in the early access (beta) program. Apply using the form on this page, then test in a sandbox account. - Create or update an API key
Create an API key, or update an existing one, with these permissions:usage_balance_unit.write,meter.write,price.write,usage_event.write, andusage_balance.read. - Create a product
Create a product for your plan. We'll add a price with an allowance to it. - Set up a notification destination
Create or update a notification destination that's subscribed tousage_balance.depleted. - Set up checkout
Customers subscribe using Paddle Checkout, like an overlay checkout.
Use an AI agent
Invoke the paddle-usage-based-billing skill included in our agent skills and plugins for Claude Code, Codex, and Cursor to build a usage-based billing integration.
Create a usage balance unit and meter
Start by defining what customers hold a balance of and what draws it down. For our API product, the usage balance unit is API calls, and the meter counts each call.
The Paddle dashboard creates a unit and a meter together.
- Go to Paddle > Catalog > Meters.
- Click New meter
- Under Unit, choose New, then enter a name for the unit, like
call. To draw down a unit you already created, choose Existing instead. - Under Meter, enter a display name, like
API calls, and an action, likeapi_call. You send the action with usage events. - Leave Aggregation set to Sum. It's the only option during early access (beta).
- Leave Meter weight set to
1. It's the meter's rate, so each call draws down one call from the balance. - Click Save
Create a usage balance unit
Send a POST request to the /usage-balance-units endpoint. Include a name and a unit_name, which is the plural name of what customers hold a balance of.
If successful, Paddle responds with a copy of the new usage balance unit entity. Paddle creates an id starting with ubu_. Keep this for the next step to associate this unit with a meter.
{ "name": "API calls", "unit_name": "calls"}{ "data": { "id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "name": "API calls", "unit_name": "calls", "status": "active", "created_at": "2026-09-23T09:00:00.000Z", "updated_at": "2026-09-23T09:00:00.000Z" }, "meta": { "request_id": "1681f87f-9c36-4557-a1da-bbb622afa0cc" }}Create a meter
Send a POST request to the /meters endpoint. Include:
usage_balance_unit_id: Paddle ID of the usage balance unit this meter draws down.name: a name for the meter, shown in the Paddle dashboard.action: the name you send with usage events. Use lowercase letters, numbers, and underscores, starting with a letter.unit_name: the singular name of what the meter measures.rate: how much of the usage balance unit each unit of usage draws down. Set it to"1"so each call draws down one call from the balance.
aggregation defaults to sum, the only option during early access (beta).
If successful, Paddle responds with a copy of the new meter entity.
{ "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "name": "API calls", "action": "api_call", "description": "Calls to the public API", "unit_name": "call", "rate": "1"}{ "data": { "id": "mtr_01kvqjfc6eb9kzk7s2wvz2aamq", "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "name": "API calls", "action": "api_call", "description": "Calls to the public API", "aggregation": "sum", "unit_name": "call", "rate": "1", "custom_data": null, "status": "active", "created_at": "2026-09-23T09:01:00Z", "updated_at": "2026-09-23T09:01:00Z" }, "meta": { "request_id": "cf40234b-e140-44d1-a83f-7bbdedd88589" }}Keep in mind:
- You can't change a meter's usage balance unit or aggregation. Create a new meter instead.
- More than one meter can draw down the same unit. For example, meters with the actions
api_callandbulk_api_callcould both draw down calls. - Two meters can't have the same action and the same unit, or a single usage event would draw down the balance twice. Paddle returns
meter_action_usage_balance_unit_duplicateif you try. - Archived meters still count usage. Archiving a meter hides it from lists, but usage events for its action still draw down balances.
- Changes to
rateapply to usage from the time you make them. Paddle doesn't recalculate usage that's already recorded.
Add an allowance to a price
Next, add an allowance to a recurring price. The allowance is how much of the usage balance unit a customer gets each billing period. For our Pro plan, that's 10,000 API calls a month.
- Go to Paddle > Catalog > Products, then click the product for your plan.
- Under the Prices section, click New price
- Enter details for your price, and set a billing period so it's recurring.
- Turn on Usage billing. This turns off the trial period, since prices with a trial can't include usage.
- Under Unit, choose Existing, then select the unit you created. To create a unit and meter here instead, choose New.
- Enter how many units the price includes each billing period, like
10000. - Click Save
To include more than one unit, click Add unit before saving.
Send a POST request to the /prices endpoint to create a price, including a billing_cycle and an allowances array. Each allowance has:
usage_balance_unit_id: Paddle ID of the usage balance unit to grant.amount: how much of the unit the price includes each billing period.
If successful, Paddle responds with a copy of the new price entity, including the allowances array.
{ "description": "Pro plan (monthly)", "name": "Pro", "product_id": "pro_01htz88xpr0mm7b3ta2pjkr7w2", "unit_price": { "amount": "2000", "currency_code": "USD" }, "billing_cycle": { "interval": "month", "frequency": 1 }, "allowances": [ { "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "amount": "10000" } ]}{ "data": { "id": "pri_01hv0vax6rv18t4tamj848ne4d", "product_id": "pro_01htz88xpr0mm7b3ta2pjkr7w2", "type": "standard", "description": "Pro plan (monthly)", "name": "Pro", "billing_cycle": { "interval": "month", "frequency": 1 }, "trial_period": null, "tax_mode": "account_setting", "unit_price": { "amount": "2000", "currency_code": "USD" }, "unit_price_overrides": [], "allowances": [ { "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "amount": "10000" } ], "quantity": { "minimum": 1, "maximum": 100 }, "status": "active", "custom_data": null, "import_meta": null, "created_at": "2026-09-23T09:05:00.000Z", "updated_at": "2026-09-23T09:05:00.000Z" }, "meta": { "request_id": "9e7f51e8-838b-4b2c-b0fa-d52d0593354c" }}To add allowances to an existing recurring price, send a PATCH request to the /prices/{price_id} endpoint with allowances. Paddle replaces the existing allowances with the array you send, so include every allowance you want to keep.
Keep in mind:
- Allowances are for recurring prices without a trial period. Paddle returns
price_allowance_not_allowed_for_trial_priceif a price has a trial period, andprice_allowance_not_allowed_for_one_off_priceif it has no billing cycle. - A price can include up to 20 allowances, one for each usage balance unit.
- Allowances multiply by quantity. If a customer buys three of a price that includes 10,000 API calls, they get 30,000 API calls.
- An allowance can be
0. The customer gets a usage balance with nothing in it, so Paddle records all their usage as overage. Use this to track usage on a plan without including any.
Let customers subscribe
Customers subscribe to a plan with an allowance the same way as any other plan. Open a checkout for the price using Paddle.js, or create a transaction and pass it to a checkout.
When the subscription activates, Paddle automatically creates a usage balance for the customer and grants the allowance. Only automatically-collected subscriptions can include usage during early access (beta).
To check a customer's usage balance:
- Go to Paddle > Customers.
- Click the customer in the list.
- Scroll to the Usage balance section.
The Usage balance section shows how much of each usage balance unit the customer has been allocated, has used, and has left. It only appears once the customer has a usage balance.
Send a GET request to the /customers/{customer_id}/usage-balances endpoint. Paddle returns a usage balance with the full allowance remaining.
{ "data": [ { "id": "ubl_01h9zx4p8q2r5t7v9w1y3z5b7d", "customer_id": "ctm_01gw9m680k848184fpttwr0b7z", "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "balance": { "used": { "total": "0", "subscription": "0" }, "remaining": { "total": "10000", "subscription": "10000" }, "overage": { "total": "0" } }, "created_at": "2026-09-23T10:00:00Z", "updated_at": "2026-09-23T10:00:00Z" } ], "meta": { "request_id": "bb093d50-ddb5-401a-a5ba-034b187b396e", "pagination": { "per_page": 50, "next": "https://sandbox-api.paddle.com/customers/ctm_01gw9m680k848184fpttwr0b7z/usage-balances?after=ubl_01h9zx4p8q2r5t7v9w1y3z5b7d", "has_more": false, "estimated_total": 1 } }}Customers can see their balance in the customer portal. The subscription page shows a usage balance card with how much they've used and how much is left.
Report usage
Each time a customer uses a metered feature, send a usage event to Paddle. Paddle finds meters that match the event's action and draws down the customer's usage balance.
Send a POST request to the /usage-events endpoint with an events array. Each event has:
event_key: a unique key for this usage from your system, like a job or request ID.customer_id: Paddle ID of the customer that used the feature.action: the action of the meter to draw down, likeapi_call.quantity: how much the customer used, as a string. For example,"1"for one API call.occurred_at: when the usage happened, as an RFC 3339 timestamp in UTC. Paddle rejects events more than 60 days in the past withusage_event_timestamp_too_old, and more than 24 hours in the future withusage_event_future_timestamp.
Send one event per request during early access (beta). Requests with more than one event return an error.
{ "events": [ { "event_key": "call_7f3a9c2e", "customer_id": "ctm_01gw9m680k848184fpttwr0b7z", "action": "api_call", "quantity": "1", "occurred_at": "2026-09-23T10:15:00Z" } ]}{ "data": { "events": [ { "event_key": "call_7f3a9c2e", "status": "accepted" } ] }, "meta": { "request_id": "cf40234b-e140-44d1-a83f-7bbdedd88589" }}Paddle responds with 202 Accepted and a status for each event:
accepted: Paddle received the event. It draws down the usage balance shortly after, so a balance you get straight away might not include it yet.rejected: Paddle didn't record the event. Theerrorobject says why. Fix the problem, then send the event again.
To avoid counting usage twice, Paddle uses event_key and occurred_at together to identify an event. If a request fails or times out, retry it with the same event_key and occurred_at, and Paddle counts it once. If you retry with a new occurred_at, Paddle counts it as new usage.
Send usage events when usage happens. If an event arrives shortly after a subscription renews, Paddle still counts it against the billing period when the usage occurred.
Check that action matches a meter. Paddle accepts events for actions that don't have a meter, but they don't draw down any usage balance.
Queue events Recommended
To make sure usage isn't lost or counted twice, save it in your app before you send it to Paddle. For example, use an outbox:
- When a customer uses a metered feature, save a record of the usage in the same database transaction as the work itself. Include the
event_keyandoccurred_atyou'll send. - Send saved records to Paddle from a background job, so customers don't wait on the request.
- Mark records as sent when Paddle accepts them. Retry failed requests with the same
event_keyandoccurred_at, and flag rejected events for review.
Run the job often, and alert if records stay unsent. Paddle rejects events more than 60 days old, including retries. If Paddle accepted an event before you retried it, the original still counts, so don't change occurred_at to get a retry accepted.
Check balances and restrict access
During early access (beta), Paddle doesn't block customers when they run out, so use usage balances to decide what customers can do in your app.
Before you build this, decide what should happen when a customer runs out: block the feature, keep providing it and prompt them to upgrade, or keep providing it without a prompt.
Get usage balances
Get a customer's usage balances to show how much they have left or to check before starting a metered action.
Send a GET request to the /customers/{customer_id}/usage-balances endpoint. Paddle returns a usage balance for each usage balance unit the customer has. In this example, the customer has used 3,500 of their 10,000 API calls this billing period.
{ "data": [ { "id": "ubl_01h9zx4p8q2r5t7v9w1y3z5b7d", "customer_id": "ctm_01gw9m680k848184fpttwr0b7z", "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "balance": { "used": { "total": "3500", "subscription": "3500" }, "remaining": { "total": "6500", "subscription": "6500" }, "overage": { "total": "0" } }, "created_at": "2026-09-23T10:00:00Z", "updated_at": "2026-09-25T14:08:31Z" } ], "meta": { "request_id": "bb093d50-ddb5-401a-a5ba-034b187b396e", "pagination": { "per_page": 50, "next": "https://sandbox-api.paddle.com/customers/ctm_01gw9m680k848184fpttwr0b7z/usage-balances?after=ubl_01h9zx4p8q2r5t7v9w1y3z5b7d", "has_more": false, "estimated_total": 1 } }}To get a single usage balance, send a GET request to the /customers/{customer_id}/usage-balances/{usage_balance_id} endpoint. Paddle returns the usage balance on its own.
{ "data": { "id": "ubl_01h9zx4p8q2r5t7v9w1y3z5b7d", "customer_id": "ctm_01gw9m680k848184fpttwr0b7z", "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "balance": { "used": { "total": "3500", "subscription": "3500" }, "remaining": { "total": "6500", "subscription": "6500" }, "overage": { "total": "0" } }, "created_at": "2026-09-23T10:00:00Z", "updated_at": "2026-09-25T14:08:31Z" }, "meta": { "request_id": "bb093d50-ddb5-401a-a5ba-034b187b396e" }}When balance.remaining.total is "0", the customer has used their allowance. Any more usage is recorded in balance.overage, and balance.overage.started_at shows when the balance first went into overage this billing period.
To avoid calling Paddle every time you check a balance, cache it in your app. Refresh it when usage_balance.depleted occurs, and when a subscription renews or changes.
Handle usage balance depleted
Paddle sends a usage_balance.depleted webhook when usage draws a customer's balance down to 0. Use it to restrict access, let the customer know, or prompt them to upgrade.
Top-ups aren't available during early access (beta). Prompt customers to upgrade to a plan with a bigger allowance instead.
{ "event_id": "evt_01js2n02s4fr6a8gvfm5enq0as", "event_type": "usage_balance.depleted", "occurred_at": "2026-09-28T16:42:10.283806Z", "notification_id": "ntf_01js2n03f91aw3v7q6f7x55r3z", "data": { "id": "ubl_01h9zx4p8q2r5t7v9w1y3z5b7d", "customer_id": "ctm_01gw9m680k848184fpttwr0b7z", "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz", "balance": { "used": { "total": "10000", "subscription": "10000" }, "remaining": { "total": "0", "subscription": "0" }, "overage": { "total": "0" } }, "created_at": "2026-09-23T10:00:00Z", "updated_at": "2026-09-28T16:42:10Z" }}The payload is the usage balance. It doesn't include a subscription ID, so use customer_id and usage_balance_unit_id to find the customer and feature in your app.
Keep in mind:
- It occurs once when a balance runs out. Paddle doesn't send it again for more usage after that.
- It can occur again after a customer gets more usage. For example, after a subscription renews or a customer upgrades, then runs out again.
- It doesn't occur when usage expires. Unused usage expiring at the end of a billing period doesn't count as running out.
Handle renewals and plan changes
Paddle updates usage balances automatically as subscriptions change:
| When | What happens to the usage balance |
|---|---|
| Subscription renews | Unused usage from the previous billing period expires. Paddle grants the full allowance for the new period and clears overage. |
| Customer upgrades or downgrades | Paddle expires the unused allowance from the old price and grants the new price's full allowance straight away, with no proration. Overage carries over until the subscription renews. |
| Quantity changes | Paddle grants the allowance again for the new quantity. |
| Subscription is canceled | When the cancellation takes effect, unused usage from that subscription expires. Paddle records any more usage as overage. |
| Subscription is paused | The usage balance doesn't change, and Paddle still records usage. |
| Subscription resumes | If you resume with a new billing period, Paddle grants the full allowance. If you continue the existing billing period, the balance doesn't change. |
| Customer gets a refund | The usage balance doesn't change. |
When you upgrade or downgrade a subscription that includes usage, set proration_billing_mode to full_immediately, full_next_billing_period, or do_not_bill. Paddle returns subscription_prorated_billing_not_allowed_for_allowance for prorated billing modes during early access (beta).
Because a plan change expires unused usage, customers who downgrade partway through a billing period lose what's left of their old allowance.
Next steps
That's it. Now you've built a plan that includes usage, reported usage to Paddle, and handled what happens when customers run out.
Events
usage_balance.depleted | Occurs when usage draws a customer's usage balance down to 0. |