List Usage Events
GET /environments/{env_id}/usage_events
GET /environments/{env_id}/usage_eventsInteractive request editor loads with JavaScript.
Authorization
BearerAdminToken Organization-scoped admin token. Read operations require a valid token, and write operations require a token with write permission.
In: header
Path Parameters
Environment ID.
uuidQuery Parameters
Inclusive lower bound on occurred_at, RFC 3339.
date-timeExclusive upper bound on occurred_at, RFC 3339. Must be
strictly after from.
date-timeRestrict to requests dispatched to this model.
uuidRestrict to requests authenticated by this caller API key.
uuidRestrict to requests whose authenticating key belonged to this
organization member, given as the membership id from
GET /members. Matches the user_id snapshotted on the row, so a
member since removed from the organization still filters
correctly.
uuidRestrict by HTTP status code. Three accepted spellings: a family
(2xx, 3xx, 4xx, 5xx), one exact code (429), or an
inclusive range (500-599). Bounds outside 100-599 are rejected.
^([2-5]xx|[1-5][0-9]{2}(-[1-5][0-9]{2})?)$Restrict to requests an enforcing guardrail refused.
Restrict to requests at least one monitor-mode guardrail hit
without acting on.
Restrict to requests an enforcing guardrail rewrote. Refusals are
reported by guardrail_blocked instead.
Restrict to one traffic kind. Gateway traffic (mcp, a2a)
carries server/tool and agent/method attribution in place of the
model and token columns that LLM traffic has.
Value in
- "llm"
- "mcp"
- "a2a"
Exact match on the request id. Free-form rather than a UUID: the
id an operator has in hand is often the caller's own
x-aisix-request-id, not a gateway-minted UUID.
Case-insensitive substring match on the model alias the caller addressed, which for a routed request is the Model-Group name rather than the dispatched target. Being a substring match, a short alias also matches longer ones that contain it.
Exact match on the kind of work the request asked for — chat,
messages, count_tokens, responses, completions,
embeddings, rerank, image_generation, image_edit,
transcription, translation, speech, video_generation,
realtime, files, batches, fine_tuning, batch_completion,
mcp, a2a or passthrough.
Matching is exact rather than substring, so this selects a kind of
call rather than a text fragment: image selects nothing, while
image_generation selects image generations. It cuts across
traffic rather than within it — mcp, a2a and passthrough
are operations too.
Deliberately not a closed enum, like a2a_operation beside it: a
gateway newer than this document records operations this list does
not name yet, and rejecting them would make the newest traffic the
only traffic an operator cannot filter for. An unknown value simply
matches nothing.
Exact match on the MCP server name the request reached.
Case-insensitive substring match on the MCP tool name the request
invoked. mcp_server matches exactly, so pair the two to scope a
tool search to one server.
Exact match on the A2A agent name the request reached.
Case-insensitive substring match on the JSON-RPC method as the
caller wrote it. Use a2a_operation, which matches exactly, to
select every spelling of one operation.
Exact match on the canonical A2A operation, which folds together the wire spellings that name it.
Exact match on the A2A task id the request produced or advanced.
Exact match on the A2A context id the request belongs to.
Exact match on the last task state the agent reported.
Case-insensitive substring match on the verified external identity a JWT-authenticated request ran as.
Case-insensitive substring match on the claim mapping that selected the caller API key. Requests whose key binds the token subject directly carry no mapping and never match.
Case-insensitive substring match on the branded provider the matched provider key carries in its telemetry tags.
Exact match on either telemetry label the request was attributed
to — the provider key's own label (pk_label) or the BYO
credential's (byo_label). One value searches both.
Case-insensitive substring search across the row's text columns.
At most 256 bytes — the fragment runs as one ILIKE per
searchable column, so its length multiplies the per-row cost.
The bound is on the encoded length, so non-ASCII text reaches it
sooner than its character count suggests.
Page size. Defaults to 100; values above 500 are clamped to 500.
Named limit rather than page_size because this feed shipped
before the resource surfaces settled on that name.
1 <= value1-based page number, applied over limit.
1 <= value <= 10000000Response Body
application/json
application/json
application/json
application/json
application/json
curl -X GET "$AISIX_CP/environments/$ENV_ID/usage_events?from=2026-08-01T00%3A00%3A00Z&to=2026-08-08T00%3A00%3A00Z&status=429" \ -H "Authorization: Bearer $AISIX_TOKEN"OK
{
"data": [
{
"id": 0,
"request_id": "string",
"env_id": "3f917d0d-4dff-42f3-97c4-6a5455dfe8c2",
"occurred_at": "2019-08-24T14:15:22Z",
"received_at": "2019-08-24T14:15:22Z",
"model_id": "string",
"api_key_id": "string",
"user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
"requested_model": "string",
"prompt_tokens": 0,
"completion_tokens": 0,
"cached_prompt_tokens": 0,
"reasoning_tokens": 0,
"cache_creation_tokens": 0,
"cache_read_tokens": 0,
"cache_hit_saved_input_tokens": 0,
"cache_hit_saved_output_tokens": 0,
"usage_estimated": true,
"audio_duration_seconds": 0.1,
"cost_usd": 0.1,
"status_code": 0,
"upstream_latency_ms": 0,
"latency_ms": 0,
"downstream_latency_ms": 0,
"ttft_ms": 0,
"upstream_ttft_ms": 0,
"attempt_index": 0,
"attempt_kind": "string",
"attempt_model": "string",
"error_class": "string",
"error_message": "string",
"finish_reason": "string",
"provider_request_id": "string",
"provider_model_version": "string",
"trace_id": "string",
"cache_status": "string",
"inbound_protocol": "string",
"operation": "chat",
"guardrail_blocked": true,
"guardrail_monitored": true,
"guardrail_rewritten": true,
"guardrail_bypassed_reason": "string",
"applied_guardrails": [
{
"kind": "string",
"hook": "string"
}
],
"guardrail_monitor_hits": [
{
"guardrail_name": "string",
"hook": "string",
"action": "string",
"reason": "string",
"counts": {}
}
],
"guardrail_enforced_hits": [
{
"guardrail_name": "string",
"hook": "string",
"action": "masked",
"error_type": "string",
"counts": {},
"duration_us": 0
}
],
"redacted_entity_counts": {
"property1": 0,
"property2": 0
},
"mcp_server_name": "string",
"mcp_tool_name": "string",
"a2a_agent_name": "string",
"a2a_method": "string",
"a2a_operation": "string",
"a2a_protocol_version": "string",
"a2a_task_id": "string",
"a2a_context_id": "string",
"a2a_task_state": "string",
"a2a_stream_event_count": 0,
"passthrough_route_name": "string",
"client_identity": "string",
"jwt_subject": "string",
"jwt_provider": "string",
"jwt_claim_mapping": "string",
"provider_kind": "string",
"provider_featured": true,
"branded_provider": "string",
"pk_label": "string",
"byo_label": "string",
"client_source_ip": "string",
"client_user_agent": "string"
}
],
"total": 0,
"page": 0,
"page_size": 0
}Request body or parameters failed validation.
{
"error": {
"code": "string",
"message": "string"
}
}Missing or invalid bearer.
{
"error": {
"code": "UNAUTHENTICATED",
"message": "no org context"
}
}Resource not found.
{
"error": {
"code": "NOT_FOUND",
"message": "provider key not found"
}
}The deployment is not configured to serve this data.
{
"error": {
"code": "DPMGR_UNAVAILABLE",
"message": "dp-manager node store not configured on this cp-api"
}
}Complete operation details and schema variants
GET /environments/{env_id}/usage_events
List Usage Events
Page through the environment's request telemetry, newest first.
One row is one upstream attempt, not one request: a request
that retried or failed over emits several rows sharing a
request_id, ordered by attempt_index. Aggregate by
request_id when you need per-request figures, or read
usage_metrics, which counts distinct requests server-side.
from and to are required — this feed is always read over an
explicit window. Rows arrive asynchronously from the gateway, so
a window reaching up to "now" keeps filling in for a short time
after the requests it covers have completed.
Requires a deployment with the usage store configured; without
one the endpoint answers 503.
Parameters
-
env_id(path, string, required): Environment ID. -
from(query, string, required): Inclusive lower bound onoccurred_at, RFC 3339. -
to(query, string, required): Exclusive upper bound onoccurred_at, RFC 3339. Must be strictly afterfrom. -
model_id(query, string, optional): Restrict to requests dispatched to this model. -
api_key_id(query, string, optional): Restrict to requests authenticated by this caller API key. -
user_id(query, string, optional): Restrict to requests whose authenticating key belonged to this organization member, given as the membershipidfromGET /members. Matches theuser_idsnapshotted on the row, so a member since removed from the organization still filters correctly. -
status(query, string, optional): Restrict by HTTP status code. Three accepted spellings: a family (2xx,3xx,4xx,5xx), one exact code (429), or an inclusive range (500-599). Bounds outside 100-599 are rejected. -
guardrail_blocked(query, boolean, optional): Restrict to requests an enforcing guardrail refused. -
guardrail_monitored(query, boolean, optional): Restrict to requests at least onemonitor-mode guardrail hit without acting on. -
guardrail_rewritten(query, boolean, optional): Restrict to requests an enforcing guardrail rewrote. Refusals are reported byguardrail_blockedinstead. -
traffic(query, string, optional): Restrict to one traffic kind. Gateway traffic (mcp,a2a) carries server/tool and agent/method attribution in place of the model and token columns that LLM traffic has. -
request_id(query, string, optional): Exact match on the request id. Free-form rather than a UUID: the id an operator has in hand is often the caller's ownx-aisix-request-id, not a gateway-minted UUID. -
requested_model(query, string, optional): Case-insensitive substring match on the model alias the caller addressed, which for a routed request is the Model-Group name rather than the dispatched target. Being a substring match, a short alias also matches longer ones that contain it. -
operation(query, string, optional): Exact match on the kind of work the request asked for —chat,messages,count_tokens,responses,completions,embeddings,rerank,image_generation,image_edit,transcription,translation,speech,video_generation,realtime,files,batches,fine_tuning,batch_completion,mcp,a2aorpassthrough.
Matching is exact rather than substring, so this selects a kind of
call rather than a text fragment: image selects nothing, while
image_generation selects image generations. It cuts across
traffic rather than within it — mcp, a2a and passthrough
are operations too.
Deliberately not a closed enum, like a2a_operation beside it: a
gateway newer than this document records operations this list does
not name yet, and rejecting them would make the newest traffic the
only traffic an operator cannot filter for. An unknown value simply
matches nothing.
-
mcp_server(query, string, optional): Exact match on the MCP server name the request reached. -
mcp_tool(query, string, optional): Case-insensitive substring match on the MCP tool name the request invoked.mcp_servermatches exactly, so pair the two to scope a tool search to one server. -
a2a_agent(query, string, optional): Exact match on the A2A agent name the request reached. -
a2a_method(query, string, optional): Case-insensitive substring match on the JSON-RPC method as the caller wrote it. Usea2a_operation, which matches exactly, to select every spelling of one operation. -
a2a_operation(query, string, optional): Exact match on the canonical A2A operation, which folds together the wire spellings that name it. -
a2a_task_id(query, string, optional): Exact match on the A2A task id the request produced or advanced. -
a2a_context_id(query, string, optional): Exact match on the A2A context id the request belongs to. -
a2a_task_state(query, string, optional): Exact match on the last task state the agent reported. -
jwt_subject(query, string, optional): Case-insensitive substring match on the verified external identity a JWT-authenticated request ran as. -
jwt_claim_mapping(query, string, optional): Case-insensitive substring match on the claim mapping that selected the caller API key. Requests whose key binds the token subject directly carry no mapping and never match. -
branded_provider(query, string, optional): Case-insensitive substring match on the branded provider the matched provider key carries in its telemetry tags. -
provider_label(query, string, optional): Exact match on either telemetry label the request was attributed to — the provider key's own label (pk_label) or the BYO credential's (byo_label). One value searches both. -
q(query, string, optional): Case-insensitive substring search across the row's text columns. At most 256 bytes — the fragment runs as oneILIKEper searchable column, so its length multiplies the per-row cost. The bound is on the encoded length, so non-ASCII text reaches it sooner than its character count suggests. -
limit(query, integer, optional): Page size. Defaults to 100; values above 500 are clamped to 500. Namedlimitrather thanpage_sizebecause this feed shipped before the resource surfaces settled on that name. -
page(query, integer, optional): 1-based page number, applied overlimit.
Responses
-
200: OK -
data(array, required): -
data[].id(integer, required): Row id, monotonic within the environment. -
data[].request_id(string, required): Identifier shared by every attempt of one request. Group on this for per-request figures. -
data[].env_id(string, required): Environment the request was served in. -
data[].occurred_at(string, required): When the gateway completed this attempt. -
data[].received_at(string, required): When the control plane stored the row. Later thanoccurred_atby the telemetry delivery delay. -
data[].model_id(string, required): Model the attempt dispatched to, or an empty string when the request failed before dispatch. -
data[].api_key_id(string, required): Caller API key that authenticated the request, or an empty string when none did. -
data[].user_id(string, optional): Organization member the authenticating key belonged to when the request ran — the membershipidfromGET /members, matching the key's ownuser_id. Snapshotted on the row, so it still resolves after the member leaves the organization. -
data[].requested_model(string, optional): Model alias the caller addressed. For routed traffic this is the Model-Group name, whilemodel_idis the target it was dispatched to. -
data[].prompt_tokens(integer, required): Input tokens.nullmeans the upstream reported no count for this request — distinct from a real0. -
data[].completion_tokens(integer, required): Output tokens.nullmeans the upstream reported no count — distinct from a real0. -
data[].cached_prompt_tokens(integer, required): Input tokens the upstream served from its own cache. -
data[].reasoning_tokens(integer, required): Tokens spent on reasoning, where the model reports them separately. -
data[].cache_creation_tokens(integer, required): Tokens written into an upstream prompt cache. -
data[].cache_read_tokens(integer, required): Tokens read from an upstream prompt cache. -
data[].cache_hit_saved_input_tokens(integer, required): Input tokens not sent upstream because the gateway cache answered. -
data[].cache_hit_saved_output_tokens(integer, required): Output tokens not generated because the gateway cache answered. -
data[].usage_estimated(boolean, required): The token counts were computed by the gateway's local tokenizer because the upstream reported none, so treat them as an estimate. -
data[].audio_duration_seconds(number, optional): Audio length billed for models priced by duration rather than tokens. Omitted on non-audio traffic;cost_usdis derived from it where present. -
data[].cost_usd(number, required): Cost of this attempt, computed by the control plane from the prices in effect when the row arrived. Not recomputed when prices change later. -
data[].status_code(integer, required): HTTP status the gateway returned for this attempt. -
data[].upstream_latency_ms(integer, required): Time this attempt spent on the upstream. Attempt-scoped, so it excludes request parsing, guardrail scans and retry backoff. -
data[].latency_ms(integer, required): Historical spelling ofupstream_latency_ms, carried unchanged for existing consumers. -
data[].downstream_latency_ms(integer, optional): What the caller waited for: the complete response written, or the first token forwarded when streaming. Request-scoped, so it is present on exactly one attempt per request, and absent on rows from gateways predating it. -
data[].ttft_ms(integer, optional): Request-scoped time to first token, as reported by gateways before 0.7. Newer rows carryupstream_ttft_msinstead — the two measure different spans, so a row has one or the other. -
data[].upstream_ttft_ms(integer, optional): Attempt-scoped time to first token. -
data[].attempt_index(integer, required): Position of this attempt within its request, starting at 0. -
data[].attempt_kind(string, optional): Why this attempt ran, e.g.initial,retryorfallback. -
data[].attempt_model(string, optional): Target this attempt was dispatched to. Empty for direct models, where there is no separate target. -
data[].error_class(string, optional): Bounded classification of a failed attempt. -
data[].error_message(string, optional): Upstream or gateway error text for a failed attempt. -
data[].finish_reason(string, required): Reason the model stopped generating, as the upstream reported it. -
data[].provider_request_id(string, required): Upstream provider's own request identifier, where it returns one. -
data[].provider_model_version(string, required): Model version the upstream reported serving. -
data[].trace_id(string, required): 32 lowercase hex characters identifying the trace this request's spans were exported under. Empty when the gateway exported no traces. -
data[].cache_status(string, optional): Gateway response-cache decision:hit,missordisabled. Absent on the error path and on rows from gateways predating the field. -
data[].inbound_protocol(string, optional): Client-facing protocol the request arrived on, e.g.openaioranthropic. -
data[].operation(string, optional): Kind of work the request asked for, from a fixed set derived from the endpoint it matched.inbound_protocolreports the same value for every OpenAI-compatible endpoint, so it cannot tell a chat completion from an image generation or a video submission; this can.
It describes the request rather than its outcome, so a failed or guardrail-refused row carries it too — and on such a row no other field names the endpoint. A request that retries or fails over repeats the same value on each attempt.
Absent on rows written by gateways predating the field.
-
data[].guardrail_blocked(boolean, required): An enforcing guardrail refused the request. -
data[].guardrail_monitored(boolean, required): At least onemonitor-mode guardrail matched without acting.guardrail_monitor_hitscarries the detail. -
data[].guardrail_rewritten(boolean, required): An enforcing guardrail rewrote content and the request continued. Refusals setguardrail_blockedinstead, so the two flags answer different questions. -
data[].guardrail_bypassed_reason(string, optional): Why guardrail evaluation was skipped for this request. -
data[].applied_guardrails(array, optional): The guardrails that governed this request, passed through from the gateway. Omitted when none did. -
data[].applied_guardrails[].kind(string, optional): -
data[].applied_guardrails[].hook(string, optional): -
data[].guardrail_monitor_hits(array, optional): Whatmonitor-mode guardrails would have done. Names and counts only — never matched content. -
data[].guardrail_monitor_hits[].guardrail_name(string, optional): -
data[].guardrail_monitor_hits[].hook(string, optional): -
data[].guardrail_monitor_hits[].action(string, optional): -
data[].guardrail_monitor_hits[].reason(string, optional): -
data[].guardrail_monitor_hits[].counts(object, optional): -
data[].guardrail_enforced_hits(array, optional): What enforcing guardrails actually did.actionismasked(content rewritten, request continued),blocked(the content policy refused it) orblocked_unavailable(the guardrail could not evaluate the request and its configuration refuses what it cannot check).
Read as an audit trail, blocked_unavailable is not a policy
violation — it is a refusal the policy never decided, so
counting it as one turns a provider outage into a burst of
apparent violations. error_type carries the bounded cause
and appears on that action only.
-
data[].guardrail_enforced_hits[].guardrail_name(string, optional): -
data[].guardrail_enforced_hits[].hook(string, optional): -
data[].guardrail_enforced_hits[].action(string, optional): -
data[].guardrail_enforced_hits[].error_type(string, optional): -
data[].guardrail_enforced_hits[].counts(object, optional): -
data[].guardrail_enforced_hits[].duration_us(integer, optional): -
data[].redacted_entity_counts(object, optional): Per-detector count of spans a masking guardrail rewrote, input and output merged, e.g.{"email": 2}. Detector names only, never matched values. -
data[].mcp_server_name(string, optional): MCP server the request reached. Absent on non-MCP traffic. -
data[].mcp_tool_name(string, optional): MCP tool the request invoked. -
data[].a2a_agent_name(string, optional): A2A agent the request reached. Absent on non-A2A traffic. -
data[].a2a_method(string, optional): JSON-RPC method as the caller wrote it. -
data[].a2a_operation(string, optional): Canonical operation the method names. Group on this rather thana2a_method, which spells one operation several ways. -
data[].a2a_protocol_version(string, optional): A2A protocol version the caller negotiated. -
data[].a2a_task_id(string, optional): Task this request produced or advanced. -
data[].a2a_context_id(string, optional): Conversation the task belongs to. -
data[].a2a_task_state(string, optional): Last state the agent reported for the task. -
data[].a2a_stream_event_count(integer, optional): Events forwarded on a streaming A2A response. -
data[].passthrough_route_name(string, optional): Passthrough route that served the request. Absent on non-passthrough traffic. -
data[].client_identity(string, optional): End-user identity the passthrough route's identity header carried. -
data[].jwt_subject(string, optional): Verified external identity a JWT-authenticated request ran as. Absent on API-key traffic. -
data[].jwt_provider(string, optional): OIDC provider that issued the verified token. -
data[].jwt_claim_mapping(string, optional): Claim mapping that selected the caller API key. Absent when the key bound the token subject directly. -
data[].provider_kind(string, optional): Provider-key telemetry tag describing the upstream kind. -
data[].provider_featured(boolean, optional): Provider-key telemetry tag marking a featured provider. -
data[].branded_provider(string, optional): Provider-key telemetry tag naming the branded provider. -
data[].pk_label(string, optional): Provider-key telemetry label the request was attributed to. -
data[].byo_label(string, optional): Telemetry label of the BYO credential the request used. -
data[].client_source_ip(string, optional): Source address the gateway saw for the caller. -
data[].client_user_agent(string, optional): User agent the caller sent. -
total(integer, required): Attempts matching the filters across all pages. -
page(integer, required): 1-based page number this response covers. -
page_size(integer, required): Page size actually applied, after clamping. -
400: Request body or parameters failed validation. -
error(object, required): Error details. -
error.code(string, required): Machine-readable identifier. Examples:UNAUTHENTICATED,INVALID_REQUEST,MASTER_KEY_UNSET,INTERNAL. -
error.message(string, required): Human-readable explanation; safe to surface to operators. -
401: Missing or invalid bearer. Response fields (application/json): identical toGET /environments/{env_id}/usage_events, response 400 above. -
404: Resource not found. Response fields (application/json): identical toGET /environments/{env_id}/usage_events, response 400 above. -
503: The deployment is not configured to serve this data. Response fields (application/json): identical toGET /environments/{env_id}/usage_events, response 400 above.
cURL
curl -X GET '$AISIX_CP/environments/{env_id}/usage_events' -H 'Authorization: Bearer $AISIX_TOKEN'