Skip to content

Self-service usage

With persistence enabled, each authenticated user can view their own usage — totals, per-model breakdown, routes, and date ranges — without access to anyone else’s data or to the operator’s Prometheus metrics.

Two surfaces expose it: an authenticated GET /v1/usage endpoint and a llmsoup usage terminal dashboard that renders it.

GET /v1/usage returns the calling user’s usage as JSON. It is mounted behind the same identity-bearing auth layer as /v1/chat/completions (it requires auth.tokens_file), never on the unauthenticated /metrics route. A caller only ever sees the usage attributed to their own token’s user_id.

Terminal window
curl -H "Authorization: Bearer $LLMSOUP_TOKEN" \
https://your-server/v1/usage

An unauthenticated or invalid request is rejected with 401 in the standard OpenAI error format:

{ "error": { "message": "Invalid or missing authentication token", "type": "invalid_request_error", "code": "invalid_api_key" } }

The body is an envelope: usage is the same snapshot the stats dashboard renders (fields with no per-user source, such as active_connections, are 0), and quota reports your current-period limit / used / remaining. The quota block is present only if you have a quota configured and is omitted otherwise; limit and remaining are null for an unlimited dimension.

{
"usage": {
"total_cost": 1.234,
"total_savings": 0.456,
"total_requests": 128,
"active_connections": 0,
"errors": 2,
"model_tokens": { "gpt-5-mini": { "input": 40100, "output": 15980 } },
"model_costs": { "gpt-5-mini": 1.234 },
"model_latencies": { "gpt-5-mini": 0.82 },
"model_selections": {},
"triggered_routes": { "code_review": 40 },
"errors_by_type": { "model_error": 2 }
},
"quota": {
"period": "monthly",
"requests": { "limit": 10000, "used": 128, "remaining": 9872 },
"cost": { "limit": null, "used": 1.234, "remaining": null },
"tokens": { "limit": null, "used": 56080, "remaining": null }
}
}

The quota block always reflects the current quota period regardless of the window you request — a window only changes the usage figures.

A window query parameter selects the reporting range. lifetime and period are served from the live accumulator; bounded ranges are summed from the daily rollups. The period window reports only the current period’s consumption — once a period boundary has rolled over it reads zero rather than surfacing the previous period’s counters, consistent with the quota block above.

QueryMeaning
?window=lifetimeAll-time totals with full per-model detail (default)
?window=periodCurrent quota period (aggregate totals)
?window=7dLast 7 days
?window=30dLast 30 days
?window=mtdMonth-to-date
?from=YYYY-MM-DD&to=YYYY-MM-DDExplicit date range

Bounded ranges (7d, 30d, mtd, and explicit from/to) carry headline totals only — per-model and latency detail is available for lifetime. A range that reaches earlier than the retained rollups (see daily_retention_days) is clamped; the effective range is reported in the x-llmsoup-usage-from and x-llmsoup-usage-to response headers.

llmsoup usage opens the stats TUI pointed at a running server’s /v1/usage endpoint, rendering your own usage with the familiar widgets. It fetches on the same 500 ms tick as --stats.

Terminal window
export LLMSOUP_ENDPOINT="https://your-server"
export LLMSOUP_TOKEN="sk-your-token"
llmsoup usage
# or pass the endpoint explicitly:
llmsoup usage --endpoint https://your-server
  • Endpoint comes from --endpoint or the LLMSOUP_ENDPOINT environment variable.
  • Token is read from the LLMSOUP_TOKEN environment variable only. There is deliberately no --token <value> flag, so the token never appears in ps, /proc/<pid>/cmdline, or your shell history. It is never logged or echoed.
  • The command fails with a clear message if either the endpoint or the token is unset.
KeyAction
wCycle the reporting window (lifetime → current period → last 7d → last 30d → month-to-date) and re-query
rReset the view baseline (client-side only — never affects the server or other viewers)
mToggle the per-model gauge between cost and token share
q / Ctrl-CQuit

Reset and uptime are per-viewer and applied entirely on the client, so the server stays stateless per viewer. The reset baseline is per-window: cycling the window with w clears it, so each window shows real totals until you reset it again.

The usage dashboard opens on the lifetime window (which carries full per-model, per-route, and latency detail) — press w to cycle to the current period or a date range. It has no server-side logs to show, so the bottom panel is a Quota panel instead: one progress bar per limited dimension (requests / cost / tokens) for the current period, coloured green → orange → red as you approach your limit, with the used / limit and percentage. It is shown only if you have a quota configured.