---
title: "Usage Series"
method: GET
path: "/v1/usage/series"
tags: ["usage"]
---

# Usage Series

`GET /v1/usage/series`

Time series split by one dimension, for the dashboard's stacked charts.

Same filters and window bounds as ``/summary`` (kept in lockstep: the
dashboard serializes one filter object for both, and a filter this endpoint
silently ignored would make the stacked chart disagree with the tiles beside
it). The window's top groups by spend are returned as their own series;
everything past the top eight folds into a single ``other`` series per
bucket, so the stack always reconciles with the summary totals. Points are
sparse (populated cells only); the bucket grid is bounded like ``/summary``'s
series, so an hourly bucket over a too-wide window is rejected rather than
ballooning the payload.

## Query parameters

- `group_by` 'model' | 'user_id' | 'api_key_id' | 'source', required — Dimension to split the series by
- `start_date` string, date-time, nullable — Return logs with timestamp >= start_date (ISO 8601 or Unix epoch seconds)
- `end_date` string, date-time, nullable — Return logs with timestamp < end_date (ISO 8601 or Unix epoch seconds)
- `user_id` string[], nullable — Filter to one or more users; repeatable (user_id=a&user_id=b). Several values match any of them. At most 50 per call.
- `status` string, nullable — Filter to a single status: 'success', 'error', or 'absorbed' (an attempt a routing policy recovered from, excluded from error_count and request_count)
- `status_code` integer, nullable — Filter to a single failure status code (e.g. 429 for provider rate limits, 402 for missing-pricing rejections). Only error rows carry one, so this filter also restricts to status='error' unless 'status' is given explicitly
- `model` string[], nullable — Filter to one or more models; repeatable (model=a&model=b). Several values match any of them. At most 50 per call.
- `endpoint` string, nullable — Filter to a single endpoint (e.g. '/v1/chat/completions')
- `provider` string, nullable — Filter to a single provider (e.g. 'openai')
- `source` string, nullable — Filter to a single provenance source (e.g. 'gateway' or 'claude_code')
- `source_label` string, nullable — Filter to a single session/project label (the source_label carried by imported usage)
- `api_key_id` string[], nullable — Filter to one or more API key ids; repeatable (api_key_id=a&api_key_id=b). Several values match any of them. At most 50 per call.
- `priced` boolean, nullable — Filter by token-pricing state: true = only rows whose model tokens were priced, false = only rows that still need pricing (no cost at all, or tokens that were never metered because the model had no rate). A row charged only for gateway-run tool calls still counts as needing pricing.
- `tool` 'any' | 'web_search' | 'code_execution', nullable — Filter to requests that ran a gateway-run tool. 'any' matches any tool; a tool name (web_search, code_execution) matches that tool specifically.
- `counts_toward_budget` boolean, nullable — Filter by budget participation: true = only enforced gateway rows, false = only imported rows that never touch a budget
- `workspace_id` string, uuid, nullable — Only usage recorded in this workspace.
- `bucket` 'hour' | 'day' — Time-series granularity: 'hour' or 'day'

## Response `200`

Successful Response

- UsageGroupedSeries — A per-group time series for the dashboard's stacked charts. ``groups`` ranks the window's top groups by spend (plus the reconciling ``other`` fold), in the order a chart should stack and color them; ``points`` is sparse (only populated cells), keyed by canonical UTC ``bucket_start``.
  - `bucket` 'hour' | 'day', required
  - `end_date` string, required
  - `group_by` 'model' | 'user_id' | 'api_key_id' | 'source', required
  - `groups` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `points` UsageGroupedSeriesPoint[], required
    - `bucket_start` string, required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `requests` integer, required
    - `tokens` integer, required
  - `start_date` string, required

## Other responses

- `422` — Validation Error

## Changes

> 126 revisions in range; 18 could not be searched.

- **2026-08-19** `3945cde96091` — 1 info
  - added the new optional `query` request parameter `workspace_id`
- **2026-08-07** `9efeac9dd037` — 1 info
  - added the optional property `groups/items/label` to the response with the `200` status
- **2026-08-06** `6a81ed6c41ee` — 6 breaking
  - `query` request parameter `api_key_id` list-of-types was narrowed by removing types `string`
  - `query` request parameter `model` list-of-types was narrowed by removing types `string`
  - `query` request parameter `user_id` list-of-types was narrowed by removing types `string`
  - for the `query` request parameter `api_key_id`, the type/format of property `anyOf[subschema #1]/` was specialized from `string`/`` to `array`/``
  - …2 more
- **2026-08-05** `c339f3aee928` — 1 info
  - added the new optional `query` request parameter `tool`
- **2026-08-04** `f1f390d57e3a` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/mozilla-ai/apis/otari/changes/v1/usage/series/get.md)

---

[API](https://skmtc.dev/mozilla-ai/apis/otari.md) · [All operations](https://skmtc.dev/mozilla-ai/apis/otari/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/mozilla-ai/otari/revisions/7f178e92b56c/schema)
