Overview
If you bill customers on usage with metered features on Chargebee Billing, you often want to show them inside your own product where that usage comes from: which projects, users, or models drove consumption this billing period, and how it trended over time.
This tutorial explains how to build those usage widgets using one Chargebee Billing read API: Retrieve usage summary for a subscription. The metered feature must have group-by attributes configured. You don't need a special portal API for this.
This is about your customer-facing UI, not Chargebee Billing's hosted self-serve portal. All Chargebee API calls must go through your backend.
Scope
This tutorial covers three use cases:
- Usage by project: a ranked view of how much each project used in the current billing period.
- Usage trend: a daily trend by project, for a chart.
- Team view: a filtered view that shows only the projects a team lead manages, split by model.
The examples group by project_id and model. The same pattern works with any group-by attribute configured on the metered feature, such as user_id or region.
It does not cover itemized usage PDFs for invoices, Usage Insights reports in Chargebee Billing, or Automatic Exports. For those, see Break down usage by group-by attributes.
Architecture overview
The flow splits responsibilities between your backend and Chargebee Billing as follows.
Your application:
- Authenticates the signed-in customer and resolves their Chargebee
subscription_id - Calls Chargebee Billing from your backend (never from the browser)
- Shapes API responses into UI-ready payloads
- Renders usage widgets in your product
Chargebee Billing:
- Returns usage aggregated for a metered feature on a subscription over a timeframe
- Returns one result for each unique combination of group-by attribute values
- Optionally returns a time series, with one result per combination for each window
A typical flow:
- When the customer opens the usage page in your portal, your frontend calls your backend.
- Your backend calls Retrieve usage summary for a subscription, grouped by the attribute you want to show.
- Your backend labels and sorts the results and returns shaped JSON.
- Your frontend renders the table or chart.
Before you start
For this tutorial to work, you need the following setup:
- Enable Advanced Usage-Based Billing on your Chargebee Billing site.
- Add the group-by attributes that you want customers to see to your metered feature. In this tutorial, the Tokens metered feature aggregates usage as the Sum of
tokensand is grouped byproject_id,region, andmodel. To choose attributes, see Group-by attribute limits and best practices. Usage ingested before you add an attribute is reported undernullfor that attribute. - Obtain an API key from your Chargebee Billing test site (subdomain ends in
-test). - Have a backend service that can call Chargebee REST APIs securely. The API authenticates with your site API key, which must never be exposed in the browser. The pattern throughout this guide is: your frontend calls your backend, your backend calls Chargebee, and you return a shaped response to the frontend. Every curl example below runs server-side.
- Be familiar with REST/JSON and the frontend framework of your choice. Formatting and layout are yours. This tutorial focuses on the data.
API at a glance
| Use case | API call | What it gives you |
|---|---|---|
| 1. Usage by project | GET /subscriptions/{subscription_id}/usage_summary with group_by | One result per project for the timeframe |
| 2. Usage trend | The same call with window_size | One result per project for each window |
| 3. Team view | The same call with an attributes filter | Results limited to the attribute values you specify |
Implementation steps
Follow these steps to power usage widgets in your customer portal.
Security
- Never expose Chargebee API keys in client-side code.
- Call Chargebee REST APIs only from secure backend services.
Conventions used in the examples below:
- Base URL:
https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/ - Auth: HTTP Basic, with the API key as the username:
-u YOUR_API_KEY: - Case sensitivity. Group-by attribute names and values are case-sensitive. Use them exactly as they appear in your usage events.
- Timestamps.
aggregated_fromandaggregated_toare Unix timestamps in seconds (UTC). The start is inclusive and the end is exclusive. - Usage values are decimal strings.
aggregated_valueholds the aggregated usage as a string to preserve precision. Keep the string for storage and display. Before you sort, compare, or add usage values, parse the string with a decimal library. Don't use JavaScript+,<, or>on the raw string, and don't convert it with JavaScriptNumber. - List envelope. List responses use Chargebee's standard shape:
{ "list": [{ "usage_summary": { ... } }], "next_offset": "..." }. Response snippets below show the inner resource object unless noted. - Pagination. Every call in this tutorial can return more results than fit on one page. The endpoint returns 10 results per page by default, 100 maximum (
limit). Pass thenext_offsetvalue from the previous response asoffsetin the next call, and repeat until the response has nonext_offset. Collect every page before you sort, total, or render a view.
Use case 1: Usage by project
Goal: a ranked list of how much each project used in the current billing period, for example "proj_a used 1,250,000 tokens".
Call Retrieve usage summary for a subscription for the customer's subscription and the metered feature, with group_by[0] set to project_id. Without window_size, the response contains one aggregate per project for the whole reporting range. Without a timeframe, the range starts at the beginning of the subscription's current term and ends at the current time.
curl https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/subscriptions/__test__8asukSOXdv6kOj/usage_summary -G \
-u YOUR_API_KEY: \
--data-urlencode "feature_id=Tokens" \
--data-urlencode "group_by[0]=project_id"
Each list[].usage_summary looks like this (abridged):
{
"feature_id": "Tokens",
"aggregated_value": "1250000",
"aggregated_from": 1748822400,
"aggregated_to": 1751414400,
"attributes": [
{ "name": "project_id", "value": "proj_a" }
]
}
A subscription with more than 10 projects returns them across several pages. Collect every page with next_offset before you rank the projects, or the table will leave some out.
Map each result to a row in your UI:
- Project →
attributes[0].value - Usage →
aggregated_value(a decimal string; compare with a decimal library when you rank projects) - Period →
aggregated_fromtoaggregated_to(Unix seconds; format as dates)
Label empty groups
Every event that the metered feature counts is placed in a group, so your results can include groups with no real value. Label them in your UI instead of showing raw null.
value in the response | What it means | Suggested label |
|---|---|---|
null | The event doesn't include the attribute, includes it with a null value, or includes the text NULL. Usage ingested before you added the attribute is also reported here. | Unassigned |
"" | The event includes the attribute with an empty string. | (No value) |
Best practice
For metered features that use Sum or Count, the project values add up to the metered feature total, so the numbers in your portal match the usage on the customer's invoice for the same period. For Average, Min, Max, and Count Distinct, each value is calculated within its own group and the values don't add up to the total. Show these as a per-group view, and get the overall figure from a separate call without group_by.
Data freshness
The Usage Summary API is eventually consistent. Usage usually appears in near real time, but Chargebee doesn't publish a fixed freshness delay, so a response might not yet include usage events you sent moments ago. Design portal views to tolerate slightly stale data, and don't use this response for time-sensitive checks.
Use case 2: Usage trend
Goal: daily usage for each project, for a trend chart.
Add window_size to the same call. With window_size=day, the response contains one result for each project for each day in the reporting range. The supported window sizes are minute, hour, day, week, and month.
Windows start at timeframe_start and continue back to back until timeframe_end. They are aligned to timeframe_start, not to calendar boundaries. To align daily windows to calendar days, set timeframe_start to 00:00:00 UTC on the day you want.
curl https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/subscriptions/__test__8asukSOXdv6kOj/usage_summary -G \
-u YOUR_API_KEY: \
--data-urlencode "feature_id=Tokens" \
--data-urlencode "group_by[0]=project_id" \
--data-urlencode "window_size=day" \
--data-urlencode "timeframe_start=1748822400" \
--data-urlencode "timeframe_end=1751414400"
The response contains one usage_summary for each project and day. Two consecutive days for the same project look like this (abridged):
[
{
"aggregated_value": "41000",
"aggregated_from": 1748822400,
"aggregated_to": 1748908800,
"attributes": [{ "name": "project_id", "value": "proj_a" }]
},
{
"aggregated_value": "38500",
"aggregated_from": 1748908800,
"aggregated_to": 1748995200,
"attributes": [{ "name": "project_id", "value": "proj_a" }]
}
]
Map each result to a point on your chart:
- Series →
attributes[0].value - X axis →
aggregated_from - Y axis →
aggregated_value
Watch pagination
Every project and window combination is a separate result, so a trend call returns many more results than the other views. For example, 12 projects over 30 days return 360 results, which takes at least four pages at the maximum limit of 100. Collect every page with next_offset before you draw the chart.
Count Distinct
For metered features that use Count Distinct, each window is calculated separately. For example, a user who is active on two days is counted once in each day's window, so don't add windows together to get a period total.
Use case 3: Team view
Goal: show a team lead only the projects they manage, and which models those projects use.
Add an attributes filter to the same call, and group by the attribute you want to show. This call filters to two projects and groups by model:
curl https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/subscriptions/__test__8asukSOXdv6kOj/usage_summary -G \
-u YOUR_API_KEY: \
--data-urlencode "feature_id=Tokens" \
--data-urlencode "group_by[0]=model" \
--data-urlencode 'attributes[project_id][in]=["proj_a","proj_b"]'
Each list[].usage_summary looks like this (abridged):
{
"feature_id": "Tokens",
"aggregated_value": "720000",
"aggregated_from": 1748822400,
"aggregated_to": 1751414400,
"attributes": [
{ "name": "model", "value": "model-a" }
]
}
The filter operators are:
| Operator | Value | Example |
|---|---|---|
is, is_not | A single value | attributes[project_id][is]=proj_a |
in, not_in | A JSON-encoded array | attributes[project_id][in]=["proj_a","proj_b"] |
You can filter on several attributes in the same call. The filters are combined with AND.
The filtered response is paginated in the same way. Collect every page with next_offset before you render the team view, so no model is left out.
Best practice
Decide which projects a signed-in user can see in your backend, from your own access-control data. Never take the list of projects from a request parameter that the browser sends.
Watch filter values
Filter values must match the values in your usage events exactly, including case. The is_not and not_in operators also return the null and empty-string groups, because those groups don't match the excluded values. To return the null group, filter on the uppercase value NULL. This also includes events where the attribute is the text NULL.
Summary
In this tutorial, you learned how to:
- Retrieve usage for a subscription grouped by a group-by attribute, and map the results to a ranked table.
- Add
window_sizefor a time series. - Collect every page with
next_offsetbefore you sort, total, or render a view. - Filter by attribute values to build views for a subset of projects.
- Proxy all Chargebee Billing calls from your backend and shape responses for your frontend.
Reference
We're always happy to help you with any questions you might have! Click here to reach out to us.
In this Page
Ask AI