Skip to main content

Cursor-Based Pagination

All Orbit list endpoints use cursor-based pagination for consistent, efficient navigation through large result sets. Unlike offset-based pagination, cursors ensure stable results even as data changes.

Overview

Every paginated response includes a pagination object in the meta field:
{
  "data": [...],
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-03-08T12:00:00Z",
    "pagination": {
      "cursor": "cur_msg_abc123",
      "has_more": true,
      "total": 1542
    }
  }
}
FieldTypeDescription
cursorstringOpaque cursor pointing to the last item in the current page
has_morebooleanWhether more results exist beyond this page
totalintegerTotal number of matching items (when available)

Quick Start

First Page

Request without a cursor to get the first page:
curl "https://orbit-api.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:
curl "https://orbit-api.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

All list endpoints accept these pagination parameters:
ParameterTypeDefaultDescription
cursorstringCursor from a previous response to fetch the next page
limitinteger20Number of items per page (1–100)

Examples

Node.js — Iterate All Messages

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 — Iterate All Contacts

orbit = OrbitClient(api_key="dv_live_sk_xxxx")

all_contacts = []
cursor = None

while True:
    response = orbit.contacts.list(limit=100, cursor=cursor)
    all_contacts.extend(response.data)

    if not response.meta.pagination.has_more:
        break
    cursor = response.meta.pagination.cursor

print(f"Fetched {len(all_contacts)} contacts")

Go — Iterate All Agents

client := orbit.NewClient("dv_live_sk_xxxx")

var allAgents []orbit.Agent
cursor := ""

for {
    resp, err := client.Agents.List(ctx, &orbit.ListParams{
        Limit:  100,
        Cursor: cursor,
    })
    if err != nil {
        log.Fatal(err)
    }

    allAgents = append(allAgents, resp.Data...)

    if !resp.Meta.Pagination.HasMore {
        break
    }
    cursor = resp.Meta.Pagination.Cursor
}

fmt.Printf("Fetched %d agents\n", len(allAgents))

Best Practices

  1. Use a reasonable page size. limit=100 is the 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:
curl "https://orbit-api.devotel.io/api/v1/messages?channel=sms&status=delivered&limit=50" \
  -H "X-API-Key: dv_live_sk_your_key_here"

Why Cursor-Based Pagination?

FeatureCursor-BasedOffset-Based
Consistency during concurrent writesStableMay skip or duplicate items
Performance on large datasetsO(1) per pageDegrades with high offsets
Supported by OrbitYesNo
Orbit exclusively uses cursor-based pagination to guarantee consistent, performant results regardless of dataset size.
Offset-based pagination (?page=2 or ?offset=20) is not supported by any Orbit endpoint.