Skip to main content
POST
Typescript (SDK)
Aggregate usage events by time period. Returns usage totals grouped by feature and optionally by a custom property.

Working with Properties

When tracking events, you can attach custom properties that can later be used for grouping aggregations:
You can then aggregate events grouped by any property using the group_by parameter:

Special Group By Operators

In addition to custom properties, you can group by built-in columns using $-prefixed operators:
  • $customer_id — Group results by customer ID. Useful when aggregating across all customers (i.e. no customer_id specified).
  • $entity_id — Group results by entity ID. Useful for seeing usage broken down per entity.

Response Format

The response structure changes based on whether group_by is provided:

Without group_by (Flat Response)

When no grouping is specified, values contains the aggregated sum for each feature:

With group_by (Grouped Response)

When grouping is specified, values contains the total sum while grouped_values breaks down values by group:
The grouped_values field is only present when group_by is provided in the request.

Deduction Breakdowns

By default, aggregations answer “how much usage was tracked?”. Passing aggregate_on: "deducted" additionally answers “which balances did that usage actually come out of?” — the response gains a deductions array, keyed by the balance-owning feature rather than the tracked event. The list and total fields are unchanged. A customer_id is required in this mode, since deductions are resolved against a specific customer’s balances. There are two situations where this matters:

Case 1: Usage spilling into a credit system

A feature can have its own included allowance and feed a credit system — usage drains the allowance first, then overflows into credits at the feature’s credit cost. The standard aggregation only shows tracked totals, so the overflow is invisible. With aggregate_on: "deducted", the two sides show up separately:
Reading this: the customer’s 1M-event allowance absorbed the tracked usage, and 10,000 events overflowed into the usage_credits pool, burning 0.8 credits at the feature’s credit cost of 0.00008. Each entry is in that balance’s own unit — events for the metered feature, credits for the credit system.
credit_cost is only populated when the request pins a single non-credit feature via feature_id, since several features feeding one pool each convert at a different rate. It reflects the credit system’s current schema.

Case 2: Per-entity balances falling through to a shared pool

When entities (e.g. seats) each carry their own balance and overflow lands on a customer-level shared balance, group by $entity_id to see who spent from where:
Finding each entity’s spillover takes one join between the two halves of the response:
  1. In balances, find the entry whose entity_id is null. That’s the customer-level shared balance — the pool that entities fall through to when their own balance runs out. (Entries with an entity_id are balances owned by that entity.)
  2. Take that entry’s balance_id and look it up in grouped_values. The keys of that object are the entities that spent from the shared pool, and each deducted is exactly how much they overdrew.
Here cus_ent_shared is the shared balance, and grouped_values["cus_ent_shared"] shows seats 3 and 4 each pulled 25 credits from it beyond their own 600-credit seat balances — the “used 25 extra credits” number to show next to each seat. The seat-owned balances appear in grouped_values too, but for them the owner and the spender are the same entity, so the split just restates the balance total.
group_by: "$plan_id" is rejected with aggregate_on — every balance belongs to exactly one plan, so balances[].plan_id already carries the plan split.

Body Parameters

Response

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Headers

x-api-version
string
default:2.4.0
required

Body

application/json
feature_id
required

Feature ID(s) to aggregate events for

Minimum string length: 1
customer_id
string

Customer ID to aggregate events for

Minimum string length: 1
entity_id
string

Entity ID to filter aggregated events for (e.g., per-seat or per-resource limits)

Minimum string length: 1
group_by
string

Property to group events by (e.g. "properties.region"), or "$customer_id" / "$entity_id" / "$plan_id" to group by those columns. When aggregate_on is "deducted", "$feature_id" groups deductions by the tracked feature that consumed each balance.

range
enum<string>

Time range to aggregate events for. Either range or custom_range must be provided

Available options:
24h,
7d,
30d,
90d,
last_cycle,
1bc,
3bc
bin_size
enum<string>
default:day

Size of the time bins to aggregate events for. Defaults to hour if range is 24h, otherwise day

Available options:
day,
hour,
week,
month
custom_range
object

Custom time range to aggregate events for. If provided, range must not be provided

filter_by
object

Filter events by property values, e.g. {"model": "gpt-4", "region": "us"}. Maximum 5 filters.

max_groups
integer

Maximum number of distinct group values to return per time bin when using group_by. Remaining values are bundled into an 'Other' bucket. Defaults to 9

Required range: 1 <= x <= 250
aggregate_on
enum<string>

Set to "deducted" to additionally return a per-balance breakdown of what each event consumed, under deductions. Purely additive: list and total are unchanged. Requires customer_id.

Available options:
deducted

Response

200 - application/json

OK

list
object[]
required

Array of time periods with aggregated values

total
object
required

Total aggregations per feature. Keys are feature IDs, values contain count and sum.

deductions
object[]

Per-balance breakdown of what was consumed. Present only when aggregate_on is "deducted".