> ## Documentation Index
> Fetch the complete documentation index at: https://docs.poly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Aggregate spans

> Percentiles, counts and error rates over product spans, bucketed by time and grouped by dimension.

Aggregates the spans in a time window into one row per bucket and group. Use it for latency breakdowns per turn or per span, error rates per function or per error type, and trends over hours, days, weeks or months. See the [overview](https://docs.poly.ai/api-reference/traces/introduction.md) for the span, measure and error-type vocabulary.

## Request

### Headers

**X-API-KEY** `string` *required*

Account API key with read access to conversations.

### Body

**from** `datetime` *required*

Inclusive window start, ISO 8601.

**to** `datetime` *required*

Exclusive window end. The window may span at most 30 days.

**aggs** `string[]` *required*

Aggregations to compute, one column each: `p50`, `p95`, `p99`, `avg`, `min`, `max` (over `measure`), `count`, `error_count`, `error_rate` (errors divided by count, `null` when count is 0), `conversation_count`.

**measure** `string` (default: duration\_ms)

Per-span value the timing aggregations run over: `duration_ms`, `time_to_first_token_ms` (set on `polyai.llm_gateway.provider_call` spans) or `time_to_first_byte_ms` (set on TTS spans). Filter on `span_name` for the last two: where no span carries the measure, percentiles return `0` and `avg`/`min`/`max` return `null`.

**group\_by** `string[]`

Dimensions to group by, one column each: `span_name`, `service`, `layer`, `project_id`, `client_env`, `turn_index`, `function_name`, `flow_name`, `step_name`, `deployment_version`, `error_type`.

**interval** `string`

Time bucket: `hourly`, `daily`, `weekly` or `monthly`. Adds a leading `bucket` column. Omit for one unbucketed result per group.

**timezone** `string`

IANA timezone (for example `Europe/London`) applied to the buckets. Defaults to UTC.

**project\_id** `string`

Restrict to one agent. Omit to aggregate the whole account.

**span\_name** `string`

Exact span name, for example `polyai.turn`.

**status\_code** `string`

`Ok`, `Error` or `Unset`.

**function\_name** `string`

Restrict to the spans of one GenAI function.

**error\_type** `string`

Restrict to spans that failed for this reason, for example `LLM_PROVIDER_UNAVAILABLE` or `function_error`.

**conversation\_id** `string`

Restrict to one conversation.

**client\_env** `string[]`

Restrict to environments: `test`, `sandbox`, `pre-release`, `live`, `scenarios`. `pre-release` is the Staging environment.

**sort** `object`

`{ "field": "<a group_by or aggs value>", "order": "asc" | "desc" }`. Applied after the ascending bucket sort.

**limit** `integer` (default: 20)

Rows per page, 1–100.

**offset** `integer` (default: 0)

Rows to skip, 0–1000.

## Response

**columns** `string[]`

Column names in row order: `bucket` (when `interval` is set), then each `group_by` dimension, then each aggregation.

**rows** `array[]`

One array per bucket and group, values in `columns` order. Timing values are milliseconds.

**limit** `integer`

**offset** `integer`

**has\_more** `boolean`

`true` when another page exists.

## Examples

### Latency and error rate per span

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.us.poly.ai/api/v1/traces/spans/aggregate \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "PROJECT-V258TF29",
    "from": "2026-10-26T00:00:00Z",
    "to": "2026-10-28T00:00:00Z",
    "aggs": ["p50", "p95", "count", "error_count", "error_rate"],
    "group_by": ["span_name"],
    "sort": {"field": "count", "order": "desc"},
    "limit": 10
  }'
```

```json Response theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "columns": ["span_name", "p50", "p95", "count", "error_count", "error_rate"],
  "rows": [
    ["polyai.turn", 896.16, 2645.03, 5207, 0, 0.0],
    ["polyai.rag.query", 37.57, 45.88, 5084, 0, 0.0],
    ["polyai.llm_gateway.provider_call", 1226.5, 2140.77, 3495, 49, 0.014],
    ["polyai.function.execution", 62.4, 151.42, 1949, 0, 0.0]
  ],
  "limit": 10,
  "offset": 0,
  "has_more": false
}
```

### Time to first token per turn, hourly

```json Request theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "project_id": "PROJECT-V258TF29",
  "from": "2026-10-26T00:00:00Z",
  "to": "2026-10-27T00:00:00Z",
  "span_name": "polyai.llm_gateway.provider_call",
  "measure": "time_to_first_token_ms",
  "aggs": ["p95"],
  "group_by": ["turn_index"],
  "interval": "hourly",
  "timezone": "Europe/London"
}
```

### Why LLM calls failed

```json Request theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "project_id": "PROJECT-V258TF29",
  "from": "2026-10-26T00:00:00Z",
  "to": "2026-10-28T00:00:00Z",
  "status_code": "Error",
  "aggs": ["count"],
  "group_by": ["error_type"]
}
```

```json Response theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "columns": ["error_type", "count"],
  "rows": [
    ["LLM_PROVIDER_UNAVAILABLE", 89]
  ],
  "limit": 20,
  "offset": 0,
  "has_more": false
}
```

## Errors

| Status | Meaning |
| - | - |
| `400` | `sort.field` is not one of the output columns. |
| `401` | Missing or invalid API key. |
| `403` | The key cannot read conversations. |
| `422` | `from` is not before `to`, the window exceeds 30 days, or a value is outside its enum or range. |
| `502` | The trace backend did not answer. |
