> For the complete documentation index, see [llms.txt](https://developer.paddle.com/llms.txt).

# Set up usage-based billing

Include prepaid usage with subscription plans, report usage as it happens, and let Paddle track each customer's usage balance.

---

{% access-form key="global-usage-based-billing" card=true gateTitle="Usage-based billing is in early access (beta)" gateBody="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**](https://developer.paddle.com/api-reference/usage-balance-units.md) describe what customers hold a balance of, like API calls, tokens, or credits. In the Paddle dashboard, they're called units.
- [**Meters**](https://developer.paddle.com/api-reference/meters.md) turn usage events into usage. Each meter has an `action`, like `api_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.

```mermaid
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 to `0` and never goes negative.
- **`overage`**: usage recorded after `remaining` reached `0`.

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](https://developer.paddle.com/build/customers/get-customer-credit-balances.md), 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.

```mermaid
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](https://developer.paddle.com/build/subscriptions/bill-add-one-time-charge.md).

## Overview

Set up usage-based billing in six steps:

1. [**Create a usage balance unit and meter**](#create-a-usage-balance-unit-and-meter)  
   Define what customers hold a balance of, and the action that draws it down.
2. [**Add an allowance to a price**](#add-an-allowance-to-a-price)  
   Set how much usage a plan includes each billing period.
3. [**Let customers subscribe**](#let-customers-subscribe)  
   Open a checkout as normal. Paddle creates a usage balance when the subscription activates.
4. [**Report usage**](#report-usage)  
   Send a usage event to Paddle each time a customer uses a metered feature.
5. [**Check balances and restrict access**](#check-balances-and-restrict-access)  
   Get usage balances and handle `usage_balance.depleted` to decide what customers can do.
6. [**Handle renewals and plan changes**](#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](https://developer.paddle.com/api-reference/about/authentication.md), or update an existing one, with these [permissions](https://developer.paddle.com/api-reference/about/permissions.md): `usage_balance_unit.write`, `meter.write`, `price.write`, `usage_event.write`, and `usage_balance.read`.
- **Create a product**  
  [Create a product](https://developer.paddle.com/build/products/create-products-prices.md) for your plan. We'll add a price with an allowance to it.
- **Set up a notification destination**  
  [Create or update a notification destination](https://developer.paddle.com/webhooks/about/notification-destinations.md) that's subscribed to `usage_balance.depleted`.
- **Set up checkout**  
  Customers subscribe using Paddle Checkout, like an [overlay checkout](https://developer.paddle.com/build/checkout/build-overlay-checkout.md).

## 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.](https://developer.paddle.com/sdks/ai/agent-skills.md)

## Create a usage balance unit and meter {% step=true %}

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.

{% tabs sync="interaction-preference" %}
{% tab-item title="Dashboard" %}

The Paddle dashboard creates a unit and a meter together.

1. Go to **Paddle** > **Catalog** > **Meters**.
2. Click New meter
3. Under **Unit**, choose **New**, then enter a name for the unit, like `call`. To draw down a unit you already created, choose **Existing** instead.
4. Under **Meter**, enter a display name, like `API calls`, and an action, like `api_call`. You send the action with usage events.
5. Leave **Aggregation** set to **Sum**. It's the only option during early access (beta).
6. Leave **Meter weight** set to `1`. It's the meter's rate, so each call draws down one call from the balance.
7. Click Save

{% /tab-item %}
{% tab-item title="API" %}

### Create a usage balance unit

Send a {% method-badge method="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.

{% api-example method="POST" path="/usage-balance-units" href="/api-reference/usage-balance-units/create-usage-balance-unit" %}

```json {% title="Request" %}
{
  "name": "API calls",
  "unit_name": "calls"
}
```

```json {% title="Response (201 Created)" collapse=false highlightLines="3" %}
{
  "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"
  }
}
```

{% /api-example %}

### Create a meter

Send a {% method-badge method="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.

{% api-example method="POST" path="/meters" href="/api-reference/meters/create-meter" %}

```json {% title="Request" highlightLines="4" %}
{
  "usage_balance_unit_id": "ubu_01kw8ta2j8x5t7q4v9m1kd6phz",
  "name": "API calls",
  "action": "api_call",
  "description": "Calls to the public API",
  "unit_name": "call",
  "rate": "1"
}
```

```json {% title="Response (201 Created)" collapse=true %}
{
  "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"
  }
}
```

{% /api-example %}

{% /tab-item %}
{% /tabs %}

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_call` and `bulk_api_call` could 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_duplicate` if 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 `rate` apply to usage from the time you make them.** Paddle doesn't recalculate usage that's already recorded.

## Add an allowance to a price {% step=true %}

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.

{% tabs sync="interaction-preference" %}
{% tab-item title="Dashboard" %}

1. Go to **Paddle** > **Catalog** > **Products**, then click the product for your plan.
2. Under the Prices section, click New price
3. Enter details for your price, and set a billing period so it's recurring.
4. Turn on **Usage billing**. This turns off the trial period, since prices with a trial can't include usage.
5. Under **Unit**, choose **Existing**, then select the unit you created. To create a unit and meter here instead, choose **New**.
6. Enter how many units the price includes each billing period, like `10000`.
7. Click Save

To include more than one unit, click Add unit before saving.

{% /tab-item %}
{% tab-item title="API" %}

Send a {% method-badge method="POST" /%} request to the `/prices` endpoint to [create a price](https://developer.paddle.com/build/products/create-products-prices.md), 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.

{% api-example method="POST" path="/prices" href="/api-reference/prices/create-price" %}

```json {% title="Request" highlightLines="13-18" %}
{
  "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"
    }
  ]
}
```

```json {% title="Response (201 Created)" collapse=false highlightLines="19-24" %}
{
  "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"
  }
}
```

{% /api-example %}

To add allowances to an existing recurring price, send a {% method-badge method="PATCH" /%} request to the [`/prices/{price_id}`](https://developer.paddle.com/api-reference/prices/update-price.md) endpoint with `allowances`. Paddle replaces the existing allowances with the array you send, so include every allowance you want to keep.

{% /tab-item %}
{% /tabs %}

Keep in mind:

- **Allowances are for recurring prices without a trial period.** Paddle returns `price_allowance_not_allowed_for_trial_price` if a price has a trial period, and `price_allowance_not_allowed_for_one_off_price` if 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 {% step=true %}

Customers subscribe to a plan with an allowance the same way as any other plan. Open a checkout for the price using [Paddle.js](https://developer.paddle.com/build/checkout/build-overlay-checkout.md), or [create a transaction](https://developer.paddle.com/build/transactions/create-transaction.md) 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:

{% tabs sync="interaction-preference" %}
{% tab-item title="Dashboard" %}

1. Go to **Paddle** > **Customers**.
2. Click the customer in the list.
3. 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.

{% /tab-item %}
{% tab-item title="API" %}

Send a {% method-badge method="GET" /%} request to the `/customers/{customer_id}/usage-balances` endpoint. Paddle returns a usage balance with the full allowance remaining.

{% api-example method="GET" path="/customers/{customer_id}/usage-balances" href="/api-reference/usage-balances/list-usage-balances" %}

```json {% title="Response (200 OK)" highlightLines="7-19" collapse=true %}
{
  "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
    }
  }
}
```

{% /api-example %}

{% /tab-item %}
{% /tabs %}

Customers can see their balance in the [customer portal](https://developer.paddle.com/concepts/sell/customer-portal.md). The subscription page shows a usage balance card with how much they've used and how much is left.

## Report usage {% step=true %}

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 {% method-badge method="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, like `api_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 with `usage_event_timestamp_too_old`, and more than 24 hours in the future with `usage_event_future_timestamp`.

{% callout type="info" %}
**Send one event per request during early access (beta).** Requests with more than one event return an error.
{% /callout %}

{% api-example method="POST" path="/usage-events" href="/api-reference/usage-events/create-usage-events" %}

```json {% title="Request" %}
{
  "events": [
    {
      "event_key": "call_7f3a9c2e",
      "customer_id": "ctm_01gw9m680k848184fpttwr0b7z",
      "action": "api_call",
      "quantity": "1",
      "occurred_at": "2026-09-23T10:15:00Z"
    }
  ]
}
```

```json {% title="Response (202 Accepted)" %}
{
  "data": {
    "events": [
      {
        "event_key": "call_7f3a9c2e",
        "status": "accepted"
      }
    ]
  },
  "meta": {
    "request_id": "cf40234b-e140-44d1-a83f-7bbdedd88589"
  }
}
```

{% /api-example %}

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. The `error` object 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.

{% callout type="warning" %}
**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.
{% /callout %}

### Queue events {% badgeLabel="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:

1. 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_key` and `occurred_at` you'll send.
2. Send saved records to Paddle from a background job, so customers don't wait on the request.
3. Mark records as sent when Paddle accepts them. Retry failed requests with the same `event_key` and `occurred_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 {% step=true %}

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 {% method-badge method="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.

{% api-example method="GET" path="/customers/{customer_id}/usage-balances" href="/api-reference/usage-balances/list-usage-balances" %}

```json {% title="Response (200 OK)" highlightLines="12-15" collapse=true %}
{
  "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
    }
  }
}
```

{% /api-example %}

To get a single usage balance, send a {% method-badge method="GET" /%} request to the `/customers/{customer_id}/usage-balances/{usage_balance_id}` endpoint. Paddle returns the usage balance on its own.

{% api-example method="GET" path="/customers/{customer_id}/usage-balances/{usage_balance_id}" href="/api-reference/usage-balances/get-usage-balance" %}

```json {% title="Response (200 OK)" highlightLines="11-14" collapse=true %}
{
  "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"
  }
}
```

{% /api-example %}

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`](https://developer.paddle.com/webhooks/usage-balances/usage-balance-depleted.md) 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.

{% callout type="info" %}
**Top-ups aren't available during early access (beta).** Prompt customers to [upgrade](https://developer.paddle.com/build/subscriptions/replace-products-prices-upgrade-downgrade.md) to a plan with a bigger allowance instead.
{% /callout %}

```json {% title="usage_balance.depleted" highlightLines="15-18" collapse=true %}
{
  "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 {% step=true %}

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](https://developer.paddle.com/build/subscriptions/replace-products-prices-upgrade-downgrade.md) 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](https://developer.paddle.com/concepts/subscriptions/proration.md) 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.

{% card-group cols=2 %}
{% card title="Provision access with webhooks" icon="carbon:webhook" url="/build/subscriptions/provision-access-webhooks" %}
Use webhooks to keep subscription and usage access in your app up to date.
{% /card %}
{% card title="Upgrade or downgrade a subscription" icon="carbon:arrows-vertical" url="/build/subscriptions/replace-products-prices-upgrade-downgrade" %}
Change the items on a subscription to move customers between plans.
{% /card %}
{% /card-group %}