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

# Pagination

> Page through large result sets with cursor-based or offset-based pagination

# Pagination

Most Orbit list endpoints use cursor-based pagination for consistent, efficient navigation through large result sets — cursors keep your iteration stable even as new rows arrive. A subset of dashboard-oriented endpoint families use offset-based pagination instead. This guide covers cursor-based pagination first, then the [offset-based endpoints](#cursor-vs-offset-pagination) and how to tell which style an endpoint uses.

## Overview

Every cursor-paginated response includes a `pagination` object in the `meta` field:

```json theme={null}
{
  "data": [...],
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-03-08T12:00:00Z",
    "pagination": {
      "cursor": "cur_msg_abc123",
      "has_more": true,
      "total": 1542
    }
  }
}
```

| Field      | Type    | Description                                                 |
| ---------- | ------- | ----------------------------------------------------------- |
| `cursor`   | string  | Opaque cursor pointing to the last item in the current page |
| `has_more` | boolean | Whether more results exist beyond this page                 |
| `total`    | integer | Total number of matching items (when available)             |

***

## Quick Start

### First Page

Request without a cursor to get the first page:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/messages?limit=10" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

### Next Page

Pass the `cursor` from the previous response to get the next page:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/messages?limit=10&cursor=cur_msg_abc123" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

### Continue Until Done

Keep paginating until `has_more` is `false`.

***

## Parameters

Cursor-paginated endpoints accept these pagination parameters:

| Parameter | Type    | Default | Description                                                                                                                                                                                                                                                                                  |
| --------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cursor`  | string  | —       | Cursor from a previous response to fetch the next page                                                                                                                                                                                                                                       |
| `limit`   | integer | 25      | Number of items per page. The minimum is 1. The maximum depends on the endpoint: most cursor-paginated endpoints accept up to 200, and a few endpoint families cap lower (for example, 100). Stay within an endpoint's documented maximum — check its API reference entry for the exact cap. |

***

## Examples

### Node.js — Iterate All Messages

```javascript theme={null}
const orbit = new Devotel({ apiKey: 'dv_live_sk_xxxx' })

let cursor = undefined
let allMessages = []

do {
  const response = await orbit.messages.list({
    limit: 100,
    cursor,
    channel: 'sms',
  })

  allMessages = allMessages.concat(response.data)
  cursor = response.meta.pagination.has_more
    ? response.meta.pagination.cursor
    : undefined
} while (cursor)

console.log(`Fetched ${allMessages.length} messages`)
```

### Python / Go / Other languages

First-party SDKs for Python, Go, Java, Ruby, PHP, and .NET are built and in beta but not yet published to their package registries. Until then, hit the REST endpoint directly (`requests`, `httpx`, `net/http`, OkHttp, etc.) — the cursor pattern above maps 1:1 onto any HTTP client.

***

## Best Practices

1. **Use a reasonable page size.** Most cursor-paginated list endpoints accept a `limit` of up to 200; a few endpoint families cap lower (for example, 100). Offset-based endpoints set their own per-endpoint caps. Check the endpoint's API reference entry for its exact maximum. For most use cases, `20–50` is sufficient.

2. **Do not store cursors long-term.** Cursors are opaque and may expire. They are meant for sequential iteration, not bookmarking.

3. **Do not modify cursors.** Cursors are server-generated tokens. Altering them will return a `400 Bad Request`.

4. **Handle empty pages.** If a page returns an empty `data` array with `has_more: false`, iteration is complete.

5. **Combine with filters.** Apply query filters alongside pagination to narrow results before iterating:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/messages?channel=sms&status=delivered&limit=50" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

***

## Cursor vs. offset pagination

Orbit uses two pagination styles depending on the endpoint. Most list endpoints are cursor-based; a number of dashboard-oriented endpoint families use offset-based pagination instead.

|                                      | Cursor-based                                      | Offset-based                                              |
| ------------------------------------ | ------------------------------------------------- | --------------------------------------------------------- |
| Parameters                           | `cursor`, `limit`                                 | `limit`, `offset`                                         |
| Where the page metadata lives        | `meta.pagination` (`cursor`, `has_more`, `total`) | the `data` envelope (`items`, `total`, `limit`, `offset`) |
| Consistency during concurrent writes | Stable                                            | May skip or duplicate items across pages                  |
| Performance on large datasets        | O(1) per page                                     | Degrades as `offset` grows                                |
| Best for                             | Iterating an entire collection                    | Jumping to an arbitrary page in a bounded list            |

Cursor-based pagination is the default for high-volume, append-heavy collections (messages, notifications, audit logs, call lists, and most voice and messaging resources). Offset-based pagination is used where stable deep iteration matters less than jumping to a specific page.

### Offset-based endpoints

Offset-based endpoints accept `limit` and `offset` query parameters and return the page inside the `data` envelope rather than in `meta.pagination`:

```json theme={null}
{
  "data": {
    "items": [...],
    "total": 1542,
    "limit": 50,
    "offset": 100
  }
}
```

Pass `offset` to skip rows and `limit` to size the page; `total` is the full count of matching rows. Offset depth is bounded on these endpoints (for example, the inbox ticket list caps `offset` at 10,000). To reach rows past the cap, narrow the result set with filters (status, date range) instead of paging deeper.

Endpoint families that use offset-based pagination today:

* **Inbox tickets** — `GET /inbox/tickets`, plus the ticket companies roll-up, comments, and history.
* **Contact data (CDP)** — account profiles, account scoring, and contact relationships.
* **Compliance** records.
* **Reports** and **analytics goals**.
* **Surveys** and **referrals**.
* **Admin** — organization, routing, and agent administration.
* **Email suppressions**.

All other list endpoints use cursor-based pagination as described above. When in doubt, check the response: a `meta.pagination.cursor` field means cursor-based; a `data.offset` field means offset-based.
