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

# Pagination

> Navigate large result sets with cursor-based pagination.

The ChatGrid API uses **cursor-based pagination** for all list endpoints. This
approach is more reliable than offset pagination when data is being created or
deleted between requests.

## Query parameters

| Parameter | Type    | Default | Description                                       |
| --------- | ------- | ------- | ------------------------------------------------- |
| `limit`   | integer | 50      | Number of items per page (1-100)                  |
| `cursor`  | string  | --      | Opaque cursor from a previous response            |
| `order`   | string  | `desc`  | Sort direction: `asc` or `desc` (by `created_at`) |

<Note>
  The Messages endpoint defaults to `order=asc` (chronological) instead of
  `desc`. All other endpoints default to `desc` (newest first).
</Note>

## Response format

Every list response includes pagination metadata:

```json theme={null}
{
  "object": "list",
  "data": [...],
  "has_more": true,
  "cursor": "eyJpZCI6ImExYjJjM2Q0LWU1ZjYtNzg5MC1hYmNkLWVmMTIzNDU2Nzg5MCIsImNyZWF0ZWRfYXQiOiIyMDI2LTAzLTE1VDEwOjMwOjAwLjAwMFoifQ"
}
```

| Field      | Type           | Description                                                                                                |
| ---------- | -------------- | ---------------------------------------------------------------------------------------------------------- |
| `data`     | array          | The items for this page                                                                                    |
| `has_more` | boolean        | `true` if there are more items beyond this page                                                            |
| `cursor`   | string or null | Pass this value as the `cursor` query parameter to get the next page. `null` when there are no more pages. |

## Fetching all pages

To iterate through all results, keep fetching while `has_more` is `true`:

```typescript theme={null}
const API_KEY = "cgk_live_your_key_here";
const boards: Board[] = [];
let cursor: string | null = null;

do {
  const params = new URLSearchParams({ limit: "50" });
  if (cursor) params.set("cursor", cursor);

  const response = await fetch(
    `https://api.chatgrid.ai/v1/boards?${params}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } }
  );

  const json = await response.json();
  boards.push(...json.data);
  cursor = json.has_more ? json.cursor : null;
} while (cursor);

console.log(`Fetched ${boards.length} boards`);
```

```python theme={null}
import requests

api_key = "cgk_live_your_key_here"
headers = {"Authorization": f"Bearer {api_key}"}
boards = []
cursor = None

while True:
    params = {"limit": 50}
    if cursor:
        params["cursor"] = cursor

    resp = requests.get(
        "https://api.chatgrid.ai/v1/boards",
        headers=headers,
        params=params,
    )
    data = resp.json()
    boards.extend(data["data"])

    if not data["has_more"]:
        break
    cursor = data["cursor"]

print(f"Fetched {len(boards)} boards")
```

## How cursors work

Cursors are base64url-encoded JSON containing the `id` and `created_at` of the
last item on the current page. The server uses these values for a keyset query,
which is efficient even on large datasets.

Cursors are opaque -- do not decode or construct them manually. They may change
format without notice. Always use the `cursor` value returned by the API.

## Tips

* **Start with a reasonable `limit`.** 50 is a good default. Use 100 only if
  you need to minimize round trips.
* **Cursors do not expire** but they become invalid if the referenced row is
  deleted. If you receive an `invalid_cursor` error, restart pagination from
  the beginning.
* **Combine with filters.** You can use `cursor` alongside filters like
  `type`, `node_id`, `source_node_id`, or `status`. The cursor is always
  relative to the filtered result set.
