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

# Cursor-Based Pagination in the Shilo API

> Shilo list endpoints use cursor-based pagination. Learn how to use limit, cursor, and sort parameters to page through large result sets efficiently.

All Shilo list endpoints return results in pages. Rather than offset-based pagination (which can miss or duplicate records when data changes between requests), Shilo uses a cursor-based approach that gives you a stable pointer into the result set. This makes it safe to page through large collections—like all calls for an agent over the past year—without worrying about skipped or duplicated records.

## Response Shape

Every list endpoint returns a top-level `data` array and a `pagination` object:

```json theme={null}
{
  "data": [
    { "id": "ee6f2136-b94a-4438-9335-3acf5b2a0d31", ... },
    { "id": "a1b2c3d4-5678-90ab-cdef-111122223333", ... }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJjcmVhdGVkX2RhdGUiOiIyMDMwLTA5LTI0VDEyOjM0OjU2WiIsImlkIjoiYTFiMmMzZDQifQ=="
  }
}
```

| Field                    | Type           | Description                                                                            |
| ------------------------ | -------------- | -------------------------------------------------------------------------------------- |
| `data`                   | array          | The current page of results                                                            |
| `pagination.has_more`    | boolean        | `true` if additional results exist beyond this page                                    |
| `pagination.next_cursor` | string \| null | Opaque cursor to pass in your next request; `null` when you have reached the last page |

## Query Parameters

| Parameter | Default | Maximum | Description                                                         |
| --------- | ------- | ------- | ------------------------------------------------------------------- |
| `limit`   | `50`    | `100`   | Number of records to return per page                                |
| `cursor`  | —       | —       | Opaque cursor from the previous response's `pagination.next_cursor` |
| `sort`    | `asc`   | —       | Sort direction by created date: `asc` or `desc`                     |

## Fetching All Pages

To retrieve every record in a collection, loop until `has_more` is `false`:

```bash theme={null}
# Page 1 — no cursor needed
curl "https://api.shilo.ai/api/v1/calls?limit=100&sort=asc" \
  -H "x-api-key: YOUR_API_KEY"

# Page 2 — pass the next_cursor from the previous response
curl "https://api.shilo.ai/api/v1/calls?limit=100&sort=asc&cursor=eyJjcmVh..." \
  -H "x-api-key: YOUR_API_KEY"

# Continue until has_more is false
```

In pseudocode:

```
cursor = null
loop:
  response = GET /api/v1/calls?limit=100&sort=asc[&cursor={cursor}]
  process(response.data)
  if response.pagination.has_more == false:
    break
  cursor = response.pagination.next_cursor
```

## Last Page Indicator

When you have retrieved all available records, the response looks like this:

```json theme={null}
{
  "data": [
    { "id": "last-record-uuid", ... }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}
```

`has_more: false` and `next_cursor: null` together confirm you have reached the end of the result set.

<Note>
  The cursor value is opaque—do not attempt to parse, decode, or construct it manually. Always use the exact string returned by the API in the previous response. Modifying the cursor produces a `400 Bad Request` error.
</Note>

## Filtering and Sorting with Pagination

Most list endpoints accept additional query parameters for filtering (e.g. `user_id`, `contact_id`, `created_date_gte`). You can combine filters with pagination—just include all parameters in every request, including cursor pages:

```bash theme={null}
# Filter calls for a specific user, 100 per page, newest first
curl "https://api.shilo.ai/api/v1/calls?limit=100&sort=desc&user_id=external_user_id:agent-123" \
  -H "x-api-key: YOUR_API_KEY"
```

<Tip>
  When syncing a large dataset for the first time, use `sort=asc` and `limit=100` to minimize the number of requests. For incremental syncs, use `created_date_gte` with your last sync timestamp so you only fetch new records.
</Tip>
