{"slug": "ai-traces-query-api-query-llm-cost-and-tool-calls", "title": "AI Traces Query API - Query LLM Cost and Tool Calls", "summary": "SigNoz introduced a `builder_ai_query` query type in its Trace API that runs queries against AI traces and exposes per-trace fields including `trace.tool_call_count`, `trace.distinct_tool_count` and `trace.estimated_total_cost`. The query type uses the existing `POST https://<your-signoz-url>/api/v5/query_range` endpoint with `signal` set to `traces`, and accepts the same `filter`, `selectFields`, `aggregations`, `groupBy`, `order`, `limit` and `stepInterval` fields as a standard builder query. The API gives developers the data behind SigNoz's AI Observability Explorer without the UI, letting them filter, aggregate and order traces that contain at least one LLM, tool or agent span.", "body_md": "The `builder_ai_query` query type runs queries against your AI traces, which are traces with at least one LLM, tool, or agent span. It adds per-trace fields such as tool call count and estimated cost, so you can filter, aggregate, and order AI traces programmatically. Use it when you want the data behind the [AI Observability Explorer](https://signoz.io/docs/ai-observability-explorer/) without the UI.\n\n## API endpoint\n\n`builder_ai_query` uses the same endpoint, authentication, and top-level payload as the rest of the Trace API:\n\n`POST` `https://<your-signoz-url>/api/v5/query_range`\n\nReplace `<your-signoz-url>` with the URL that you use to open SigNoz in a browser, for example `<your-workspace>.<region>.signoz.cloud` for SigNoz Cloud.\n\nSee the [Trace API overview](https://signoz.io/docs/traces-management/trace-api/overview/) for the API key and header setup, and the [payload model](https://signoz.io/docs/traces-management/trace-api/payload-model/) for the `start`, `end`, `requestType`, and `compositeQuery` fields.\n\nTo use this query type, set `type` to `builder_ai_query` inside a query envelope and keep `signal` as `traces`:\n\n```\n{\n  \"type\": \"builder_ai_query\",\n  \"spec\": {\n    \"name\": \"A\",\n    \"signal\": \"traces\"\n  }\n}\n```\n\nThe `spec` accepts the same fields as a [builder query](https://signoz.io/docs/traces-management/trace-api/payload-model/#builder-query) (`filter`, `selectFields`, `aggregations`, `groupBy`, `order`, `limit`, `stepInterval`). The difference is that `builder_ai_query` also exposes the AI trace fields below.\n\n## Fields for AI queries\n\nTwo kinds of fields are available in AI queries.\n\n**Trace fields** hold a total for the whole trace. Use them in `filter`, in `aggregations`, and in `order`. With `requestType: trace`, SigNoz uses only the spans inside `start` and `end` to select traces. `filter` tests those spans, and `order` uses them to select the traces for the page. The returned totals include every span of the trace, even spans outside `start` and `end`. SigNoz sorts the page by these full totals. So a returned trace can show totals that differ from the values that `filter` tested. With `raw`, `scalar`, and `time_series`, totals include only spans inside the query range.\n\n| Field | Type | Holds | \n|---|---|---|\n| `trace.tool_call_count` | int | Number of tool calls in the trace. | \n| `trace.distinct_tool_count` | int | Number of different tools called in the trace. | \n| `trace.estimated_total_cost` | float | Estimated cost of all LLM calls in the trace. See [Model pricing](https://signoz.io/docs/ai-observability-model-pricing/) for how cost is computed. | \n\nThe Explorer lists the full set of trace fields, including `trace.llm_call_count`, `trace.input_tokens`, `trace.output_tokens`, `trace.total_tokens`, and `trace.max_llm_duration_nano`. See [Run a query](https://signoz.io/docs/ai-observability-explorer/#run-a-query) for the complete table.\n\n**Span attributes** come from the GenAI semantic conventions. Reference them with `fieldContext: attribute` in `selectFields` and `groupBy`.\n\n| Attribute | Holds | \n|---|---|\n| `gen_ai.request.model` | The model that the call requested, for example `gpt-4o` . | \n| `gen_ai.tool.name` | The name of a tool called by the model. | \n| `gen_ai.usage.input_tokens` | The number of input tokens consumed. | \n| `gen_ai.usage.output_tokens` | The number of output tokens produced. | \n\nFor every attribute that SigNoz reads from GenAI spans and the source keys it maps by default, see [AI Observability Telemetry Requirements](https://signoz.io/docs/ai-observability-telemetry-requirements/).\n\n## Examples\n\nAll examples use `type: builder_ai_query` and `signal: traces`. The `start` and `end` values are epoch timestamps in milliseconds; replace them with your query range. `stepInterval` accepts a number of seconds (`60`) or a duration string (`\"60s\"`).\n\n### Raw spans from AI traces with many tool calls\n\nReturn up to 100 spans from the `ai-agent` service whose trace has more than 10 tool calls, newest first. `requestType: raw` returns individual spans.\n\n```\n{\n  \"schemaVersion\": \"v1\",\n  \"start\": 1640995200000,\n  \"end\": 1640998800000,\n  \"requestType\": \"raw\",\n  \"compositeQuery\": {\n    \"queries\": [\n      {\n        \"type\": \"builder_ai_query\",\n        \"spec\": {\n          \"name\": \"A\",\n          \"signal\": \"traces\",\n          \"filter\": {\n            \"expression\": \"service.name = 'ai-agent' AND trace.tool_call_count > 10\"\n          },\n          \"order\": [\n            {\n              \"key\": {\n                \"name\": \"timestamp\",\n                \"fieldContext\": \"span\"\n              },\n              \"direction\": \"desc\"\n            }\n          ],\n          \"limit\": 100\n        }\n      }\n    ]\n  }\n}\n```\n\n### Raw spans with selected gen_ai fields\n\nThe same filter as above, with explicit output columns: the span name plus the requested model, tool name, and input and output token counts.\n\n```\n{\n  \"schemaVersion\": \"v1\",\n  \"start\": 1640995200000,\n  \"end\": 1640998800000,\n  \"requestType\": \"raw\",\n  \"compositeQuery\": {\n    \"queries\": [\n      {\n        \"type\": \"builder_ai_query\",\n        \"spec\": {\n          \"name\": \"A\",\n          \"signal\": \"traces\",\n          \"filter\": {\n            \"expression\": \"service.name = 'ai-agent' AND trace.tool_call_count > 10\"\n          },\n          \"selectFields\": [\n            {\n              \"name\": \"name\",\n              \"fieldContext\": \"span\"\n            },\n            {\n              \"name\": \"gen_ai.request.model\",\n              \"fieldContext\": \"attribute\"\n            },\n            {\n              \"name\": \"gen_ai.tool.name\",\n              \"fieldContext\": \"attribute\"\n            },\n            {\n              \"name\": \"gen_ai.usage.input_tokens\",\n              \"fieldContext\": \"attribute\"\n            },\n            {\n              \"name\": \"gen_ai.usage.output_tokens\",\n              \"fieldContext\": \"attribute\"\n            }\n          ],\n          \"order\": [\n            {\n              \"key\": {\n                \"name\": \"timestamp\",\n                \"fieldContext\": \"span\"\n              },\n              \"direction\": \"desc\"\n            }\n          ],\n          \"limit\": 100\n        }\n      }\n    ]\n  }\n}\n```\n\n### Average cost by model (scalar)\n\nCompute the average estimated per-trace cost for each model, most expensive first. `requestType: scalar` returns one summary value per group. When you group by model, each model gets only the cost of its own spans, so a trace that calls two models does not count its full cost under both.\n\n```\n{\n  \"schemaVersion\": \"v1\",\n  \"start\": 1640995200000,\n  \"end\": 1640998800000,\n  \"requestType\": \"scalar\",\n  \"compositeQuery\": {\n    \"queries\": [\n      {\n        \"type\": \"builder_ai_query\",\n        \"spec\": {\n          \"name\": \"A\",\n          \"signal\": \"traces\",\n          \"aggregations\": [\n            {\n              \"expression\": \"avg(trace.estimated_total_cost)\"\n            }\n          ],\n          \"groupBy\": [\n            {\n              \"name\": \"gen_ai.request.model\",\n              \"fieldContext\": \"attribute\"\n            }\n          ],\n          \"order\": [\n            {\n              \"key\": {\n                \"name\": \"avg(trace.estimated_total_cost)\"\n              },\n              \"direction\": \"desc\"\n            }\n          ],\n          \"limit\": 10\n        }\n      }\n    ]\n  }\n}\n```\n\n### Cost by model over time (time series)\n\nSum the estimated per-trace cost for each model in 60-second buckets. `requestType: time_series` with `stepInterval` is suited to trend charts.\n\n```\n{\n  \"schemaVersion\": \"v1\",\n  \"start\": 1640995200000,\n  \"end\": 1640998800000,\n  \"requestType\": \"time_series\",\n  \"compositeQuery\": {\n    \"queries\": [\n      {\n        \"type\": \"builder_ai_query\",\n        \"spec\": {\n          \"name\": \"A\",\n          \"signal\": \"traces\",\n          \"aggregations\": [\n            {\n              \"expression\": \"sum(trace.estimated_total_cost)\"\n            }\n          ],\n          \"stepInterval\": \"60s\",\n          \"groupBy\": [\n            {\n              \"name\": \"gen_ai.request.model\",\n              \"fieldContext\": \"attribute\"\n            }\n          ],\n          \"order\": [\n            {\n              \"key\": {\n                \"name\": \"sum(trace.estimated_total_cost)\"\n              },\n              \"direction\": \"desc\"\n            }\n          ],\n          \"limit\": 10\n        }\n      }\n    ]\n  }\n}\n```\n\n### Top traces by cost (trace list)\n\nReturn the 10 most expensive traces that have more than 10 tool calls but fewer than 3 distinct tools. `requestType: trace` returns one row per trace.\n\n```\n{\n  \"schemaVersion\": \"v1\",\n  \"start\": 1640995200000,\n  \"end\": 1640998800000,\n  \"requestType\": \"trace\",\n  \"compositeQuery\": {\n    \"queries\": [\n      {\n        \"type\": \"builder_ai_query\",\n        \"spec\": {\n          \"name\": \"A\",\n          \"signal\": \"traces\",\n          \"filter\": {\n            \"expression\": \"trace.tool_call_count > 10 AND trace.distinct_tool_count < 3\"\n          },\n          \"order\": [\n            {\n              \"key\": {\n                \"name\": \"trace.estimated_total_cost\"\n              },\n              \"direction\": \"desc\"\n            }\n          ],\n          \"limit\": 10\n        }\n      }\n    ]\n  }\n}\n```\n\n## Next steps\n\n- [AI Observability Explorer](https://signoz.io/docs/ai-observability-explorer/) : run the same queries in the UI.\n- [AI Observability Telemetry Requirements](https://signoz.io/docs/ai-observability-telemetry-requirements/) : the full list of GenAI attributes and their default source keys.\n- [Trace API payload model](https://signoz.io/docs/traces-management/trace-api/payload-model/) : the shared fields for every query type.", "url": "https://wpnews.pro/news/ai-traces-query-api-query-llm-cost-and-tool-calls", "canonical_source": "https://signoz.io/docs/traces-management/trace-api/ai-traces", "published_at": "2026-10-28 00:00:00+00:00", "updated_at": "2026-11-03 06:21:38.065187+00:00", "lang": "en", "topics": ["ai-tools", "mlops", "ai-agents", "developer-tools", "artificial-intelligence"], "entities": ["SigNoz", "builder_ai_query", "AI Observability Explorer", "Trace API", "gen_ai.request.model", "gen_ai.tool.name"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/ai-traces-query-api-query-llm-cost-and-tool-calls", "markdown": "https://wpnews.pro/news/ai-traces-query-api-query-llm-cost-and-tool-calls.md", "text": "https://wpnews.pro/news/ai-traces-query-api-query-llm-cost-and-tool-calls.txt", "jsonld": "https://wpnews.pro/news/ai-traces-query-api-query-llm-cost-and-tool-calls.jsonld"}}