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

# Pagination

> How list endpoints page results — keyset cursors, offset pages, and full rosters.

Most public list and search endpoints return one **page** of rows. `limit`
sets the page size. It is not a cap on the full result set. Read every
matching row by following the page token until it is gone.

See also: [Data model](/data-model) · [Errors](/errors)

## Keyset pages

These endpoints use keyset pagination: guests, master profiles, reservations,
transactions, consents, actions, reviews, loyalty programs, loyalty members,
channels, spaces, and the event log in `timeline` mode.

The response shape is:

```json theme={null}
{ "data": [ ... ], "next_cursor": "eyJ2IjoxLCJrIjpb...", "has_more": true }
```

| Field         | Meaning                                                      |
| ------------- | ------------------------------------------------------------ |
| `data`        | Rows on this page.                                           |
| `next_cursor` | Opaque token for the next page. `null` when no page remains. |
| `has_more`    | `true` when another page exists.                             |

`limit` defaults to **100**. Casa Layer clamps it to **500**. Omit `cursor` on
the first request. Then send the last `next_cursor` as `cursor` until
`next_cursor` is `null`.

```bash theme={null}
# first page
curl "https://api.casa-layer.com/v1/master-profiles?limit=500" \
  -H "Authorization: Bearer casa_your_key_here"

# next page
curl "https://api.casa-layer.com/v1/master-profiles?limit=500&cursor=eyJ2IjoxLCJrIjpb...==" \
  -H "Authorization: Bearer casa_your_key_here"
```

Cursors are opaque. Do not parse them. They stay stable under concurrent
inserts — a walk does not skip or double-count a row.

A malformed `cursor` returns `400` with `{ "error": "invalid_cursor" }`. Start
again from the first page.

Master profile search also accepts `include_total`. When it is `true` or `1`
and you send no cursor, the first page includes `total_count` for matching
master profiles.

## One-page lookups

Some queries return the same envelope but never page:

* `GET /v1/guests?source_id=…&integration_id=…` — one matching guest, or
  `data: []`. A miss is an empty list, not `404`.
* `GET /v1/events?mode=latest` — one page of the latest delta per entity.
  `next_cursor` is `null`. (`rollup` is a deprecated alias for `latest`.)

`GET /v1/guests/{id}` and other get-by-ID routes return `{ "data": { … } }`.
They are not lists.

## Full rosters

These routes return the full set in `{ "data": [ … ] }`. They do not take
`cursor` or `offset`:

* `GET /v1/properties`
* `GET /v1/integrations`

`GET /v1/reviews/summary` is an aggregate object, not a list.

## Offset pages (tables)

Table list, member, and preview routes use **offset** pagination:

```json theme={null}
{ "data": [ ... ], "has_more": true, "next_offset": 100 }
```

Send `limit` and `offset`. Default `limit` is 100 (max 500). Default `offset`
is 0. Casa Layer clamps `offset` to 50,000.

When `has_more` is `true`, send `offset` equal to `next_offset`. Repeat until
`has_more` is `false`.

Some table routes accept `include_total`. When true, the page includes
`total_count`.

<Info>
  The [MCP server](/mcp-server) uses the same page shapes. `events` in
  `latest` mode is still one page.
</Info>
