Monitor AI Traffic and Track LLM Costs
Gain visibility into LLM usage, token consumption, latency, and costs with API7 Gateway's observability features.
This guide explains how to observe AI traffic and estimate LLM costs using built-in AI logging, APISIX context variables, and standard observability integrations.
Overview
AI observability is different from traditional API observability. For LLM workloads, you need token-level visibility, model attribution, and latency breakdowns such as time to first token.
With API7 Gateway, you can collect:
- Request and response model metadata.
- Prompt and completion token counts.
- End-to-end and upstream timing signals.
- Optional payload-level logs for request/response content.
The gateway does not calculate billing directly. Cost tracking is derived by mapping token usage to provider pricing.
Prerequisites
-
Install Docker.
-
Install cURL to send requests to the services for validation.
-
Have a running API7 Gateway instance.
-
Create a token from the Dashboard and save it to an environment variable:
export API_KEY=your-dashboard-token # replace with your Dashboard token -
Replace
{gateway_group_id}with your gateway group ID. Usedefaultif you are following the quickstart. -
If you are following the Admin API examples, create or reuse a service in API7 Gateway. If you do not have one yet, follow Create or Reuse a Service, then save its ID to an environment variable:
export SERVICE_ID=your-service-id # replace with your service ID
Built-in AI Logging
ai-proxy and ai-proxy-multi support a logging configuration with:
logging.summaries(boolean): logsrequest_model,model,duration,prompt_tokens,completion_tokens, andupstream_response_time.logging.payloads(boolean): logs request messages, stream flag, and response text content.
Enable logging at route scope (or in shared plugin policy where applicable):
curl -k "https://localhost:7443/apisix/admin/routes?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
--data-binary @- <<EOF
{
"id": "ai-observability",
"service_id": "$SERVICE_ID",
"paths": ["/ai"],
"plugins": {
"ai-proxy": {
"provider": "openai",
"auth": { "header": { "Authorization": "Bearer $OPENAI_API_KEY" } },
"options": { "model": "gpt-4o" },
"logging": {
"summaries": true,
"payloads": false
}
}
}
}
EOF❶ Enable AI logging on the ai-proxy plugin.
❷ Log summary-level fields (model + token/timing metadata) for cost and performance analysis.
❸ Keep payload logging disabled by default to reduce sensitive content exposure.
services:
- name: AI Observability
routes:
- uris:
- /ai
name: ai-observability
plugins:
ai-proxy:
provider: openai
auth:
header:
Authorization: "Bearer ${OPENAI_API_KEY}"
options:
model: gpt-4o
logging:
summaries: true
payloads: false❶ Enable AI logging on the ai-proxy plugin.
❷ Log summary-level fields (model + token/timing metadata) for cost and performance analysis.
❸ Keep payload logging disabled by default to reduce sensitive content exposure.
Synchronize the configuration to API7 Gateway:
adc sync -f adc.yamlFor multi-model routes, apply the same logging fields in each ai-proxy-multi instance:
curl -k "https://localhost:7443/apisix/admin/routes?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
--data-binary @- <<EOF
{
"id": "ai-observability-multi",
"service_id": "$SERVICE_ID",
"paths": ["/ai-multi"],
"plugins": {
"ai-proxy-multi": {
"instances": [
{
"name": "openai-primary",
"provider": "openai",
"auth": { "header": { "Authorization": "Bearer $OPENAI_API_KEY" } },
"options": { "model": "gpt-4o-mini" },
"logging": {
"summaries": true,
"payloads": false
},
"weight": 1
}
]
}
}
}
EOF❶ Configure logging.summaries and logging.payloads per instance on ai-proxy-multi.
services:
- name: AI Observability Multi
routes:
- uris:
- /ai-multi
name: ai-observability-multi
plugins:
ai-proxy-multi:
instances:
- name: openai-primary
provider: openai
auth:
header:
Authorization: "Bearer ${OPENAI_API_KEY}"
options:
model: gpt-4o-mini
logging:
summaries: true
payloads: false
weight: 1❶ Configure logging.summaries and logging.payloads per instance on ai-proxy-multi.
Synchronize the configuration to API7 Gateway:
adc sync -f adc.yamlFor the full configuration reference, see ai-proxy (opens in Plugin Hub docs) and ai-proxy-multi (opens in Plugin Hub docs).
APISIX Variables for AI Traffic
The most useful variables for cost tracking are below. They can be used in access log formats, in external logging plugins, and anywhere else a variable is accepted:
| Variable | Description |
|---|---|
llm_prompt_tokens | Prompt token count reported by the provider. |
llm_completion_tokens | Completion token count reported by the provider. |
llm_cache_read_input_tokens | Prompt tokens the provider served from its cache. Usually billed at a lower rate. |
llm_cache_creation_input_tokens | Prompt tokens the provider wrote to its cache. |
llm_reasoning_tokens | Tokens spent on reasoning, for models that report them separately. |
llm_model | The model the gateway sent upstream. |
request_llm_model | The model the client asked for. |
llm_time_to_first_token | Time to first token, in milliseconds. |
apisix_upstream_response_time | Total time the provider took, in milliseconds. |
For the full set of 14, including the streaming and tool-call variables, see Built-In Variables.
llm_response_text is not a variable. It is a field the logging plugins add to the JSON log entry when logging.payloads is enabled, so $llm_response_text in an access log format resolves to nothing. Read it from the structured log entry instead — and remember that enabling payload logging writes model output, which usually contains whatever the user put in.
Prometheus Integration
Use the standard prometheus (opens in Plugin Hub docs) plugin to export gateway metrics. For AI traffic, the built-in metrics are:
| Metric | Type | What it holds |
|---|---|---|
apisix_llm_latency | histogram | LLM request latency in milliseconds. The type label separates total (whole response, both chat and streaming) from ttft (time to first token, streaming only). |
apisix_llm_prompt_tokens | counter | Cumulative prompt tokens. |
apisix_llm_completion_tokens | counter | Cumulative completion tokens. |
apisix_llm_prompt_tokens_dist | histogram | Prompt tokens per request, so you can see the distribution rather than just the total. |
apisix_llm_completion_tokens_dist | histogram | Completion tokens per request. |
apisix_llm_active_connections | gauge | Connections currently open to the provider. |
apisix_ai_cache_hits_total | counter | Responses served from the AI cache, with a layer label for which cache layer answered. |
apisix_ai_cache_misses_total | counter | Requests that missed the AI cache. |
apisix_ai_cache_bypasses_total | counter | Requests that skipped the cache entirely. |
apisix_ai_cache_embedding_latency | histogram | Time spent generating the embedding used for a semantic cache lookup. |
Every one of these carries route, route_id, service, service_id, consumer, node, gateway_group_id, instance_id, portal_id, api_product_id, request_type, request_llm_model and llm_model, and all but apisix_llm_latency also carry matched_uri and matched_host.
There is no provider label. Group by llm_model for what the gateway actually sent, or by request_llm_model for what clients asked for — the two differ whenever a route pins a model with options.model.
If you have set plugin_attr.prometheus.allow_degradation: true, these metrics stop being recorded while the gateway is shedding load, so a gap in a cost report is not necessarily a gap in traffic.
Example PromQL queries:
# Current active LLM connections across all routes
sum(apisix_llm_active_connections)# Top routes by active LLM connections
topk(10, sum by (route) (apisix_llm_active_connections))# Alert condition example: sustained high active connections
avg_over_time(sum(apisix_llm_active_connections)[5m]) > 200# Prompt tokens per minute by model — the shape of a cost report
sum by (llm_model) (rate(apisix_llm_prompt_tokens[1m])) * 60# Cache hit ratio
sum(rate(apisix_ai_cache_hits_total[5m]))
/ (sum(rate(apisix_ai_cache_hits_total[5m])) + sum(rate(apisix_ai_cache_misses_total[5m])))# 95th percentile time to first token, by model
histogram_quantile(0.95,
sum by (le, llm_model) (rate(apisix_llm_latency_bucket{type="ttft"}[5m])))Adjust labels and thresholds to your deployment and traffic profile.
Logging AI Requests to External Systems
You can forward structured logs with AI metadata to systems such as ELK, Loki, or Splunk by combining APISIX logging plugins with AI context variables.
Example structured log payload:
{
"route_id": "27bcdea2-7586-47ad-9262-a3adf4b6699e",
"service_id": "d8402098-2f80-4e08-afab-21b9ebc62090",
"model": "deepseek-chat",
"request_model": "",
"prompt_tokens": 11,
"completion_tokens": 16,
"duration_ms": 3991,
"ttft_ms": 2159,
"upstream_response_time": 1133
}route_idandservice_idare the gateway-assigned UUIDs for the route and service.request_modelis empty when the client does not specify a model explicitly; the model is then determined by the plugin configuration.duration_msis not a built-in AI log field — use$latency(total request time) and$upstream_latency(upstream response time) in your log format configuration to measure request duration.
Use summary logging for default production telemetry. Enable payload logging only when you need short-term debugging with appropriate data handling controls.
Building a Cost Dashboard
API7 Gateway exposes token usage signals, while cost is calculated externally:
estimated_cost = (prompt_tokens × prompt_price_per_token) + (completion_tokens × completion_price_per_token)
Typical dashboard panels in Grafana:
- Cost by consumer (hourly/daily).
- Cost by model.
- Prompt vs. completion token split.
- Spend trend and budget threshold alerts.
Implementation approach:
- Export AI logs/metrics.
- Enrich records with provider pricing metadata.
- Compute estimated cost in your log or metrics pipeline.
- Visualize and alert in Grafana.
For token governance controls, see Token-Based Rate Limiting and Quota Management.
OpenTelemetry Tracing
Use the standard opentelemetry (opens in Plugin Hub docs) plugin to trace AI request lifecycle latency across:
- Client request ingress.
- Gateway processing.
- Upstream LLM call.
- Response egress.
This helps isolate whether latency is caused by client-side behavior, gateway plugins, or upstream model/provider response time.
Next Steps
- Token-Based Rate Limiting and Quota Management — Enforce token budgets per route, consumer, and model.
- Multi-LLM Routing and Fallback — Improve reliability and optimize provider/model usage.
- For plugin details, see
ai-proxy(opens in Plugin Hub docs),ai-proxy-multi(opens in Plugin Hub docs),prometheus(opens in Plugin Hub docs), andopentelemetry(opens in Plugin Hub docs).