Subscriptions

Usage Reporting

Report metered usage against a subscription's current billing period. Usage items are billed on top of the base subscription amount at the next renewal.

How it works

Each usage item is identified by a kind string (e.g. api_calls, storage_gb). There is exactly one row per (subscription, current period, kind) combination. Reporting the same kind twice replaces the previous value — it is an absolute upsert, not an increment. The engine computes totalCents = unitCount × unitPriceCents and stores it on the row.

At renewal the cron sums all usage rows for the period and adds them to the base subscription charge. The base amount and total usage overage appear as separate line items in the renewal invoice.

Required scopes

  • subscription_usage:write — report (upsert) a usage item.
  • subscription_usage:read — list usage items. subscription_usage:write also grants read access.

Report usage

POST /api/v1/subscriptions/{id}/usage

Body fields:

  • kind (string, 1–64 chars, required) — usage category identifier. Use a stable, lowercase slug you control.
  • unitCount (integer ≥ 0, required) — the absolute number of units consumed this period.
  • unitPriceCents (integer ≥ 0, required) — per-unit price in the smallest currency unit (cents for USD).
ts
// Report a metered usage item for the current billing period.
// One row per (subscription, current period, kind) — calling this twice
// with the same kind replaces the previous count (absolute upsert).
const res = await fetch(
  `https://api.usethrottle.dev/api/v1/subscriptions/${subscriptionId}/usage`,
  {
    method: 'POST',
    headers: {
      // Secret API key (sk_test_… / sk_live_…). The key is already pinned to
      // one application + environment, so do NOT also send X-Throttle-Application-Id
      // / X-Throttle-Environment-Id — that returns 400 header_scope_conflict.
      'X-API-Key': process.env.THROTTLE_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      kind: 'api_calls',
      unitCount: 1500,
      unitPriceCents: 2,   // $0.02 per call
    }),
  },
);
const { data } = await res.json();
// data.totalCents === 3000 (1500 × 2)
Absolute, not incremental
Set unitCount to the total consumed so far this period, not the delta. If you called this endpoint earlier with unitCount: 1000 and 500 more units were used, report unitCount: 1500 — not 500.

List usage

GET /api/v1/subscriptions/{id}/usage

Returns all usage items recorded for the subscription's current billing period.

ts
// Retrieve all usage items for the current billing period.
const res = await fetch(
  `https://api.usethrottle.dev/api/v1/subscriptions/${subscriptionId}/usage`,
  {
    headers: {
      'X-API-Key': process.env.THROTTLE_SECRET_KEY,
    },
  },
);
const { items } = await res.json();
// items = [{ id, subscriptionId, kind, unitCount, unitPriceCents, totalCents, periodStart, periodEnd, createdAt }]

Response shape per item:

  • id — UUID of the usage row.
  • subscriptionId — parent subscription UUID.
  • kind — usage category string.
  • unitCount — units reported.
  • unitPriceCents — per-unit price.
  • totalCents — computed unitCount × unitPriceCents.
  • periodStart / periodEnd — ISO 8601 timestamps bounding the current billing period.
  • createdAt — when the row was first written or last upserted.

Multiple usage kinds

A subscription can carry any number of independently tracked usage kinds within a period. At renewal every kind's totalCents is summed and added to the renewal charge.

ts
// Report multiple usage kinds in the same period.
// Each kind is tracked independently; all are billed at renewal.
await reportUsage(subscriptionId, { kind: 'api_calls',  unitCount: 1500, unitPriceCents: 2 });
await reportUsage(subscriptionId, { kind: 'storage_gb', unitCount: 12,   unitPriceCents: 50 });

// Later — update api_calls (replaces 1500 with 1800).
await reportUsage(subscriptionId, { kind: 'api_calls',  unitCount: 1800, unitPriceCents: 2 });
// Now: api_calls = 1800 × 2¢ = $36.00, storage_gb = 12 × 50¢ = $6.00

Billing at renewal

The renewal cron calls getRenewalChargeAmount which sums the subscription base amount plus all usage totalCents for the period. No prorations are applied — the full usage overage is billed in the renewal cycle it was accrued in. After a successful renewal all usage rows for that period are finalized; new usage accumulates against the next period.

Usage is not retroactive
Usage recorded after the renewal cron runs for a period is attributed to the next period. Send your final tally before the periodEnd timestamp on the subscription.

Next