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

# Pagination

> The three paging styles in the REST API and the cursor style on MCP tools, and how to write one client that handles them.

Lists come back in a `{ data, error, meta }` envelope. When a list is paged, `meta.pagination` says
how to get the next page. There are three styles; each endpoint's OpenAPI reference names its query
parameters.

| Style            | Query parameters  | `meta.pagination`                  | Used by (examples)                                            |
| ---------------- | ----------------- | ---------------------------------- | ------------------------------------------------------------- |
| **Cursor**       | `cursor`, `limit` | `nextCursor`, `hasMore`            | Notifications, RFPs, campaigns, audit logs, seller media buys |
| **Limit/offset** | `limit`, `offset` | `limit`, `offset`, `nextOffset`    | Advertisers                                                   |
| **Take/skip**    | `take`, `skip`    | `skip`, `take`, `total`, `hasMore` | Assistant conversations                                       |

Page sizes are capped at 200. Defaults are 25, except advertisers (50).

The campaign list (`GET /api/v2/buyer/campaigns`) also accepts `take` (up to 250) and `skip` as
alternatives to `limit` and `cursor`; when you send a `cursor`, `skip` is ignored.

## Cursor

```http theme={null}
GET /api/v2/notifications?limit=50
GET /api/v2/notifications?limit=50&cursor=<nextCursor>
```

Pass `nextCursor` back as `cursor` until it's `null` (`hasMore: false`). Treat a cursor as opaque:
don't parse it or build one.

## Limit/offset

```http theme={null}
GET /api/v2/buyer/advertisers?limit=50&offset=100
```

Request `offset = nextOffset` until `nextOffset` is `null`.

## Take/skip

```http theme={null}
GET /api/v2/assistant/conversations?take=25&skip=50
```

`total` is the full count; stop when `hasMore` is `false`.

## MCP tools

Tools that list use `limit` and `cursor`. `search` returns a cursor for the next page, and
`get_delivery` takes `limit` (up to 1,000) and `cursor`. Pass the returned cursor unchanged with the
same other arguments.

## One client for all three

```ts theme={null}
async function* pages(fetchPage: (next?: string | number) => Promise<Envelope>) {
  let next: string | number | undefined;
  for (;;) {
    const { data, meta } = await fetchPage(next);
    yield data;
    const p = meta?.pagination;
    if (!p) return;
    if ('nextCursor' in p) { if (!p.nextCursor) return; next = p.nextCursor; }
    else if ('nextOffset' in p) { if (p.nextOffset === null) return; next = p.nextOffset; }
    else { if (!p.hasMore) return; next = p.skip + p.take; }
  }
}
```

## Single-page lists

Some list endpoints return everything in one page with `hasMore: false` and a `null` cursor. Don't
assume a second page exists; follow `meta.pagination`.

The seller media-buys list (`GET /api/v2/storefront/media-buys`) also accepts `take` and `skip` (see
[Media buys](/sell/media-buys)).
