---
title: "Usage Summary"
method: GET
path: "/v1/usage/summary"
tags: ["usage"]
---

# Usage Summary

`GET /v1/usage/summary`

Aggregate spend, tokens, and request volume for the dashboard Usage page.

Range-bounded (default last 30 days, hard-capped): unlike the raw ``/v1/usage``
list, every aggregate is scoped to a bounded window so it stays served by the
timestamp index. Returns grand totals, breakdowns by model / user / API key /
source / session (``source_label``) / endpoint / provider (top rows plus a
reconciling ``other`` fold, billed token counts), the error taxonomy grouped
by failure status code, and a UTC-bucketed time series carrying each bucket's
error count and billed token composition (input incl. cache, cache read/write,
output).

Each breakdown is its own ``GROUP BY`` pass, so a caller that reads only the
totals or the series should narrow ``dimensions`` rather than pay for all eight
(the dashboard's tiles, timeline context, and model typeahead all do). Omitting
the parameter keeps the full set.

``model``, ``user_id``, and ``api_key_id`` are repeatable: several values match
any of them, so one chart can compare a handful of models, users, or keys.

## Query parameters

- `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
- `bucket` 'hour' | 'day' — Time-series granularity: 'hour' or 'day'
- `dimensions` string[], nullable — Which breakdowns to compute; repeatable (dimensions=model&dimensions=user). Each value names the 'by_<value>' response field it fills, except 'status_code', which fills the failure taxonomy in 'errors_by_status_code'. Omit for every breakdown (the default); pass 'none' for a totals-and-series-only response. Each dimension left out skips one GROUP BY scan, so a caller that reads only the tiles or the time series should say so. Fields that were not requested come back empty.

## Response `200`

Successful Response

- UsageSummary — Aggregate spend/volume for the Usage & analytics page. Every breakdown field is always present. One the caller excluded through ``dimensions`` comes back as an empty list, the same shape a window with no matching rows produces, so narrowing the selector never changes the schema.
  - `bucket` 'hour' | 'day', required
  - `by_api_key` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `by_endpoint` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `by_model` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `by_provider` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `by_source` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `by_source_label` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `by_tool` UsageToolRow[]
    - `calls` integer, required
    - `cost` number, required
    - `errors` integer, required
    - `requests` integer, required
    - `tool` string, required
  - `by_user` UsageGroupRow[], required
    - `cost` number, required
    - `is_other` boolean
    - `key` string, nullable, required
    - `label` string, nullable
    - `requests` integer, required
    - `tokens` integer, required
  - `end_date` string, required
  - `errors_by_status_code` UsageErrorCodeRow[], required
    - `error_class` 'pricing' | 'rate_limit' | 'auth' | 'provider_error' | 'client_error' | 'unknown', required
    - `requests` integer, required
    - `status_code` integer, nullable, required
  - `series` UsageSeriesPoint[], required
    - `bucket_start` string, required
    - `cache_read_tokens` integer
    - `cache_write_tokens` integer
    - `cost` number, required
    - `errors` integer
    - `input_tokens` integer
    - `output_tokens` integer
    - `requests` integer, required
    - `tokens` integer, required
  - `start_date` string, required
  - `totals` UsageTotals, required — Grand totals over the filtered window.
    - `avg_latency_ms` number, nullable, required
    - `billed_input_tokens` integer
    - `billed_output_tokens` integer
    - `cache_read_tokens` integer, required
    - `cache_write_1h_tokens` integer, required
    - `cache_write_tokens` integer, required
    - `completion_tokens` integer, required
    - `cost` number, required
    - `error_count` integer, required
    - `prompt_tokens` integer, required
    - `request_count` integer, required
    - `total_tokens` integer, required
    - `unpriced_requests` integer

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/mozilla-ai/apis/otari.md) · [All operations](https://skmtc.net/mozilla-ai/apis/otari/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/mozilla-ai/otari/versions/9efeac9dd037/schema)
