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

# Pagination

> Iterate through every page of a list endpoint with the paginate helper.

List endpoints return a single page plus a `pagination` block on the response envelope:

```typescript theme={null}
const { data, pagination } = await client.markets.getMarkets({ limit: 100 });

pagination?.has_more;
pagination?.pagination_key;
```

You can walk pages manually by feeding `pagination_key` into the next call, or use the `paginate` helper to iterate through every item as a stream.

## paginate

`paginate(fetcher, params, pageSize?)` is an async generator that lazily pulls each page and yields individual items. It terminates when the server reports `has_more: false` or returns a null `pagination_key`.

```typescript theme={null}
import { StructClient, paginate } from "@structbuild/sdk";

const client = new StructClient({ apiKey: "sk_live_xxx" });

for await (const market of paginate(
  (params) => client.markets.getMarkets(params),
  { tags: "politics" },
  100,
)) {
  console.log(market.slug, market.last_price);
}
```

| Argument   | Type                                     | Description                                                                      |
| ---------- | ---------------------------------------- | -------------------------------------------------------------------------------- |
| `fetcher`  | `(params) => Promise<HttpResponse<T[]>>` | A namespace method bound to the client.                                          |
| `params`   | base params object                       | Everything except `limit` and `pagination_key`, which are managed by the helper. |
| `pageSize` | `number`                                 | Optional page size. Defaults to `100`.                                           |

Any list-style namespace method works:

```typescript theme={null}
for await (const trade of paginate(
  (params) => client.markets.getTrades(params),
  { condition_ids: "0xabc..." },
)) {
}

for await (const event of paginate(
  (params) => client.events.getEvents(params),
  { tags: "politics" },
)) {
}

for await (const trader of paginate(
  (params) => client.trader.getGlobalPnl(params),
  {},
)) {
}
```

## Early termination

You can stop iterating at any time. Exiting the `for await` loop stops fetching the next page, so you only pay for what you read.

```typescript theme={null}
let count = 0;

for await (const trade of paginate(
  (params) => client.markets.getTrades(params),
  { condition_ids: "0xabc..." },
)) {
  if (++count >= 500) break;
}
```

## Manual pagination

If you prefer to walk pages yourself (for example, to render a paginated UI), skip the helper and drive the cursor directly:

```typescript theme={null}
let cursor: string | number | undefined;

while (true) {
  const page = await client.markets.getMarkets({
    limit: 100,
    pagination_key: cursor,
  });

  render(page.data);

  if (!page.pagination?.has_more || page.pagination.pagination_key == null) break;
  cursor = page.pagination.pagination_key;
}
```

## Offset vs `pagination_key`

A few endpoints (the trader trades and PnL methods) also accept an `offset` param that skips a fixed number of rows. It exists for one case: server-rendered, deep-linkable pages, where `?page=42` maps to `offset=4100` and you need to render that slice on the first request without replaying every page to obtain a cursor.

```typescript theme={null}
const page = await client.trader.getTraderTrades({
  address: "0x...",
  limit: 100,
  offset: 4100,
});
```

`offset` is capped at a few thousand rows (typically in the 3,000 to 5,000 range), so it cannot reach deep into a large dataset. For everything else, prefer `pagination_key`. Cursor pagination is the right default for any programmatic consumer, while `offset` is reserved for rendering a specific page directly from a URL.

<Warning>
  When both `offset` and `pagination_key` are supplied on the same request, `offset` takes precedence and the cursor is ignored.
</Warning>

The [`paginate`](#paginate) helper always uses cursor pagination, so it never sends an `offset`.

See the [API Reference](/api-reference/pagination) for the underlying query parameters.

For a complete worked backfill, see [Fetching an entire dataset](/guides/fetching-all-data).
