GET /v1/usage is a forward-only cursor pagination — pass limit for the page size and use the response’s next_page token to walk forward. The cursor encodes the query fingerprint so filters cannot drift between pages.
Basic walk
page_token is provided, you do NOT need to re-send start_time, end_time, bucket_width, group_by, or filter parameters — they’re encoded in the cursor. In fact, sending them with different values mismatches the fingerprint and returns:
now for end_time on page 2) would return different data than page 1 sees — silent skew. The fingerprint check is Stripe-grade and we don’t relax it.
Cursor lifetime
- Tokens expire 24 hours after the page-1 response that minted them. Walking pages slower than 24 hours apart returns
400 invalid_page_tokenwithtoken_expireddetail. - Tokens are scoped to your team — using a cursor from team A’s response against team B’s API key returns
400 invalid_page_token.
Cursor opacity
Thenext_page value is base64url-encoded JSON with version + query fingerprint + last-bucket-start + expiry. Don’t parse it yourself — treat it as opaque. Future versions of the platform may add fields or change the encoding entirely.
Known edge case — limit smaller than per-bucket group count
The cursor encodes last_bucket_start (the timestamp of the last bucket returned on the current page). If a bucket has N groups and your limit is less than N:
- Page 1 returns the bucket with the first
limitgroups;has_more: true;next_pageencodes that bucket’sbucket_start - Page 2 — using the cursor — starts AFTER
bucket_start, so the remaining groups in that bucket are LOST
- Your
group_byis multi-dimensional (e.g.group_by=type,modelcould produce 8 groups per bucket: 4 types × 2 models) - Your
limitis unusually small (< the maximum expected groups-per-bucket)
limit ≥ the max possible groups per bucket. For a single group_by like type (≤ 4 groups), limit ≥ 4 is safe. For multi-dim group_by, multiply: group_by=type,model → limit ≥ 16 is safe up to 8 models. The defaults (limit=100) are generous enough that most production callers never hit this.
The proper fix is to paginate at the bucket level (return whole buckets, never split a bucket’s groups across pages). It’s on the v1.1 roadmap — see the pagination plan in launch-week followups.
Stable ordering
Within a response, buckets are ordered bybucket_start ascending (oldest first). Within a bucket, groups are ordered by total credit charge descending (highest spend first), then by dimensions alpha-sort tie-break.
The order is stable across page walks for a given fingerprint — paging forward through a 1000-row window deterministically returns the same row order whether you do it in one shot or in five 200-row pages.
Sample code
Where to next?
- Usage overview — the full query surface
- Usage event stream — per-event detail rows
GET /v1/usage— endpoint reference

