Skip to content

Usage-based billing

BoxBilling supports full usage-based billing. The pipeline works in three stages:

Events → Billable Metrics → Charges → Fees on Invoices
  1. Events are ingested via the API with a metric code, customer ID, and properties
  2. Billable Metrics define how events are aggregated (count, sum, max, etc.)
  3. Charges attach metrics to plans with a pricing model (standard, graduated, volume, etc.)
  4. Fees are calculated at invoice time based on aggregated usage
Terminal window
curl -X POST /v1/events \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transaction_id": "txn_unique_123",
"external_customer_id": "cust_001",
"code": "api_calls",
"timestamp": "2025-01-15T10:30:00Z",
"properties": {
"region": "us-east",
"method": "POST",
"bytes": 1024
}
}'

You can also pass an Idempotency-Key header for request-level deduplication in addition to transaction_id.

Terminal window
curl -X POST /v1/events/batch \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"transaction_id": "txn_1",
"external_customer_id": "cust_001",
"code": "api_calls",
"timestamp": "2025-01-15T10:30:00Z",
"properties": {"region": "us-east"}
},
{
"transaction_id": "txn_2",
"external_customer_id": "cust_001",
"code": "api_calls",
"timestamp": "2025-01-15T10:31:00Z",
"properties": {"region": "eu-west"}
}
]
}'
  • Up to 100 events per batch
  • Events are idempotent — duplicate transaction_id values return the existing event
  • Rate limited per organization (default: 1000/minute, configurable)
  • The response includes ingested and duplicates counts so you can verify delivery

Preview the fees that would result from a hypothetical event without actually ingesting it:

Terminal window
curl -X POST /v1/events/estimate_fees \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription_id": "sub-uuid",
"code": "api_calls",
"properties": {"region": "us-east"}
}'

Returns charge_model, metric_code, units, amount_cents, and unit_amount_cents.

Re-triggers usage threshold and alert checks for all active subscriptions associated with an event:

Terminal window
curl -X POST /v1/events/{event_id}/reprocess \
-H "Authorization: Bearer $API_KEY"

Get hourly event counts for a time range:

Terminal window
curl "/v1/events/volume?from_timestamp=2025-01-01T00:00:00Z&to_timestamp=2025-01-02T00:00:00Z" \
-H "Authorization: Bearer $API_KEY"

Returns an array of {timestamp, count} data points.

For high-volume event ingestion, enable ClickHouse by setting the CLICKHOUSE_URL environment variable. Events are dual-written to both the primary database and ClickHouse, with aggregation queries routed to ClickHouse for performance.

MethodPathDescription
POST/v1/events/Ingest a single event
POST/v1/events/batchIngest up to 100 events
GET/v1/events/List events (filterable by external_customer_id, code, from_timestamp, to_timestamp)
GET/v1/events/{event_id}Get event by ID
POST/v1/events/{event_id}/reprocessReprocess an event
POST/v1/events/estimate_feesEstimate fees for a hypothetical event
GET/v1/events/volumeGet hourly event volume

Billable metrics define how raw events are aggregated into usage values.

Terminal window
curl -X POST /v1/billable_metrics \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "api_calls",
"name": "API Calls",
"aggregation_type": "count"
}'
TypeDescriptionRequires field_name
countNumber of eventsNo
sumSum of a numeric propertyYes
maxMaximum value of a propertyYes
unique_countCount of distinct property valuesYes
weighted_sumTime-weighted sum (proportional to duration)Yes
latestMost recent event’s valueYes
customCustom expression evaluationRequires expression
Terminal window
curl -X POST /v1/billable_metrics \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "data_transfer",
"name": "Data Transfer (GB)",
"aggregation_type": "sum",
"field_name": "bytes",
"rounding_function": "ceil",
"rounding_precision": 0
}'

Set recurring: true to persist aggregated values across billing periods instead of resetting to zero. Useful for metrics like “total storage used” where the value carries forward.

FunctionBehavior
roundHalf-up rounding
ceilRound up
floorRound down

Precision: 0–15 decimal places.

Filters segment events by property values, enabling different pricing per segment:

Terminal window
curl -X POST /v1/billable_metrics/api_calls/filters \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "region",
"values": ["us-east", "us-west", "eu-west"]
}'

List existing filters with GET /v1/billable_metrics/{code}/filters and remove with DELETE /v1/billable_metrics/{code}/filters/{filter_id}.

MethodPathDescription
POST/v1/billable_metrics/Create a metric
GET/v1/billable_metrics/List metrics
GET/v1/billable_metrics/{metric_id}Get metric by ID
PUT/v1/billable_metrics/{metric_id}Update a metric
DELETE/v1/billable_metrics/{metric_id}Delete a metric
GET/v1/billable_metrics/statsGet metrics statistics (total + breakdown by aggregation type)
GET/v1/billable_metrics/plan_countsGet plan counts per metric
GET/v1/billable_metrics/{metric_id}/plansList plans using a metric
POST/v1/billable_metrics/{code}/filtersCreate a metric filter
GET/v1/billable_metrics/{code}/filtersList metric filters
DELETE/v1/billable_metrics/{code}/filters/{filter_id}Delete a metric filter

Charges connect billable metrics to plans with a pricing model. When creating a plan, include charges in the request:

