Skip to content

Pagination

How list operations return a page at a time, and how to read the next page with a cursor.

Updated

List operations answer one page at a time, in a stable order. Each page carries opaque cursors that lead to the pages around it.

Request a page

Parameter Default Meaning
limit 25 The most items in one page, from 1 to 100. Reading more takes another page.
cursor None A cursor from the previous answer’s pageInfo. Leave it out for the first page.
direction forward Which way to traverse the order from cursor: forward or backward.

Most lists are GET operations and take these as query parameters. Searches sent with POST take them in the JSON body instead, such as agenda.appointments.search and raiz.knowledge.search.

A few operations set their own limit bounds. agenda.appointments.list allows up to 500, and raiz.knowledge.search defaults to 8 with a maximum of 20. The operation’s reference states its bounds.

Read the page

A list’s data is a page:

Field Meaning
object Always "list".
data The items on this page.
pageInfo.hasNextPage Whether a later page exists in the current order.
pageInfo.hasPreviousPage Whether an earlier page exists in the current order.
pageInfo.nextCursor The cursor for the next page, or null when there is none.
pageInfo.previousCursor The cursor for the previous page, or null when there is none.

For example, the first page of account.apiKeys.list, one key long. The call needs the account:api-keys:read scope:

curl "https://api.getarbol.com/v1/account/api-keys?limit=1" \
  --header "Authorization: Bearer $ARBOL_API_KEY"
{
  "success": true,
  "data": {
    "object": "list",
    "data": [
      {
        "id": "exampleClientId0001",
        "name": "Billing backend",
        "scopes": ["account:billing:read"],
        "createdAt": "2026-10-01T15:00:00.000Z",
        "createdBy": null,
        "restrictions": null
      }
    ],
    "pageInfo": {
      "hasNextPage": true,
      "hasPreviousPage": false,
      "nextCursor": "EXAMPLE_CURSOR_2",
      "previousCursor": null
    }
  },
  "error": null,
  "metadata": { "requestId": "00000000-0000-4000-8000-000000000003" }
}

Read every page

Call without a cursor, then call again with cursor set to pageInfo.nextCursor while pageInfo.hasNextPage is true. Keep every other parameter the same.

cursor=""
while :; do
  params=(--data-urlencode "limit=100")
  if [ -n "$cursor" ]; then params+=(--data-urlencode "cursor=$cursor"); fi
  page=$(curl -sS --get https://api.getarbol.com/v1/account/api-keys "${params[@]}" \
    --header "Authorization: Bearer $ARBOL_API_KEY")
  jq -e '.success' <<<"$page" >/dev/null || { echo "$page" >&2; break; }
  jq -c '.data.data[]' <<<"$page"
  [ "$(jq -r '.data.pageInfo.hasNextPage' <<<"$page")" = "true" ] || break
  cursor=$(jq -r '.data.pageInfo.nextCursor' <<<"$page")
done
import { createClient } from "@arbol-ai/sdk";

const arbol = createClient({
  baseUrl: "https://api.getarbol.com",
  token: process.env.ARBOL_API_KEY!,
});

let cursor: string | undefined;
do {
  const { data: page } = await arbol.account.apiKeys.list({ limit: 100, cursor });
  for (const key of page.data) console.log(key.id, key.name);
  cursor = page.pageInfo.hasNextPage ? (page.pageInfo.nextCursor ?? undefined) : undefined;
} while (cursor);

Cursor rules

  • Cursors are opaque. Use them exactly as the API returned them. Don’t build, edit or decode one.
  • A cursor belongs to its query. Send it with the same filters that produced it. Raíz refuses a cursor from another list with 400 INVALID_CURSOR.
  • Some reads don’t page. A few take only limit and answer no pageInfo, such as agenda.firstAvailability.get. Their reference shows it.

Type to search…

↑↓ navigate↵ selectEsc close