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

# Pagination

> Cursor-based pagination, with a full loop in JS and Python.

Every list endpoint uses **cursor pagination**. No `offset`, no `page` number. Cursors stay stable as long as the sort order holds, so they beat offsets on live data.

## The shape

A list response looks like this:

```json theme={null}
{
  "object": "list",
  "data": [ /* up to `limit` items */ ],
  "has_more": true,
  "next_cursor": "95.85_0x885783760858e1bd5dd09a3c3f916cfa251ac270",
  "meta": { "request_id": "req_abc123", "cached": false }
}
```

To get the next page, pass `next_cursor` back as the `cursor` query param:

```bash theme={null}
curl -H "Authorization: Bearer $OXINSIDER_API_KEY" \
  "https://api.0xinsider.com/api/v1/leaderboard?limit=20&cursor=95.85_0x885..."
```

Repeat until `has_more` is `false`.

## Parameters

| Parameter | Type    | Default | Max   |
| --------- | ------- | ------- | ----- |
| `limit`   | integer | `20`    | `100` |
| `cursor`  | string  | —       | —     |

## Full loop, end to end

This pulls every page of the leaderboard.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const KEY = process.env.OXINSIDER_API_KEY;
  const headers = { Authorization: `Bearer ${KEY}` };

  async function listAll(path, params = {}) {
    const items = [];
    let cursor;
    do {
      const qs = new URLSearchParams({ limit: "100", ...params });
      if (cursor) qs.set("cursor", cursor);

      const res = await fetch(
        `https://api.0xinsider.com/api/v1${path}?${qs}`,
        { headers }
      );
      const body = await res.json();
      items.push(...body.data);
      cursor = body.has_more ? body.next_cursor : undefined;
    } while (cursor);

    return items;
  }

  const leaders = await listAll("/leaderboard");
  console.log(`Fetched ${leaders.length} traders`);
  ```

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

  BASE = "https://api.0xinsider.com/api/v1"
  H = {"Authorization": f"Bearer {os.environ['OXINSIDER_API_KEY']}"}

  def list_all(path, **params):
      items, cursor = [], None
      while True:
          p = {"limit": 100, **params}
          if cursor:
              p["cursor"] = cursor
          body = requests.get(f"{BASE}{path}", params=p, headers=H).json()
          items.extend(body["data"])
          if not body.get("has_more"):
              return items
          cursor = body["next_cursor"]

  leaders = list_all("/leaderboard")
  print(f"Fetched {len(leaders)} traders")
  ```
</CodeGroup>

## Cursors are opaque

Don't parse, construct, or store cursors long-term. Always use the `next_cursor` from the response you just got.

Different endpoints encode cursors differently:

* **Leaderboard**: `{score}_{address}`
* **Whale trades**: `{timestamp}_{id}` (ISO 8601)
* **Insider radar**: `{score}_{id}`

You don't need to know this. Just pass the string back.

## Gotchas

* **Don't mix cursors across filters.** A cursor from `?category=crypto` won't work on `?category=politics`. Restart pagination when filters change.
* **Don't mix cursors across endpoints.** A leaderboard cursor won't work on whale trades.
* **`400 Invalid cursor format`** means the cursor came from a different filter set, a different endpoint, or got mangled. Drop it and start over.
* **Stale cursors expire.** Pause for hours and the underlying sort can shift. Expect to restart.