Terminal window
curl -X POST /v1/plans \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "pro",
"name": "Pro Plan",
"interval": "monthly",
"amount_cents": 9900,
"charges": [
{
"billable_metric_id": "metric-uuid",
"charge_model": "standard",
"properties": {
"unit_price": "0.01"
}
}
]
}'
ModelDescription
standardFixed per-unit price
graduatedTiered pricing — different rates per unit range
volumeTotal units determine per-unit price for all units
packageFixed price per block of units
percentagePercentage of a total amount plus optional fixed fee
graduated_percentageTiered percentage rates based on amount ranges
customCustom expression evaluation
dynamicPer-event property-based pricing

Simple per-unit pricing.

{
"charge_model": "standard",
"properties": {
"unit_price": "0.01"
}
}

Formula: amount = units × unit_price

Tiered pricing where different unit ranges have different prices.

{
"charge_model": "graduated",
"properties": {
"tiers": [
{"from": 0, "to": 100, "unit_price": "0.10", "flat_fee": "0"},
{"from": 101, "to": 1000, "unit_price": "0.05", "flat_fee": "0"},
{"from": 1001, "to": null, "unit_price": "0.01", "flat_fee": "0"}
]
}
}

The total units determine the per-unit price for all units.

{
"charge_model": "volume",
"properties": {
"tiers": [
{"from": 0, "to": 100, "unit_price": "0.10", "flat_fee": "0"},
{"from": 101, "to": null, "unit_price": "0.05", "flat_fee": "0"}
]
}
}

Fixed price per block of units.

{
"charge_model": "package",
"properties": {
"package_size": 10,
"package_price": "50.00"
}
}

Percentage of a total amount.

{
"charge_model": "percentage",
"properties": {
"rate": "2.5",
"fixed_amount": "0.30"
}
}

Tiered percentage rates based on amount ranges.

Uses a custom expression for aggregation:

{
"charge_model": "custom",
"properties": {
"expression": "cpu_seconds + memory_gb * 2"
}
}

Per-event property-based pricing — each event is priced individually based on its properties.

Apply different pricing based on event properties:

{
"charges": [
{
"billable_metric_id": "metric-uuid",
"charge_model": "standard",
"properties": {"unit_price": "0.01"},
"filters": [
{
"billable_metric_filter_id": "filter-uuid",
"values": ["us-east"],
"properties": {"unit_price": "0.02"},
"invoice_display_name": "US East API Calls"
}
]
}
]
}

Events matching the filter values use the filter’s properties (here, $0.02/unit for us-east). All other events use the base charge properties ($0.01/unit). Use invoice_display_name to customize how the filter appears on invoices.

Trigger actions when cumulative usage crosses a monetary threshold within a billing period.

Terminal window
# On a plan (applies to all subscriptions)
curl -X POST /v1/plans/{plan_code}/usage_thresholds \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 10000,
"recurring": true,
"threshold_display_name": "Usage cap ($100)"
}'
# On a specific subscription
curl -X POST /v1/subscriptions/{id}/usage_thresholds \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 5000,
"recurring": false
}'

When a threshold is crossed:

  1. A usage_threshold.crossed webhook is sent
  2. Progressive billing invoices can be generated for early collection
Terminal window
curl /v1/subscriptions/{id}/current_usage \
-H "Authorization: Bearer $API_KEY"

Returns current_usage_amount_cents, billing_period_start, and billing_period_end.

MethodPathDescription
POST/v1/plans/{plan_code}/usage_thresholdsCreate plan-level threshold
GET/v1/plans/{plan_code}/usage_thresholdsList plan-level thresholds
POST/v1/subscriptions/{id}/usage_thresholdsCreate subscription-level threshold
GET/v1/subscriptions/{id}/usage_thresholdsList subscription-level thresholds
DELETE/v1/usage_thresholds/{threshold_id}Delete a threshold

Usage alerts monitor metric-level usage on a subscription and fire when a threshold value is crossed.

Terminal window
curl -X POST /v1/usage_alerts \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subscription_id": "sub-uuid",
"billable_metric_id": "metric-uuid",
"threshold_value": "1000",
"recurring": true,
"name": "API calls limit"
}'
  • recurring: true — the alert resets and can fire again each billing period
  • recurring: false — the alert fires once and stays triggered
Terminal window
curl /v1/usage_alerts/{alert_id}/status \
-H "Authorization: Bearer $API_KEY"

Returns current_usage, threshold_value, usage_percentage, and the current billing_period_start/billing_period_end.

Manually check whether an alert would trigger based on current usage:

Terminal window
curl -X POST /v1/usage_alerts/{alert_id}/test \
-H "Authorization: Bearer $API_KEY"
Terminal window
curl /v1/usage_alerts/{alert_id}/triggers \
-H "Authorization: Bearer $API_KEY"

Returns past trigger records including current_usage, threshold_value, metric_code, and triggered_at.

MethodPathDescription
POST/v1/usage_alerts/Create an alert
GET/v1/usage_alerts/List alerts
GET/v1/usage_alerts/{alert_id}Get alert by ID
PATCH/v1/usage_alerts/{alert_id}Update alert (threshold_value, name, recurring)
DELETE/v1/usage_alerts/{alert_id}Delete alert
GET/v1/usage_alerts/{alert_id}/statusGet current usage vs. threshold
POST/v1/usage_alerts/{alert_id}/testTest alert against current usage
GET/v1/usage_alerts/{alert_id}/triggersList trigger history

Track daily usage over time for a subscription:

Terminal window
curl "/v1/subscriptions/{id}/usage_trend?start_date=2025-01-01&end_date=2025-01-31" \
-H "Authorization: Bearer $API_KEY"

Returns an array of data points with date, value, and events_count for each day.

A background task runs daily at 00:30 UTC to pre-aggregate usage data into the daily_usages table. This speeds up period-based usage queries for invoice generation and threshold checking.