---
updatedAt: 2026-08-27T14:04:37.000Z
---

Fetch the complete documentation index at: https://developers.tidio.com/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Pagination

Two mechanisms are available. Both need an explicit `order` to be stable - without one the database may return rows in a different sequence between pages, silently skipping or duplicating records.

## Limit and offset

```bash
curl "https://taa.data.tidio.com/ticket_details?select=ticket_id,subject,created_at&order=_unique_id.asc&limit=1000&offset=0" ...
```

Advance `offset` by `limit` for each page.

## Range headers

The equivalent using HTTP ranges. Ranges are inclusive, so `0-999` is the first 1000 rows:

```bash
curl -i "https://taa.data.tidio.com/ticket_details?select=ticket_id,subject&order=_unique_id.asc" \
  -H "X-Tidio-Openapi-Client-Id: $TIDIO_CLIENT_ID" \
  -H "X-Tidio-Openapi-Client-Secret: $TIDIO_CLIENT_SECRET" \
  -H "Range-Unit: items" \
  -H "Range: 0-999"
```

## Counting rows

Send a `Prefer` header to have the total reported in `Content-Range`. The response echoes back which behaviour was actually applied in a `Preference-Applied` header:

| `Prefer`          | Behaviour                                | Cost                  |
| ----------------- | ---------------------------------------- | --------------------- |
| *(omitted)*       | `Content-Range: 0-9/*` - no total        | free                  |
| `count=exact`     | `Content-Range: 0-9/112` - precise total | scans the whole table |
| `count=planned`   | planner estimate                         | cheap                 |
| `count=estimated` | exact when small, planned when large     | cheap                 |

```bash
curl -i "https://taa.data.tidio.com/agent_details?select=_unique_id&limit=1" \
  -H "X-Tidio-Openapi-Client-Id: $TIDIO_CLIENT_ID" \
  -H "X-Tidio-Openapi-Client-Secret: $TIDIO_CLIENT_SECRET" \
  -H "Prefer: count=exact"

# Content-Range: 0-0/112
# Preference-Applied: count=exact
```

On the large tables, use `count=planned` - `count=exact` will time out.

## Keyset pagination for full extracts

`offset` gets slower as it grows, because the database still walks every skipped row. For a complete extract, filter on the last id you saw instead. Page 500 then costs the same as page 1:

```bash
# First page
curl "https://taa.data.tidio.com/ticket_details?select=_unique_id,ticket_id,subject&order=_unique_id.asc&limit=1000" ...

# Subsequent pages - feed the last _unique_id back in
curl "https://taa.data.tidio.com/ticket_details?select=_unique_id,ticket_id,subject&_unique_id=gt.15664967821407535453&order=_unique_id.asc&limit=1000" ...
```

Stop when a page returns fewer rows than the limit.

## Choosing a page size

1000 rows is a good default. On the widest and largest tables - `live_chat_message_details`, `ticket_message_details`, `visitor_details`, `ticket_details`, `flow_statistics_daily` - use **500** to stay clear of the 25s timeout.

## Paging past the end

Behaviour differs depending on whether you asked for a count:

| Request                                       | Result                                   |
| --------------------------------------------- | ---------------------------------------- |
| `offset` past the end, no count               | `200` with `[]` and `Content-Range: */*` |
| `offset` past the end + `Prefer: count=exact` | `500` with `PGRST103`                    |
| `Range` past the end                          | `200` with `[]` and `Content-Range: */*` |

```json
{"detail": {"code": "PGRST103", "details": "An offset of 999 was requested, but there are only 112 rows."}}
```

If you page with a count, read the total from `Content-Range` first and stop before the end.