Request logs
Package: agentrouter.insights.v1 Service: RequestLogsService
Endpoints
Query request logs
Changed in v0.5.0 (deprecations): The microdollar cost fields are deprecated. They have always returned 0, because nothing ever wrote them, and the decimal string fields carry the real amount at full precision. Read total_cost for one request, or cost for a stats group. The fields stay in the API and keep returning 0.
Changed in v0.2.0 (new features): Request-log statistics can now separate spend the organization was actually billed for from cost that is only estimated. Group or filter by usage_mode (managed, byok, passthrough) to keep BYOK and passthrough traffic out of a spend total. Group or filter by usage_source (actual, partial, estimated) to see how much of a figure rests on locally estimated token counts rather than counts the provider reported. Both work on the admin and customer stats endpoints and can be combined with an existing dimension, for example per-model billed spend. usage_mode is available for historical requests; usage_source is recorded from this release onward and is empty for earlier requests, for requests served by a data plane that has not been upgraded, and for rejected or budget-blocked requests. Individual request logs now carry usage_source too, which answers whether a request's token counts were measured or estimated, a question the usage_measured flag cannot answer.
Changed in v0.1.5 (bug fixes): A management API key that holds the request_logs_reader scope can now read request logs and request-log stats. Every scoped key was previously refused on these endpoints with "scoped api key cannot call an operation without a tenant boundary", so the scope granted nothing. Results are confined to the key's own organization or project, and the three read endpoints accept an optional customer_id filter. Admin credentials and console sessions are unchanged.
What it does: Retrieves request logs with filtering and pagination. Requires request_logs_reader (admin implies it).
Request fields:
| Field | Required | Description |
|---|---|---|
user_id | no | Filter by user ID |
api_key_id | no | Filter by API key ID. Superseded by api_key_ids when non-empty. |
api_key_ids | no | Filter by any of these API key IDs (UUIDs). |
endpoint | no | Filter by request endpoint (e.g. "/v1/chat/completions"). |
model_name | no | Filter by model name (e.g., "claude-3-opus", "gpt-4") |
status | no | Filter by status (e.g., "success", "error", "timeout") |
status_code | no | Filter by HTTP status code |
start_time | no | Start of time range (inclusive) |
end_time | no | End of time range (exclusive) |
min_duration_ms | no | Minimum duration in milliseconds |
max_duration_ms | no | Maximum duration in milliseconds |
page_size | no | Page size (default: 50, max: 1000) |
page_token | no | Page token for pagination |
order_by | no | Order by field (default: "timestamp") Valid values: timestamp, duration_ms, input_tokens, output_tokens, total_tokens, total_cost, status_code, model_name, endpoint, api_key_name |
order_direction | no | Order direction: "asc" or "desc" (default: "desc") |
search | no | Case-insensitive literal substring match over request_id. |
dataplane_id | no | Filter by the data plane that served the request (the workspace id the reporting dataplane's telemetry is authenticated as) -- see RequestLog.dataplane_id. |
customer_id | no | Tenant boundary / filter. Restricts results to logs recorded by API keys of this customer. A scoped (non-admin) management key is always confined to its own customer whether or not this is set; admin and session callers query all customers when empty (fraser#6341). |
Response fields:
| Field | Required | Description |
|---|---|---|
logs | no | List of request logs matching the query |
next_page_token | no | Token for retrieving the next page (empty if no more pages) |
total_count | no | Total count of matching logs (may be approximate for large datasets) |
{"signatures":{"go":"func (x *RequestLogsClient) QueryRequestLogs(ctx context.Context, req *insightsv1.QueryRequestLogsRequest) (*insightsv1.QueryRequestLogsResponse, error)","python":"query_request_logs(req: request_logs_pb2.QueryRequestLogsRequest) -\u003e QueryRequestLogsResponse","typescript":"queryRequestLogs(req: MessageInitShape\u003ctypeof tars_insights_v1_request_logs_pb.QueryRequestLogsRequestSchema\u003e): Promise\u003cQueryRequestLogsResponse\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/request-logs\" \\\n -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tinsightsv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/insights/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026insightsv1.QueryRequestLogsRequest{}\n\n\tresp, err := client.RequestLogs().QueryRequestLogs(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/requestlogs/queryrequestlogs\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.insights.v1 import request_logs_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = request_logs_pb2.QueryRequestLogsRequest()\ntry:\n result = client.request_logs.query_request_logs(req)\n print(result)\nexcept Exception as err:\n print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n# agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n baseUrl: process.env.AGENTROUTER_BASE_URL,\n apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n const result = await client.requestLogs.queryRequestLogs(req)\n console.log(result)\n} catch (err) {\n console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n \"name\": \"requestlogs\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"typescript\": \"^5.4.0\"\n }\n}\n"},{"name":"tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true\n }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/request-logs\" \\\n -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/request-logs","slug":"query-request-logs"}
Get request log
Changed in v0.5.0 (deprecations): The microdollar cost fields are deprecated. They have always returned 0, because nothing ever wrote them, and the decimal string fields carry the real amount at full precision. Read total_cost for one request, or cost for a stats group. The fields stay in the API and keep returning 0.
Changed in v0.2.0 (new features): Request-log statistics can now separate spend the organization was actually billed for from cost that is only estimated. Group or filter by usage_mode (managed, byok, passthrough) to keep BYOK and passthrough traffic out of a spend total. Group or filter by usage_source (actual, partial, estimated) to see how much of a figure rests on locally estimated token counts rather than counts the provider reported. Both work on the admin and customer stats endpoints and can be combined with an existing dimension, for example per-model billed spend. usage_mode is available for historical requests; usage_source is recorded from this release onward and is empty for earlier requests, for requests served by a data plane that has not been upgraded, and for rejected or budget-blocked requests. Individual request logs now carry usage_source too, which answers whether a request's token counts were measured or estimated, a question the usage_measured flag cannot answer.
Changed in v0.1.5 (bug fixes): Fetching a request log by ID over REST now works when the ID contains a colon (for example default:<uuid>). Such a request previously returned an empty 200 response, because the route treated the text after the colon as a custom verb. Both the raw and the percent-encoded forms of the ID now resolve.
Changed in v0.1.5 (bug fixes): A management API key that holds the request_logs_reader scope can now read request logs and request-log stats. Every scoped key was previously refused on these endpoints with "scoped api key cannot call an operation without a tenant boundary", so the scope granted nothing. Results are confined to the key's own organization or project, and the three read endpoints accept an optional customer_id filter. Admin credentials and console sessions are unchanged.
What it does: Retrieves a single request log by request ID. Requires request_logs_reader (admin implies it).
Request fields:
| Field | Required | Description |
|---|---|---|
request_id | no | Request identifier |
customer_id | no | Tenant boundary. A scoped (non-admin) management key is confined to its own customer whether or not this is set: a log recorded outside that customer is NotFound. Admin and session callers ignore it when empty (fraser#6341). |
Response fields:
| Field | Required | Description |
|---|---|---|
id | output-only | Unique log identifier |
timestamp | no | Timestamp when the request was received |
user_id | no | User ID who made the request |
api_key_id | no | API key information UUID of the API key that authenticated the request. |
api_key_prefix | no | Leading visible characters of the key (e.g. "sk-..."), for display. |
api_key_hash | no | Hash of the API key used to look it up without storing the secret. |
api_key_address | no | On-chain/account address the API key is bound to. |
api_key_name | no | Human-readable label assigned to the API key. |
model_name | no | Model name used for the request |
input_tokens | no | Token usage. A zero here is meaningful only when usage_measured is true; when it is false the request never recorded usage (e.g. early failure) and consumers should render these as blank rather than 0. Number of prompt/input tokens consumed by the request. |
output_tokens | no | Number of completion/output tokens generated in the response. |
input_tokens_cost_microdollar | no | Cost fields. Every amount below is a decimal string, precision 18, scale 10. An amount is empty when the request never recorded usage. "0" is a real measured zero. total_cost is the field to read. It is the whole billed amount for this request: input + output + additional. Do not add the component fields together. input_tokens_cost already contains the two cache amounts, so a sum of them counts the same money twice. A *_cost field is the amount TARS billed, after the platform fee. The matching *_cost_before_fee field is the provider price, before that fee. The fee rate is a per-deployment setting. On byok and passthrough traffic the customer pays the provider directly, so the billed amount can be the fee alone. Deprecated: always 0. Use input_tokens_cost. |
input_tokens_cost | no | Input-token cost as a decimal string (precision: 18, scale: 10). Empty when usage was never recorded. |
input_tokens_cost_before_fee | no | Input-token cost before the platform fee/markup is applied. Decimal string. |
output_tokens_cost_microdollar | no | Deprecated: always 0. Use output_tokens_cost. |
output_tokens_cost | no | Output-token cost as a decimal string (precision: 18, scale: 10). Empty when usage was never recorded. |
output_tokens_cost_before_fee | no | Output-token cost before the platform fee/markup is applied. Decimal string. |
status | no | Request/Response status Logical outcome of the request (e.g. "success", "error", "timeout"). |
status_code | no | HTTP status code returned to the client. |
request_headers | no | Request metadata Captured request headers as a JSON object. |
request_body | no | Captured request payload as a JSON object. |
response_headers | no | Response metadata Captured response headers as a JSON object. |
response_body | no | Text response body captured from the upstream response. |
response_body_raw | no | Raw response body bytes (for binary payloads such as images). |
llm_parameters | no | LLM parameters used |
upstream_duration_ms | no | Timing information Duration of the upstream LLM call in milliseconds. |
duration_ms | no | Total end-to-end request duration in milliseconds. |
request_id | no | Request identifier |
created_at | no | Record creation timestamp |
storage_location | no | Object storage information (for Parquet-based storage) GCS path to the Parquet file when use_object_storage is true. |
storage_row_index | no | Row index within the Parquet file. |
storage_batch_id | no | Batch UUID grouping rows written in the same export batch. |
use_object_storage | no | True when the row is stored in object storage (GCS); false when in the DB. |
cached_input_tokens | no | Cache token breakdown. Meaningful only when cache_measured is true; when it is false the request did not record a cache breakdown and consumers should treat cache-hit as unknown rather than 0%. Cache presence is independent of usage_measured. Input tokens served from the prompt cache (cache reads), billed at the cached rate. |
cached_input_tokens_cost | no | Cached-input cost as a decimal string (precision: 18, scale: 10). Empty when cache was never recorded. Part of input_tokens_cost, not an addition to it. |
cached_input_tokens_cost_before_fee | no | Cached-input cost before the platform fee/markup is applied. Decimal string. |
cache_creation_input_tokens | no | Input tokens written to the prompt cache (cache-creation writes). |
cache_creation_input_tokens_cost | no | Cache-creation cost as a decimal string (precision: 18, scale: 10). Empty when cache was never recorded. Part of input_tokens_cost, not an addition to it. |
cache_creation_input_tokens_cost_before_fee | no | Cache-creation cost before the platform fee/markup is applied. Decimal string. |
endpoint | no | Request route (e.g. "/v1/chat/completions", "/v1/images/generations"). |
additional_cost | no | Request-level fee after token costs (decimal string, after fee). Empty only when token usage was not measured and no nonzero additional cost was recorded; "0" means a measured real zero fee. |
additional_cost_before_fee | no | Request-level fee before the platform fee/markup is applied. Empty only when token usage was not measured and no nonzero additional cost was recorded; "0" means a measured real zero fee. |
total_cost | no | Total billed cost after fee: input + output + additional. Empty only when token usage was not measured and no nonzero additional cost was recorded. |
model_requested | no | Model name the client requested (from request headers/body). |
model_effective | no | Model name that actually served the request (after routing/fallback). |
used_fallback_model | no | True when the gateway substituted a fallback model for the request. |
used_byok | no | True when the request used bring-your-own-key (customer-supplied provider key). |
used_passthrough | no | True when the request was forwarded in passthrough mode without rewriting. |
is_provider_fallback | no | True when the provider itself returned a fallback model. |
usage_measured | no | Presence signals for the nullable metric groups above. A real 0 is indistinguishable from "never recorded" on the integer fields, so these tell consumers whether those zero values are measured. True when the request recorded token usage (input/output tokens + costs). |
cache_measured | no | True when the request recorded a prompt-cache breakdown (cached and cache-creation tokens). Independent of usage_measured. |
dataplane_id | no | Identity of the DATA PLANE that served (and reported) this request: the workspace id the reporting dataplane's telemetry channel is authenticated as (project_gateways.workspace_id), stamped server-side at telemetry ingest from the MP-verified service-account binding -- not from request headers or reporter-supplied claims -- so attribution survives customer-managed DNS failover between a project's member gateways. NOTE: this is dataplane granularity, not per-gateway -- gateways co-located on one dataplane share a value (true per-gateway attribution requires the dataplane to export its per-request P10 gateway match and is tracked as a follow-up). Empty for rows recorded before dataplane attribution existed and for deployments that write logs locally without a reporting identity. |
usage_source | no | How this row's token counts were obtained: "actual" (the provider reported usage), "partial" (input real, output estimated), "estimated" (no usage at all, e.g. a cancelled stream). This is the field that answers "are these token counts real?", which usage_measured cannot: on an estimated row the estimator writes its guess into the usage fields, so usage_measured reports true wherever that guess is non-zero. (A zero estimate leaves the token fields unset and usage_measured false, so it is not a reliable inverse either.) Empty means no usage was resolved for this request. That covers rows predating the field and rows from a data plane not yet upgraded, and permanently covers rejected and budget-blocked requests. |
{"signatures":{"go":"func (x *RequestLogsClient) GetRequestLog(ctx context.Context, req *insightsv1.GetRequestLogRequest) (*insightsv1.RequestLog, error)","python":"get_request_log(req: request_logs_pb2.GetRequestLogRequest) -\u003e RequestLog","typescript":"getRequestLog(req: MessageInitShape\u003ctypeof tars_insights_v1_request_logs_pb.GetRequestLogRequestSchema\u003e): Promise\u003cRequestLog\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/request-logs/01H...\" \\\n -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tinsightsv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/insights/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026insightsv1.GetRequestLogRequest{}\n\n\tresp, err := client.RequestLogs().GetRequestLog(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/requestlogs/getrequestlog\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.insights.v1 import request_logs_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = request_logs_pb2.GetRequestLogRequest()\ntry:\n result = client.request_logs.get_request_log(req)\n print(result)\nexcept Exception as err:\n print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n# agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n baseUrl: process.env.AGENTROUTER_BASE_URL,\n apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n const result = await client.requestLogs.getRequestLog(req)\n console.log(result)\n} catch (err) {\n console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n \"name\": \"requestlogs\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"typescript\": \"^5.4.0\"\n }\n}\n"},{"name":"tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true\n }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/request-logs/01H...\" \\\n -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/request-logs/{request_id}","slug":"get-request-log"}
Get request log stats
Changed in v0.5.0 (bug fixes): Request log stats now report the full billed amount. The group cost left out per-image generation cost, so a group of image generation traffic could read 0.00 while those requests were billed. The response-level total cost was never populated and was always empty. Both now count every cost component, so figures for image-heavy workloads rise to the amount that was always charged.
Changed in v0.5.0 (deprecations): The microdollar cost fields are deprecated. They have always returned 0, because nothing ever wrote them, and the decimal string fields carry the real amount at full precision. Read total_cost for one request, or cost for a stats group. The fields stay in the API and keep returning 0.
Changed in v0.2.0 (new features): Request-log statistics can now separate spend the organization was actually billed for from cost that is only estimated. Group or filter by usage_mode (managed, byok, passthrough) to keep BYOK and passthrough traffic out of a spend total. Group or filter by usage_source (actual, partial, estimated) to see how much of a figure rests on locally estimated token counts rather than counts the provider reported. Both work on the admin and customer stats endpoints and can be combined with an existing dimension, for example per-model billed spend. usage_mode is available for historical requests; usage_source is recorded from this release onward and is empty for earlier requests, for requests served by a data plane that has not been upgraded, and for rejected or budget-blocked requests. Individual request logs now carry usage_source too, which answers whether a request's token counts were measured or estimated, a question the usage_measured flag cannot answer.
Changed in v0.1.5 (bug fixes): A management API key that holds the request_logs_reader scope can now read request logs and request-log stats. Every scoped key was previously refused on these endpoints with "scoped api key cannot call an operation without a tenant boundary", so the scope granted nothing. Results are confined to the key's own organization or project, and the three read endpoints accept an optional customer_id filter. Admin credentials and console sessions are unchanged.
What it does: Returns aggregated statistics for request logs. Requires request_logs_reader (admin implies it).
Request fields:
| Field | Required | Description |
|---|---|---|
user_id | no | Filter by user ID |
model_name | no | Filter by model name |
start_time | no | Start of time range (inclusive) |
end_time | no | End of time range (exclusive) |
group_by | no | Group by dimension Valid values: "model", "user", "api_key", "status", "dataplane", "usage_mode", "usage_source", "hour", "day". Grouping by "dataplane" keys each StatEntry by the reporting dataplane's identity (see RequestLog.dataplane_id -- gateways co-located on one dataplane share a bucket); rows recorded before dataplane attribution existed group under the empty key "". Grouping by "usage_mode" keys each StatEntry by how the request was paid for: "managed" (billed by TARS per token), "byok" (the customer's own provider credential) or "passthrough" (the caller's own subscription). Cost on a byok or passthrough group is a token-priced ESTIMATE, not a charge. Derived from the upstream-backend response header, so it is available for the full history; a row whose header is absent (client cancel, upstream error, or a gateway-side 4xx before routing) currently groups under "managed" -- see tetrateio/fraser#5166. Cost on a byok or passthrough group means what TARS billed, which is deployment-dependent and differs from what the usage dashboard shows for the same requests -- see the StatEntry.cost comment before comparing the two. Grouping by "usage_source" splits a group into measured versus estimated usage: "actual" (the provider reported usage), "partial" (input real, output estimated), "estimated" (no usage at all, e.g. a cancelled stream). Rows for which no usage was resolved group under the empty key "". That bucket does NOT drain over time: as well as rows predating this dimension and rows from a data plane not yet upgraded, it permanently contains rejected and budget-blocked requests, which never reach usage resolution. |
metric | no | Metric to calculate Valid values: "count", "tokens", "cost", "latency", "errors" |
dataplane_id | no | Optional filter restricting stats to logs served by this data plane (see RequestLog.dataplane_id). Empty means "no filter" -- the unattributed "" bucket surfaced by group_by="dataplane" cannot be selected here. |
usage_mode | no | Optional filter restricting stats to one billing mode: "managed", "byok" or "passthrough" (see group_by). Empty means "no filter"; any other value is rejected with InvalidArgument rather than matching nothing, because an empty result reads as "no traffic in this mode". Combine with group_by to cross-cut, e.g. group_by="model" with usage_mode="managed" for per-model actual spend. |
usage_source | no | Optional filter restricting stats to one usage source: "actual", "partial" or "estimated" (see group_by). Empty means "no filter"; any other value is rejected with InvalidArgument. The "" bucket surfaced by group_by="usage_source" cannot be selected here. |
customer_id | no | Tenant boundary / filter. Restricts aggregation to logs recorded by API keys of this customer. A scoped (non-admin) management key is always confined to its own customer whether or not this is set; admin and session callers aggregate all customers when empty (fraser#6341). |
Response fields:
| Field | Required | Description |
|---|---|---|
stats | no | Statistics grouped by the requested dimension |
total_count | no | Total counts across all groups Total number of requests across all groups. |
total_input_tokens | no | Sum of input tokens across all groups. |
total_output_tokens | no | Sum of output tokens across all groups. |
total_cost | no | Total billed cost across all groups as a decimal string. |
avg_duration_ms | no | Mean request duration in milliseconds across all groups. |
error_count | no | Total number of error requests across all groups. |
total_cached_input_tokens | no | Sum of input tokens served from the prompt cache (cache reads) across all groups. |
total_cache_creation_input_tokens | no | Sum of input tokens written to the prompt cache (cache-creation writes) across all groups. |
{"signatures":{"go":"func (x *RequestLogsClient) GetRequestLogStats(ctx context.Context, req *insightsv1.GetRequestLogStatsRequest) (*insightsv1.RequestLogStatsResponse, error)","python":"get_request_log_stats(req: request_logs_pb2.GetRequestLogStatsRequest) -\u003e RequestLogStatsResponse","typescript":"getRequestLogStats(req: MessageInitShape\u003ctypeof tars_insights_v1_request_logs_pb.GetRequestLogStatsRequestSchema\u003e): Promise\u003cRequestLogStatsResponse\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/request-logs/stats\" \\\n -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tinsightsv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/insights/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026insightsv1.GetRequestLogStatsRequest{}\n\n\tresp, err := client.RequestLogs().GetRequestLogStats(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/requestlogs/getrequestlogstats\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.insights.v1 import request_logs_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = request_logs_pb2.GetRequestLogStatsRequest()\ntry:\n result = client.request_logs.get_request_log_stats(req)\n print(result)\nexcept Exception as err:\n print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n# agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n baseUrl: process.env.AGENTROUTER_BASE_URL,\n apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n const result = await client.requestLogs.getRequestLogStats(req)\n console.log(result)\n} catch (err) {\n console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n \"name\": \"requestlogs\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"typescript\": \"^5.4.0\"\n }\n}\n"},{"name":"tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true\n }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/request-logs/stats\" \\\n -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/request-logs/stats","slug":"get-request-log-stats"}