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

# Pagination

> One cursor scheme across every collection.

Every collection returns the same envelope:

```json theme={null}
{
  "data": [ ... ],
  "meta": { "limit": 25, "has_more": true },
  "links": { "next": "https://api.chatsailer.com/v1/contacts?limit=25&cursor=eyJvZmZ..." }
}
```

Read pages by following `links.next` until it is `null`:

```python theme={null}
url = "https://api.chatsailer.com/v1/contacts?limit=100"
headers = {"Authorization": f"Bearer {token}"}

while url:
    page = httpx.get(url, headers=headers).json()
    for contact in page["data"]:
        handle(contact)
    url = page["links"]["next"]
```

## Treat the cursor as opaque

Do not parse, construct, or store a cursor. Its contents are an implementation
detail and will change as endpoints move to keyset pagination — but `links.next`
will keep working, which is the whole point of handing you a URL instead of an
offset.

Passing a cursor you built yourself, or one from a different endpoint, returns
`400 invalid_cursor`.

## There is no total count

The response tells you whether another page exists (`meta.has_more`), not how
many records there are in total. Counting the filtered set would mean a second
full scan on every page, which gets slower exactly as a workspace grows.

If you need a count, page through and count what you receive.

## Sorting

Pass `sort` to order a collection, for example `sort=-created_at` for newest
first. Keep `sort` identical across every page of one traversal — changing it
mid-traversal invalidates the cursor.

## Choosing a limit

`limit` defaults to 25 and accepts up to 100. Larger pages mean fewer round
trips; smaller pages mean faster individual responses. For a bulk sync, 100 is
usually right.
