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

# Pagination

> The cursor and page_size parameters, the response envelope, and how to walk a collection safely.

Every collection endpoint is paginated the same way — 53 of them across HRIS, ATS, LMS and the platform APIs. There is no unpaginated variant and no total count.

## Query parameters

| Parameter | Type | Default | Range | Behavior |
| - | - | - | - | - |
| `page_size` | integer | `50` | `1`-`200` | Number of results per page. A value above 200 is rejected, not clamped |
| `cursor` | string | none | - | Opaque pointer to the next page |

Send `page_size` on the first request, then `cursor` on every request after it, copied verbatim from the previous response.

<Warning>
  **Keep every other parameter identical for the whole walk.** Filters are not carried by the cursor, so changing one mid-walk invalidates your position and produces gaps or repeats. The usual cause is recomputing a timestamp filter inside the loop — compute it once, before.
</Warning>

<Note>
  Two platform endpoints have their own bounds: `GET /api/v1/webhooks` defaults to `20` with a maximum of `100`, and `GET /api/v1/webhooks/logs` defaults to `25` with a maximum of `50`. Everything else is `50` and `200`.
</Note>

## Response envelope

All three fields are always present.

```json theme={null}
{
  "cursor": "MDE4YjE4ZWYtYzk5Yy03YTg2LTk5NDYtN2I3YzlkNTQzM2U1",
  "page_size": 50,
  "items": [ ... ]
}
```

| Field | Type | Holds |
| - | - | - |
| `cursor` | string or `null` | Pointer to the next page. **`null` means this is the last page** |
| `page_size` | integer | The count of items in *this* response, not the size you asked for |
| `items` | array | The records |

<Warning>
  Stop on `cursor: null`, not on a short page. `page_size` reports what came back, and a page can be shorter than requested while more records remain — so treating a short page as the end silently truncates the collection.
</Warning>

The cursor is opaque. Its value is not an offset, a record ID or a timestamp, and nothing in it is stable enough to store, decode or construct.

## Walking a collection

Request the first page, then follow the cursor until it comes back `null`.

<CodeGroup>
  ```python Python theme={null}
  import time, requests

  def fetch_all(path, params, api_key, connector_token):
      headers = {
          "Authorization": f"Bearer {api_key}",
          "X-Connector-Token": connector_token,
      }
      params = {**params, "page_size": 200}
      cursor, records = None, []

      while True:
          if cursor:
              params["cursor"] = cursor
          r = requests.get(f"https://api.bindbee.dev{path}",
                           params=params, headers=headers, timeout=30)

          if r.status_code == 429:
              # Wait, then retry the same cursor - don't advance
              time.sleep(float(r.headers.get("Retry-After", 1)))
              continue

          r.raise_for_status()
          body = r.json()
          records.extend(body["items"])
          cursor = body.get("cursor")
          if not cursor:
              return records
  ```

  ```javascript Node theme={null}
  async function fetchAll(path, params, apiKey, connectorToken) {
    const records = [];
    let cursor = null;

    while (true) {
      const qs = new URLSearchParams({ ...params, page_size: "200" });
      if (cursor) qs.set("cursor", cursor);

      const res = await fetch(`https://api.bindbee.dev${path}?${qs}`, {
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "X-Connector-Token": connectorToken,
        },
        signal: AbortSignal.timeout(30_000),
      });

      if (res.status === 429) {
        // Wait, then retry the same cursor - don't advance
        const wait = Number(res.headers.get("Retry-After") ?? 1);
        await new Promise((r) => setTimeout(r, wait * 1000));
        continue;
      }
      if (!res.ok) throw new Error(`${res.status}`);

      const body = await res.json();
      records.push(...body.items);
      cursor = body.cursor;
      if (!cursor) return records;
    }
  }
  ```
</CodeGroup>

On a `429`, wait and retry the **same** cursor rather than advancing. Limits are per connector token, so paging several customers in parallel is fine; paging one customer harder is not.

The cheapest way to page less is to fetch less: filter server-side instead of discarding client-side, and on scheduled runs use `modified_after` to fetch only what changed. See [modified\_after](/guides/reading-writing/reading-data/modified-after).

## What pagination does not give you

There is **no total count and no page number**, so a progress bar cannot be driven from the response. If you need a count, page the whole collection and count what arrives — see [Reconcile Record Counts](/guides/troubleshooting/record-counts).

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="Records are missing or repeated across pages">
    Check every parameter is byte-identical between requests. A sync running mid-walk also shifts the underlying data — page against a stable dataset by gating the run on the sync-completed event, see [Trigger a job on completion](/guides/reading-writing/syncing#triggering-a-job-on-completion).
  </Accordion>

  <Accordion title="The loop never terminates">
    The exit condition must be the absence of a cursor. Terminating on an empty `items` array or a short page will either loop forever or stop early.
  </Accordion>

  <Accordion title="429 partway through">
    Honor `Retry-After` and resume from the cursor you were on — see [Rate limits](/api-reference/basics/rate-limits).
  </Accordion>

  <Accordion title="Pages are slow with expanded relations">
    `expand` and `include_raw_data` increase response size substantially at `page_size=200`. Drop `page_size` when using them.
  </Accordion>
</AccordionGroup>

## Related

* [Filters](/guides/reading-writing/reading-data/filters) - filters that must stay constant across a walk
* [Rate Limits](/api-reference/basics/rate-limits) - what bounds how fast you can page
* [Record Counts](/guides/troubleshooting/record-counts) - counting a collection


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.