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

# Pagination

> List endpoints use opaque cursors, not page numbers.

[`GET /events`](/api-reference/v1/events/list) and
[`GET /orders`](/api-reference/v1/orders/list) return one page at a
time and a cursor for the next page.

## The loop

1. Make the first request **without** `cursor`.
2. If the response has a `next_cursor`, pass it unchanged as `cursor` on the
   next request, **with the same filters**.
3. Stop when there is no next cursor.

The two lists signal the last page slightly differently:

| | Events | Orders |
| - | - | - |
| Default `limit` | 20 | 25 |
| Allowed `limit` | 1 to 50. Larger values are clamped to 50. | 1 to 100. Out of range is `400`. |
| Last page | `next_cursor` is **absent** and `has_more` is `false` | `next_cursor` is **`null`** |

Write your loop to stop on "no usable cursor" and it handles both:

<CodeGroup>
  ```js Node theme={null}
  async function listAll(path, key) {
    const items = [];
    let cursor;
    do {
      const url = new URL(`${BASE}${path}`);
      url.searchParams.set("limit", "50");
      if (cursor) url.searchParams.set("cursor", cursor);
      const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
      const { data } = await res.json();
      items.push(...data[key]);
      cursor = data.next_cursor; // undefined or null on the last page
    } while (cursor);
    return items;
  }

  const events = await listAll("/events", "events");
  ```

  ```python Python theme={null}
  def list_all(path, key):
      items, cursor = [], None
      while True:
          params = {"limit": 50}
          if cursor:
              params["cursor"] = cursor
          r = requests.get(f"{BASE}{path}", params=params,
                           headers={"Authorization": f"Bearer {token}"}, timeout=30)
          data = r.json()["data"]
          items += data[key]
          cursor = data.get("next_cursor")  # missing or None on the last page
          if not cursor:
              return items

  events = list_all("/events", "events")
  ```
</CodeGroup>

## Cursor rules

* Cursors are **opaque**. Do not parse, build or modify them.
* Reuse a cursor only with the filters that produced it. Changing `event_id`
  or `status` mid-loop gives you a page you didn't ask for.
* A malformed cursor is `400`: `invalid_cursor` on events (also for a cursor
  from another organization), `validation_failed` on orders.
* Cursors are for walking a list now, not bookmarks to keep for later.


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