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

# Pagination

> Cursor-based pagination for all list endpoints.

Every list endpoint in the API is **cursor-paginated**. You request a page size and, to advance, pass the opaque `next_cursor` from the previous page.

## Query parameters

<ParamField query="limit" type="integer">
  Number of objects to return. Default `20`, minimum `1`, maximum `100`. A value outside that range is **rejected** with `400 validation_failed` (`param: "limit"`, message `limit must be 1..100`) — it is not clamped.
</ParamField>

<ParamField query="after" type="string">
  An opaque cursor — the `next_cursor` returned by the previous page. Returns the page of results **immediately after** it. Omit to fetch the first page.
</ParamField>

## Response shape

All list responses share the same envelope:

<ResponseField name="data" type="array">The page of objects, newest first.</ResponseField>
<ResponseField name="has_more" type="boolean">Whether more objects exist after this page.</ResponseField>

<ResponseField name="next_cursor" type="string | null">
  The cursor to pass as `after` for the next page. `null` when `has_more` is `false`.
</ResponseField>

```json theme={"system"}
{
  "data": [
    { "id": "agt_01H...C", "object": "agent" },
    { "id": "agt_01H...B", "object": "agent" }
  ],
  "has_more": true,
  "next_cursor": "agt_01H...B"
}
```

<Note>
  For object collections the cursor is the id of the last object on the page, so pagination stays stable even as new objects are created. Some collections (agent versions, deployment runs, files) are also object lists with their own ids and paginate identically. Always treat `next_cursor` as **opaque** — pass it back verbatim rather than constructing it yourself. The only list that does **not** use this envelope is the session events log (see below).

  The two **catalogue** reads — [`GET /v1/models`](/docs/api/models) and [`GET /v1/media/models`](/docs/api/media) — are not object collections: they are read from the provider, not from rows of ours, so their cursor is a position in that read rather than an id. Paging them works exactly as above, with one caveat worth knowing: if the upstream catalogue changes between two pages of the same walk, a boundary can shift by an entry. It is stable within a single read, which is what a search-and-pick flow needs; do not rely on it to enumerate the whole catalogue exactly once.
</Note>

## Worked example

Fetch every agent, one page of 50 at a time.

<CodeGroup>
  ```bash Page 1 theme={"system"}
  curl -fsSL "https://api.vetta.sh/v1/agents?limit=50" \
    -H "authorization: Bearer sk_live_..."
  ```

  ```bash Page 2 theme={"system"}
  curl -fsSL "https://api.vetta.sh/v1/agents?limit=50&after=agt_01H...B" \
    -H "authorization: Bearer sk_live_..."
  ```
</CodeGroup>

A typical loop:

```typescript theme={"system"}
let cursor: string | null = null;
const all: Agent[] = [];

do {
  const url = new URL("https://api.vetta.sh/v1/agents");
  url.searchParams.set("limit", "50");
  if (cursor) url.searchParams.set("after", cursor);

  const res = await fetch(url, { headers: { authorization: `Bearer ${key}` } });
  const page = await res.json();

  all.push(...page.data);
  cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
```

<Note>
  Stop when `has_more` is `false` rather than relying on an empty `data` array.
</Note>

## The events log is a distinct contract

The session [events](/docs/api/events) log is **not** paginated with `after` / `next_cursor`. It is an append-only stream with its own resumable, gap-free `seq` cursor, and it returns events **oldest-first** (the opposite of the newest-first object lists above). This is a deliberately separate contract so you can resume a stream exactly where you left off.

<ParamField query="after_seq" type="integer">
  Return events with `seq` **strictly greater than** this value (exclusive). `seq` is monotonic per session, starts at `1`, and is gap-free; it is mirrored as `last_seq` on the session object and as the SSE `id:` field. Omit to read from the beginning of the retained window.
</ParamField>

```bash theme={"system"}
curl -fsSL "https://api.vetta.sh/v1/sessions/ses_01H.../events?after_seq=42" \
  -H "authorization: Bearer sk_live_..."
```

<Note>
  Events are replayable for at least 72 hours. Persist the last `seq` you processed and resume with `after_seq` to guarantee exactly-once handling across reconnects. See [Events](/docs/api/events).
</Note>
