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

# Pagination

> Cursor-based pagination across every list endpoint.

List endpoints (`GET /facade/render-jobs`, `GET /facade/brands`, etc.) return at most **50 items per page** and use opaque cursors to walk forward and backward.

## Request

| Query param | Default | Notes                                                                   |
| ----------- | ------- | ----------------------------------------------------------------------- |
| `limit`     | 25      | Max 50.                                                                 |
| `cursor`    | —       | Opaque token from a previous response's `next_cursor` or `prev_cursor`. |

```bash theme={null}
curl "https://api.kynva.ai/api/v1/facade/render-jobs?limit=50" \
  -H "Authorization: Bearer $KYNVA_API_KEY"
```

## Response shape

```json theme={null}
{
  "data": [
    { "id": "job_01HV...", "...": "..." }
  ],
  "page": {
    "has_more": true,
    "next_cursor": "eyJpZCI6Impvb...",
    "prev_cursor": null
  }
}
```

| Field              | Meaning                                                                  |
| ------------------ | ------------------------------------------------------------------------ |
| `data`             | The page items.                                                          |
| `page.has_more`    | `true` if more pages exist after this one.                               |
| `page.next_cursor` | Pass as `?cursor=` to fetch the next page. Absent on the last page.      |
| `page.prev_cursor` | Pass as `?cursor=` to fetch the previous page. Absent on the first page. |

## Walking all pages

<CodeGroup>
  ```typescript TypeScript theme={null}
  async function* listAll<T>(url: string): AsyncGenerator<T> {
    let cursor: string | undefined;
    while (true) {
      const u = new URL(url);
      u.searchParams.set("limit", "50");
      if (cursor) u.searchParams.set("cursor", cursor);

      const res = await fetch(u, {
        headers: { Authorization: `Bearer ${process.env.KYNVA_API_KEY}` },
      });
      const page = await res.json();
      for (const item of page.data) yield item as T;

      if (!page.page.has_more) break;
      cursor = page.page.next_cursor;
    }
  }

  for await (const job of listAll<{ id: string }>(
    "https://api.kynva.ai/api/v1/facade/render-jobs"
  )) {
    console.log(job.id);
  }
  ```

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

  def list_all(url: str):
      cursor = None
      while True:
          params = {"limit": 50}
          if cursor:
              params["cursor"] = cursor
          res = requests.get(
              url,
              headers={"Authorization": f"Bearer {os.environ['KYNVA_API_KEY']}"},
              params=params,
              timeout=30,
          )
          res.raise_for_status()
          page = res.json()
          yield from page["data"]
          if not page["page"]["has_more"]:
              break
          cursor = page["page"]["next_cursor"]

  for job in list_all("https://api.kynva.ai/api/v1/facade/render-jobs"):
      print(job["id"])
  ```
</CodeGroup>

## Stability guarantees

* Cursors are **stable across the page being viewed** — items added after you start paginating won't shift your cursor.
* Cursors are **opaque** — don't decode or construct them yourself. The format may change between API versions.
* Cursors are **single-direction-tolerant**: a `next_cursor` from one request can be passed back as `cursor` on the same endpoint, in any order.
