Pagination

Pagination

Reference article: Part of the API reference.

Every endpoint that lists objects, such as List workstreams or List events, returns them a page at a time. You choose the page size with limit, and move to the next page by passing back the cursor the previous page gave you.

List objects

A list endpoint returns a list object instead of a bare array:

{
  "object": "list",
  "data": [
    { "id": "3f6c2a1e-8b4d-4e2f-9a7c-5d1b0e6f2a94", "object": "workstream", "name": "Q4 pricing review" },
    { "id": "b8e41d07-2c9a-4f63-8e15-7a0d3c9b6e21", "object": "workstream", "name": "Supplier onboarding" }
  ],
  "has_more": true,
  "next_cursor": "bzoyMA"
}

The objects in data are shortened here. A real page holds complete objects.

Field Type Description
object string Always list.
data array The objects on this page, in the order the endpoint documents.
has_more boolean true when there is at least one more page.
next_cursor string or null Pass it as cursor to fetch the next page. null on the last page.

Parameters

Every list endpoint takes these two query parameters, alongside any filters of its own:

Parameter Type Description
limit integer, optional How many objects to return, 1–100. Default 20.
cursor string, optional The next_cursor from the previous page. Leave it out for the first page.

A limit outside 1–100 fails with 400 and the code parameter_invalid. So does a cursor the API didn't issue.

Cursors are opaque

A cursor is a string the API hands you so it can find its place. Don't build one, parse one, or store one for later: pass it back exactly as you got it, on the next request. How cursors are encoded can change without a new API version.

When you pass a cursor, send the same filters you sent for the first page, such as status or workstream. The cursor marks a place in that list, not in some other one.

Paging through everything

Keep requesting pages until has_more is false. This loop collects every workstream the key can reach:

::: code-group

# First page
curl "https://api.gonimbus.ai/v1/workstreams?limit=100" \
  -H "Authorization: Bearer $NIMBUS_API_KEY"

# Next page: pass next_cursor from the previous response as cursor
curl "https://api.gonimbus.ai/v1/workstreams?limit=100&cursor=bzoxMDA" \
  -H "Authorization: Bearer $NIMBUS_API_KEY"
type Page<T> = { object: 'list'; data: T[]; has_more: boolean; next_cursor: string | null }

async function* listAll<T>(path: string, params: Record<string, string> = {}): AsyncGenerator<T> {
  let cursor: string | null = null
  do {
    const query = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) })
    const response = await fetch(`https://api.gonimbus.ai/v1${path}?${query}`, {
      headers: { Authorization: `Bearer ${process.env.NIMBUS_API_KEY}` },
    })
    if (!response.ok) throw new Error(`Nimbus API ${response.status}: ${await response.text()}`)
    const page = (await response.json()) as Page<T>
    yield* page.data
    cursor = page.has_more ? page.next_cursor : null
  } while (cursor)
}

for await (const workstream of listAll<{ id: string; name: string | null }>('/workstreams')) {
  console.log(workstream.id, workstream.name)
}
import os
import requests


def list_all(path, **params):
    session = requests.Session()
    session.headers["Authorization"] = f"Bearer {os.environ['NIMBUS_API_KEY']}"
    cursor = None
    while True:
        query = {**params, "limit": 100}
        if cursor:
            query["cursor"] = cursor
        response = session.get(f"https://api.gonimbus.ai/v1{path}", params=query)
        response.raise_for_status()
        page = response.json()
        yield from page["data"]
        if not page["has_more"]:
            break
        cursor = page["next_cursor"]


for workstream in list_all("/workstreams"):
    print(workstream["id"], workstream["name"])

:::

The same helper works for any list endpoint. Pass filters through, such as list_all("/runs", status="failed").

Order

Most lists are newest first. The exceptions are called out on each endpoint: List replies, for example, returns a thread oldest first, so it reads top to bottom.

FAQ

What's the largest page I can ask for?

100 objects. Ask for limit=100 when you're reading everything, so it takes fewer requests and less of your rate limit.

Why is has_more true when the next page comes back empty?

It can happen at the end of a list when the last objects were removed between your two requests. Treat an empty data with has_more: false as the end, and an empty page as nothing to do.

What if objects change while I'm paging?

Each page is read when you request it, so an object created or deleted partway through can move others across a page boundary. If you need each object exactly once, remember the ids you've seen and skip repeats.

Can I jump to a specific page?

No. Pages are reached one after another through next_cursor. To find something specific, use the endpoint's filters, such as status or workstream, instead of paging to it.

How long does a cursor stay valid?

Use it for the next request, right away. Cursors aren't meant to be saved, and one kept for later may point somewhere the list no longer has.