Export Usage Events
GET /environments/{env_id}/usage_events/export
GET /environments/{env_id}/usage_events/exportInteractive 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
File format. csv (the default) carries a fixed, scan-ordered
column set; json carries every field the feed returns.
"csv"Value in
- "csv"
- "json"
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.
Response Body
application/json
application/json
application/json
application/json
curl -X GET "$AISIX_CP/environments/$ENV_ID/usage_events/export?from=2026-08-01T00%3A00%3A00Z&to=2026-08-08T00%3A00%3A00Z&status=429" \ -H "Authorization: Bearer $AISIX_TOKEN"The export file. Streamed, so the body may end early if the connection drops mid-transfer.
{
"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",
"model_name": "string",
"api_key_name": "string",
"user_name": "string"
}
],
"total": 0,
"exported": 0,
"truncated": true
}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/export
Export Usage Events
Download the rows listUsageEvents would return for the same
filters, as one file. Takes the identical filter set; limit and
page are ignored — an export is the whole match, capped at
50,000 rows.
Each row carries the model, caller API key and member names alongside their ids, because a file of bare UUIDs is not usable away from the dashboard that would resolve them.
Three response headers describe the file before you read it:
X-Export-Total (rows matched), X-Export-Row-Limit (the cap)
and X-Export-Truncated (true when the cap cut it short —
narrow the window rather than reading a prefix). They are written
before the body streams and cannot be revised, so the json
format repeats the same figures in a trailer that also reports
how many rows were actually written.
CSV cells beginning =, +, @, a tab or a carriage return —
and - where the cell is not a number — are prefixed with an
apostrophe so a spreadsheet cannot evaluate caller-controlled
text as a formula, and the body carries a UTF-8 BOM so non-ASCII
text opens correctly.
A failure after the first byte cannot change the status line: the
file simply ends early. Check truncated/exported in the JSON
trailer, or the row count against X-Export-Total, before
treating a file as complete.
Parameters
-
env_id(path, string, required): Environment ID. -
format(query, string, optional): File format.csv(the default) carries a fixed, scan-ordered column set;jsoncarries every field the feed returns. -
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.
Responses
-
200: The export file. Streamed, so the body may end early if the connection drops mid-transfer. -
data(array, required): -
allOf variant 1
-
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. -
allOf variant 2
-
data[].model_name(string, optional): Display name of the dispatched model. -
data[].api_key_name(string, optional): Display name of the caller API key. -
data[].user_name(string, optional): Display name of the organization member on the row. -
total(integer, required): Rows matching the filters when the export started. -
exported(integer, required): Rows this file actually contains. -
truncated(boolean, required): The row cap cut the export short. -
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/export, response 400 above. -
404: Resource not found. Response fields (application/json): identical toGET /environments/{env_id}/usage_events/export, response 400 above. -
503: The deployment is not configured to serve this data. Response fields (application/json): identical toGET /environments/{env_id}/usage_events/export, response 400 above.
cURL
curl -X GET '$AISIX_CP/environments/{env_id}/usage_events/export' -H 'Authorization: Bearer $AISIX_TOKEN'