Skip to content

Console

API monitor

Every request that reached the Gateway, including the ones that failed before a token existed. Counts, percentiles and a failure breakdown, over real rows.

/dashboard/api answers one question: is the API working. The priced ledger could already tell you what you spent, but a 502 leaves no tokens behind, so spend alone cannot see an outage. This surface reads the request trace table instead, where a row is written for every request whatever its outcome.

Every figure on the page is a count or a percentile over those rows. Nothing is modelled, scored or estimated. Where a number would have to be invented, it is not shown.


The headline row

MetricWhat it is
RequestsRows in the window. The sub-line counts how many were streamed.
Error rateRows with a status code of 400 or above, over total rows. Green to 1%, amber to 5%, red beyond.
p50 latencyThe median of every recorded latency in the window.
p95 latencyThe 95th percentile. Its sub-line carries p99.
CostSum of the per-request cost recorded at request time, with total tokens beneath.

The percentiles are computed over the raw latency values, sorted, not as an average of per-bucket averages. That distinction matters: averaging buckets produces a p95 nobody ever experienced.


The failure taxonomy

When there are errors in the window, they are broken into six classes, sorted by count, each with a share bar. The taxonomy is deliberately small: six buckets that each imply a different response, rather than a long list nobody can group by.

ClassWhat it meansWhose move
auth401 or 403 — the credential was rejected.The caller fixes its key.
quota402 or 429 — the allowance is exhausted.Upgrade, top up, or wait for the reset.
validation400, 409 or 422 — the request was malformed.The caller fixes the call.
providerA 5xx from upstream that is not our own 500.Retry. Not the caller's fault.
timeout504 or 408 — we gave up waiting.Retry.
internal500, and anything thrown that we could not classify.Ours. Quote the request id.

An unknown throw is classified as internal, not provider. Blaming upstream by default would quietly hide our own crashes. A seventh label, unclassified, appears only on rows written before the taxonomy existed.


Filters and breakdowns

The window is 7, 14, 30 or 90 days. The breakdown below it groups the same rows four ways:

GroupingKeyUse it for
Over timeCalendar day (UTC)Did something change, and when.
By modelThe model that served the requestWhich model is slow, or expensive, or failing.
By keyThe API key that authenticated itWhich client caused this spike.
By runtimeThe legacy agent row the request was attributed to, or noneSeparating traffic from a legacy agent credential from traffic on an API key.

Each row carries requests, errors, tokens, cost, p50 and p95 for that group alone. Under Over time a stacked bar chart sits above the table, successes at the baseline and failures on top, with the peak day's request count stated in the footer rather than the axis maximum.

One more filter appears when a runtime is selected in the sidebar: a This runtime only toggle. Off, the page covers the whole organization, which is the default because an organization is the unit a bill and an incident belong to.

High volume reads as a floorThe endpoint pages through at most 20,000 rows per window. When that cap binds, the page says so and the sums are a floor rather than a total. It is better than a confident wrong number, but do not treat a truncated total as exact.

What a trace row contains

A trace row is written for every request that reaches the Gateway, before it is known whether the request will succeed. Its id is allocated up front so it can be returned even when everything else fails.

FieldNote
idThe request id. Returned to the caller and quotable in support.
org_id, api_key_id, agent_idWho the request belonged to. Any of the last two may be null.
routeWhich Gateway route handled it, e.g. chat.completions.
requested_model, served_model, routedWhat was asked for, what answered, and whether those differ.
providerThe upstream provider that served it.
status_codeThe HTTP status returned to the caller.
error_class, error_code, error_messageThe bucket, the provider's code, and the message truncated to 1,000 characters.
latency_ms, ttft_msTotal latency, and time to first token on streamed requests.
prompt_tokens, completion_tokens, total_tokensZero on requests that failed before inference.
cost_usdPriced at request time. Null means unpriced, which is not the same as free.
modemanaged (our provider key) or byok (yours).
streamWhether the caller asked for server-sent events.
clientThe caller's self-reported X-Lobstack-Client or user agent, truncated to 200 characters. A label, never an authorization input.

How a row maps to the headers the caller saw

The trace row and the response headers are written from the same request, so an operator and a caller are describing the same event. If somebody quotes you a header value, this is the column it landed in.

Response headerTrace column
x-lobstack-request-idid
x-lobstack-modelserved_model
x-lobstack-routedrouted
x-lobstack-modemode
x-lobstack-cost-usdcost_usd
x-lobstack-tierNot stored. The tier is on the response only; the trace keeps the model, not the tier it came from.

The request id is on successful responses, on error responses, and inside the error body. It is the fastest route from “something went wrong at 14:02” to the row that says what.

An error bodyjson
{
  "error": {
    "message": "monthly allowance exhausted ($10.00 of $10.00 of model spend). Add a top-up or upgrade the plan, or wait for the period to reset.",
    "type": "quota",
    "code": 402,
    "request_id": "9f1c8f42-3a7e-4c19-9a0b-1d2e3f4a5b6c"
  }
}
This surface aggregates; Logs itemisesNothing on this page lists individual requests — every figure here is a count or a percentile over a group. The itemised view is Logs, which puts one row per request, newest first, and opens the whole trace row behind any of them. Both read the same table, so a number that differs between the two is a bug rather than a different definition.

The same numbers, programmatically

The panel is a client of GET /api/v1/usage and adds no arithmetic of its own. A key with the usage:read scope reads the identical response, so a script and the Console can never disagree.

bash
curl "https://www.lobstack.ai/api/v1/usage?range=7d&group_by=key" \
  -H "Authorization: Bearer $LOBSTACK_API_KEY"

The panel refreshes itself every 30 seconds. Traces are written on every request, so this stays close to live without polling a table that is scanned rather than indexed by minute. More on the credential in API keys.

Lobstack

One key over every frontier model, a receipt on every call, and an agent that waits before it changes anything.

© 2026 LobstackAll rights reserved  Status