# Pagination

Every list endpoint is cursor-paginated.

## Request

| Parameter | Type    | Default | Max   | Notes                                                       |
| :-------- | :------ | :------ | :---- | :---------------------------------------------------------- |
| `cursor`  | string  | —       | —     | Opaque token from the previous response's `nextCursor`.     |
| `limit`   | integer | `25`    | `100` | Number of items in the page.                                |

Cursors are opaque tokens; the encoded format is not guaranteed stable.

## Response

```json
{
  "ok": true,
  "data": {
    "items": [{ "id": "..." }, { "id": "..." }],
    "nextCursor": "eyJ0Ijoi..."
  }
}
```

`nextCursor: null` means there are no more pages.

## Loop pattern

`resp` is the parsed JSON envelope (`{ ok, data }`), so `items` and `nextCursor` are read from `resp["data"]`:

```python
cursor = None
while True:
    resp = client.get("/competitions", cursor=cursor, limit=50)
    data = resp["data"]
    for item in data["items"]:
        process(item)
    cursor = data.get("nextCursor")
    if not cursor:
        break
```

There's no `offset` and no `?page=` parameter — neither would be stable under writes.

## Filters

Filters are listed per endpoint and applied **before** the cursor. Changing filters invalidates the cursor — start a new walk.
