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

# Pagination

> List endpoints return pages with an opaque cursor.

Every list endpoint (`GET /drivers`, `GET /sessions`, `GET /events`, …) returns the same shape:

```json theme={null}
{
  "data": [
    { "object": "driver", "id": "d3x7n2q9k4m8p1r6t0v5w2yz" },
    { "object": "driver", "id": "e5y8p3r0m6n1q7s2t9u4v0w3" }
  ],
  "has_more": true,
  "next_cursor": "eyJvIjoyfQ"
}
```

(Objects are abbreviated here.)

| Field | Description |
| - | - |
| `data` | The objects on this page. |
| `has_more` | `true` if there are more objects after this page. |
| `next_cursor` | Pass this as `cursor` to get the next page. `null` when `has_more` is `false`. |

## Parameters

| Query parameter | Description |
| - | - |
| `limit` | Page size, from 1 to 100. Defaults to 20. |
| `cursor` | The `next_cursor` from the previous page. Omit it for the first page. |

List-specific filters, such as `status` on `/drivers` or `type` on `/events`, combine with these. Keep the filters the same on every page of a list.

## Walking a list

```javascript Node.js theme={null}
async function* listAll(path, params = {}) {
  let cursor;
  do {
    const query = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) });
    const res = await fetch(`https://partners.chargeexpressltd.com/api/public/v1${path}?${query}`, {
      headers: { Authorization: `Bearer ${process.env.CHARGEEXPRESS_API_KEY}` },
    });
    const page = await res.json();
    if (!res.ok) throw new Error(page.error.message);
    yield* page.data;
    cursor = page.next_cursor;
  } while (cursor);
}

for await (const driver of listAll('/drivers', { status: 'active' })) {
  console.log(driver.id, driver.email);
}
```

```python Python theme={null}
import os
import requests

def list_all(path, params=None):
    params = dict(params or {}, limit=100)
    while True:
        res = requests.get(
            f"https://partners.chargeexpressltd.com/api/public/v1{path}",
            headers={"Authorization": f"Bearer {os.environ['CHARGEEXPRESS_API_KEY']}"},
            params=params,
            timeout=30,
        )
        res.raise_for_status()
        page = res.json()
        yield from page["data"]
        if not page["next_cursor"]:
            return
        params["cursor"] = page["next_cursor"]

for driver in list_all("/drivers", {"status": "active"}):
    print(driver["id"], driver["email"])
```

## Rules

* **Cursors are opaque.** Pass `next_cursor` back exactly as you got it. Don't build or decode cursors; their format can change.
* **Use the same `limit` on every page.** A cursor issued for one page size is rejected with `400 bad_request` ("Use the same limit for every page of a list") if you change `limit` mid-list. A cursor that isn't valid is also rejected with `bad_request`.
* **Lists can shift while you page.** Pages are positional, so if objects are created or removed while you walk a list, you can see an object twice or miss one. For reconciliation, filter by a time window (`from` and `to`, where the endpoint supports them) and dedupe by `id`.
* **Some lists are a single page.** `GET /webhook_endpoints` and `GET /vehicle_types` return everything at once with `has_more: false`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.