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

# Pagination

> Cursor-based pagination on list endpoints.

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="PAGINATION" />

List endpoints page with cursors, not offsets. The response carries a `next_cursor` field when more results exist; you echo it back as a query parameter to fetch the next page. Pages are stable — records inserted or deleted between requests do not cause items to appear twice or be skipped. By the end of this page you will know how to walk a list end to end and how to detect the final page.

## Request parameters

<DefList>
  <Def term="limit" type="integer">Number of results per page. A server-side default applies if omitted; a maximum cap is enforced per endpoint.</Def>
  <Def term="cursor" type="string">Opaque string returned as `next_cursor` from the previous response. Omit on the first request.</Def>
</DefList>

<Note>
  Default and maximum `limit` values are not published. Check `next_cursor` to determine whether more pages exist rather than comparing against an assumed total count.
</Note>

## Response shape

Every list endpoint returns results under a `data` array. When more pages remain, `next_cursor` is present. When you have fetched all results, `next_cursor` is absent or `null`.

<ExpectedOutput>
  ```json theme={null}
  {
    "data": [
      { "id": "page_01H...", "slug": "home" },
      { "id": "page_01J...", "slug": "pricing" }
    ],
    "next_cursor": "cur_01HX..."
  }
  ```
</ExpectedOutput>

## Walk through pages

Fetch the first page without a cursor, then follow `next_cursor` until it is absent:

```bash theme={null}
# First page
curl "https://api.gavai.app/v1/workspaces/acme/pages?limit=50" \
  -H "Authorization: Bearer gak_live_xxxxxxxxxxxxx"

# Next page — use the next_cursor value from the previous response
curl "https://api.gavai.app/v1/workspaces/acme/pages?limit=50&cursor=cur_01HX..." \
  -H "Authorization: Bearer gak_live_xxxxxxxxxxxxx"
```

TypeScript, with a `do`/`while` loop:

```ts theme={null}
import { GavaiClient } from "@gavai/sdk-typescript";

const client = new GavaiClient(process.env.GAVAI_API_KEY!);

const pages = [];
let cursor: string | undefined;

do {
  const res = await client.workspaces.pages.list("acme", { limit: 50, cursor });
  pages.push(...res.data);
  cursor = res.next_cursor ?? undefined;
} while (cursor);

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

## Termination and edge cases

| Signal                                | What it means                                                                                         |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `data` empty, no `next_cursor`        | The list is empty or you have consumed everything.                                                    |
| Absent or `null` `next_cursor`        | Definitive end of results. A short page is **not** a reliable end signal — only the absent cursor is. |
| `404` or `invalid_cursor` on a cursor | The cursor has expired after an extended idle period. Restart pagination from the beginning.          |

## Next

<Card title="Rate limits" icon="gauge" href="/api-reference/rate-limits">
  Per-token limits, `X-RateLimit-*` headers, and the `Retry-After` contract.
</Card>
