> ## Documentation index
> Fetch the complete documentation index at: https://developer.rhombus.com/llms.txt
> Use it to discover all available pages before exploring further.

# Pagination and Time Ranges

> How Rhombus list operations return results in pages: lastEvaluatedKey cursors, page tokens, timestamp cursors and time windows, with loops in Python and JavaScript and the pitfalls that cause missed or duplicated results.

Operations that can return many results page them in one of a few styles. Each operation's [API Reference](https://developer.rhombus.com/api-reference/overview) page shows which fields it takes; this page explains how each style works.

| Style                                                                          | Request fields                                                     | Response field             | Last page when                       |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------ | -------------------------- | ------------------------------------ |
| [Cursor](https://developer.rhombus.com/pagination#cursor-pages)                | `maxPageSize`, `lastEvaluatedKey` (sometimes inside `pageRequest`) | `lastEvaluatedKey`         | `lastEvaluatedKey` is absent         |
| [Page token](https://developer.rhombus.com/pagination#page-tokens)             | `pageSize`, `pageToken`                                            | `pageToken`                | `pageToken` is absent                |
| [Timestamp cursor](https://developer.rhombus.com/pagination#timestamp-cursors) | `maxPageSize`, `pageTimestampMs`                                   | `lastEvaluatedTimestampMs` | `lastEvaluatedTimestampMs` is absent |
| [Time window](https://developer.rhombus.com/pagination#time-windows)           | `createdAfterMs`, `createdBeforeMs`, `limit`                       | none                       | A page has fewer than `limit` events |
| [Last item](https://developer.rhombus.com/pagination#policy-alerts)            | `maxResults`, `lastTimestampMs`, `lastUuid`                        | none                       | A page comes back empty              |

All timestamps are Unix epoch milliseconds.

## Cursor pages

Most list operations take `maxPageSize` and `lastEvaluatedKey`, and return `lastEvaluatedKey` while more results remain. Send it back unchanged to get the next page; when the response has no `lastEvaluatedKey`, you have everything. For example, [`findAccessControlledDoors`](https://developer.rhombus.com/api-reference/component-webservice/find-access-controlled-doors-within-the-org):

```python Python
import os
import requests

URL = "https://api2.rhombussystems.com/api/component/findAccessControlledDoors"
HEADERS = {"x-auth-scheme": "api-token", "x-auth-apikey": os.environ["RHOMBUS_API_KEY"]}

doors, key = [], None
while True:
    body = {"maxPageSize": 100}
    if key:
        body["lastEvaluatedKey"] = key
    page = requests.post(URL, headers=HEADERS, json=body).json()
    if page.get("error"):
        raise RuntimeError(page.get("errorMsg"))
    doors += page.get("accessControlledDoors", [])
    key = page.get("lastEvaluatedKey")
    if not key:
        break
```

```javascript JavaScript
const url = "https://api2.rhombussystems.com/api/component/findAccessControlledDoors";
const headers = {
  "x-auth-scheme": "api-token",
  "x-auth-apikey": process.env.RHOMBUS_API_KEY,
  "Content-Type": "application/json",
};

const doors = [];
let key;
do {
  const body = { maxPageSize: 100, ...(key && { lastEvaluatedKey: key }) };
  const page = await (await fetch(url, { method: "POST", headers, body: JSON.stringify(body) })).json();
  if (page.error) throw new Error(page.errorMsg);
  doors.push(...(page.accessControlledDoors ?? []));
  key = page.lastEvaluatedKey;
} while (key);
```

Some operations nest the same two fields in a `pageRequest` object (`{"pageRequest": {"maxPageSize": 100, "lastEvaluatedKey": "…"}}`) and return `lastEvaluatedKey` at the top level of the response.

- **Keep going until `lastEvaluatedKey` is absent.** A page can hold fewer results than `maxPageSize`, or none at all, while more remain: results you aren't allowed to see are removed after the page is read.
- **Always set `maxPageSize`.** Without it, some operations return every result in one response, and others apply their own default. Each operation has its own maximum, and a larger value is rejected with `400`.
- **Treat `lastEvaluatedKey` as opaque.** Don't build, edit or store it long-term. A value the server can't read isn't rejected: the operation starts again from the first page.
- **Keep the other fields the same** on every page of one listing.

## Page tokens

A few operations, such as [`getClipsWithProgress`](https://developer.rhombus.com/api-reference/event-webservice/get-list-of-saved-clips-in-organization-with-current-progress), use `pageSize` and `pageToken` instead. Send the `pageToken` from each response with the next request, and stop when the response has none. For `getClipsWithProgress`, `pageSize` is 1 to 200 and defaults to 100.

## Timestamp cursors

[`getSavedClipsV2`](https://developer.rhombus.com/api-reference/event-webservice/get-list-of-saved-clips-in-organization) returns `lastEvaluatedTimestampMs`. Send it as `pageTimestampMs` in the next request, and stop when the response doesn't include it. Set `maxPageSize`; it defaults to 100. As with cursors, a page can be short while more clips remain.

## Time windows

Event history for doors and sensors, such as [`findComponentEventsByAccessControlledDoor`](https://developer.rhombus.com/api-reference/component-webservice/retrieve-all-component-events-relevant-to-the-specified-accesscontrolleddoor), takes a window and a count instead of a cursor:

- `createdAfterMs` and `createdBeforeMs` bound the window. `createdBeforeMs` is exclusive.
- `limit` caps the number of events, the most recent first. **Set it:** without `limit`, every event in the window comes back in one response.

To read further back, repeat the request with `createdBeforeMs` set to one millisecond after the oldest event you received, and drop events you already have by their UUID. Moving the boundary to exactly the oldest timestamp can skip events that share that millisecond.

## Policy alerts

[`getPolicyAlerts`](https://developer.rhombus.com/api-reference/event-webservice/get-recent-policy-alerts-for-organization) returns alerts newest first, up to `maxResults`, with no cursor in the response. For the next page, send the `timestampMs` and `uuid` of the last alert you received as `lastTimestampMs` and `lastUuid`, keeping the same filters and window (`afterTimestampMs`, `beforeTimestampMs`). Stop when a page comes back empty or you reach the start of your window.

## Choosing a page size

Larger pages mean fewer requests against your [rate limit](https://developer.rhombus.com/rate-limits), but slower responses. For a full sync of a large organization, use the largest size the operation accepts, and keep the loop going until the last-page condition in the table above.
