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.
The GET /v1/usage endpoint
Section titled “The GET /v1/usage endpoint”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.
curl -H "Authorization: Bearer $LLMSOUP_TOKEN" \ https://your-server/v1/usageAn 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" } }Response shape
Section titled “Response shape”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.
Windows and date ranges
Section titled “Windows and date ranges”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.
| Query | Meaning |
|---|---|
?window=lifetime | All-time totals with full per-model detail (default) |
?window=period | Current quota period (aggregate totals) |
?window=7d | Last 7 days |
?window=30d | Last 30 days |
?window=mtd | Month-to-date |
?from=YYYY-MM-DD&to=YYYY-MM-DD | Explicit 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.
The llmsoup usage dashboard
Section titled “The llmsoup usage dashboard”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.
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
--endpointor theLLMSOUP_ENDPOINTenvironment variable. - Token is read from the
LLMSOUP_TOKENenvironment variable only. There is deliberately no--token <value>flag, so the token never appears inps,/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.
| Key | Action |
|---|---|
w | Cycle the reporting window (lifetime → current period → last 7d → last 30d → month-to-date) and re-query |
r | Reset the view baseline (client-side only — never affects the server or other viewers) |
m | Toggle the per-model gauge between cost and token share |
q / Ctrl-C | Quit |
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.