Aggregated Expenses API
Read-only endpoint that returns a paginated, aggregated breakdown of a user's prediction request expenses (grouped by model), along with summary totals.
Endpoint
GET /<username>/expenses/
username is a path parameter identifying the target user whose expenses are being queried (resolved via UserAccessMixin.get_target_user()).
Authentication
Requires authentication via API key. Include your key in the appropriate request header.
Permissions
IsAuthenticated— the request must be authenticated (see Authentication above).- Access to the
usernamein the path is governed byUserAccessMixin— the authenticated caller must be authorized to view that user's data (e.g. themselves, or an admin/service account with elevated access, depending onUserAccessMixin's implementation).
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
start_date | datetime (ISO 8601) | No | 7 days before now | Start of the date range to aggregate expenses over. |
end_date | datetime (ISO 8601) | No | now | End of the date range to aggregate expenses over. |
page | integer | No | 1 | 1-indexed page number. Internally converted to a 0-indexed offset ((page - 1) * 10), i.e. 10 results per page. |
If omitted entirely, the request defaults to the last 7 days, page 1.
Invalid parameter values (e.g. unparseable datetime, non-integer page) return a 400 Bad Request with serializer validation errors.
Response
{
"results": [
{
"model_name": "string",
"request_count": 0,
"sub_total_spent": 0.0,
"avg_per_request": 0.0
}
],
"start_date": "2026-06-28T00:00:00Z",
"end_date": "2026-07-05T00:00:00Z",
"page": 1,
"total_spent": 0.0,
"total_count": 0,
"total_models": 0,
"total_pages": 0
}
Top-level fields
| Field | Description |
|---|---|
results | List of per-model expense entries. |
start_date / end_date | Echoes the resolved date range (either what was requested, or the applied defaults). |
page | Echoes the requested page number. |
total_spent | Sum of spend across all models in the range (not just the current page). |
total_count | Sum of request counts across all models in the range. |
total_models | Total number of distinct models with expenses in the range (used for pagination). |
total_pages | Total number of pages available, computed from total_models at 10 per page. |
results[] fields (AggregatedExpenseItemSerializer)
| Field | Type | Description |
|---|---|---|
model_name | string | Name of the model. |
request_count | integer | Number of requests for this model in the range. |
sub_total_spent | float | Total spend for this model in the range. |
avg_per_request | float | Average spend per request for this model. |
Example
GET /alice/expenses/?start_date=2026-06-01T00:00:00Z&end_date=2026-06-30T23:59:59Z&page=1
GET /alice/expenses/
(no params — defaults to last 7 days, page 1)
Behavior Notes
- Data is sourced from ClickHouse via
predictionRequestClickhouseRepo.get_aggregated_expenses, not the primary Postgres database. - Registered on a
DefaultRouterunder basenameexpenses, nested beneath<str:username>/, alongside sibling endpointspayments,transactions,usage,subscriptions, andexpenses-granularon the same router. - The
retrieve(single-object detail) action is disabled — this viewset only supports listing; callingGET /<username>/expenses/<id>/returns405 Method Not Allowed, and it is excluded from the generated OpenAPI schema (@extend_schema(exclude=True)). - Only
listis a supported operation; no create/update/delete actions exist (ReadOnlyModelViewSet).
Errors
| Status | Cause |
|---|---|
400 Bad Request | Query parameters failed validation (unparseable start_date/end_date, non-integer page). |
401 Unauthorized | Missing or invalid credentials. |
403 Forbidden | Authenticated but not permitted to access the target username's data. |
404 Not Found | username does not correspond to an existing user. |
405 Method Not Allowed | Attempted to call the detail route (GET /<username>/expenses/<id>/) on this endpoint. |