Pagination
The three paging styles list endpoints use today, and how to detect the last page of each.
What this covers
List endpoints return one page of results at a time. Three different query
parameter and envelope styles are in use today, depending on which endpoint
you call. This page documents each exactly as it behaves — current behaviour,
not a target shape a future release converges on. A planned clean-up is
expected to unify these onto one shared pattern; until it ships, build each
integration against the exact style the operation you are calling uses,
rather than assuming one operation pages the same way as another.
How it works
limit / offset
limit / offsetUsed by GET /v1/bookings/page, GET /v1/payment-methods/personal-cards,
GET /v1/profile/colleagues, and GET /v1/consumer/travelers.
GET /v1/bookings/page?limit=20&offset=0
The response wraps rows in an items array with pagination metadata nested
under meta:
{
"items": [
{ "bookingId": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f", "status": "confirmed" }
],
"meta": { "totalCount": 42, "pageSize": 20, "nextOffset": 20 }
}Pass meta.nextOffset back as the next request's offset. You have
reached the last page when meta.nextOffset is null.
import httpx
offset = 0
bookings: list[dict] = []
while True:
response = httpx.get(
f"{TRAVELTECH_BASE_URL}/v1/bookings/page",
params={"limit": 20, "offset": offset},
headers=SIGNED_HEADERS,
)
page = response.json()
bookings.extend(page["items"])
next_offset = page["meta"]["nextOffset"]
if next_offset is None:
break
offset = next_offsetlimit / cursor
limit / cursorUsed by GET /v1/assistant/sessions and
GET /v1/assistant/sessions/{sessionId}/messages.
GET /v1/assistant/sessions?limit=20
The response is an items array with pagination fields alongside it, not
nested under meta:
{
"items": [
{ "sessionId": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f", "messageCount": 4 }
],
"pageSize": 20,
"nextCursor": "20",
"totalCount": 3
}nextCursor is an opaque token — send it back verbatim as cursor on the
next request; never parse or construct one yourself, and expect its shape to
change without notice. A malformed cursor returns 422. The session-message
endpoint returns the same items / pageSize / nextCursor shape without
totalCount.
You have reached the last page when nextCursor is null.
import httpx
cursor: str | None = None
sessions: list[dict] = []
while True:
params: dict[str, object] = {"limit": 20}
if cursor is not None:
params["cursor"] = cursor
response = httpx.get(
f"{TRAVELTECH_BASE_URL}/v1/assistant/sessions",
params=params,
headers=SIGNED_HEADERS,
)
page = response.json()
sessions.extend(page["items"])
cursor = page["nextCursor"]
if cursor is None:
breakpage_size / before
page_size / beforeUsed by GET /v1/me/notifications and GET /v1/tasks. The query parameters
are page_size and before (snake_case) on both — unlike every other
operation on this page, which take limit.
GET /v1/me/notifications?page_size=20
The response wraps rows in a data array (not items) with pagination
metadata nested under meta:
{
"data": [
{ "id": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f", "severity": "info" }
],
"meta": { "page_size": 20, "next_cursor": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f" }
}Pass meta.next_cursor back as the next request's before (the id of the
last item on the current page). You have reached the last page when
meta.next_cursor is null — equivalently, when data holds fewer than
page_size rows.
meta's field names are snake_case (page_size, next_cursor) on
GET /v1/me/notifications, but camelCase (pageSize, nextCursor) on
GET /v1/tasks, even though both take the same page_size / before query
parameters and return the same data / meta shape. Check which operation
you are calling before you hard-code a field name.
import httpx
before: str | None = None
notifications: list[dict] = []
while True:
params: dict[str, object] = {"page_size": 20}
if before is not None:
params["before"] = before
response = httpx.get(
f"{TRAVELTECH_BASE_URL}/v1/me/notifications",
params=params,
headers=SIGNED_HEADERS,
)
page = response.json()
notifications.extend(page["data"])
before = page["meta"]["next_cursor"]
if before is None:
breakGET /v1/wallets and GET /v1/wallets/{walletId}/movements take the same
limit and opaque cursor. Their page is items plus meta.totalCount,
meta.pageSize, and meta.nextCursor (camelCase). The last page has
meta.nextCursor null. On the wallet list, company wallets come before
personal wallets; each item's level says which it is.
What you need to handle
- Two envelope keys are in use:
itemsanddata. Thelimit/offset
andlimit/cursorstyles wrap rows initems; thepage_size/before
style wraps them indata. Read the key the operation you are calling
actually returns rather than assuming one name works everywhere. - The "is there a next page" field differs by style and sometimes by
operation:meta.nextOffset,nextCursor(top-level),meta.nextCursor,
ormeta.next_cursor. Confirm the exact field and its casing for the
operation you are calling. - A cursor is opaque. Whether it is named
nextCursorornext_cursor,
treat its value as an unstructured token: store it and send it back
unchanged, and tolerate its format changing between releases. - These styles are not unified today. A planned follow-up is expected to
converge list endpoints onto one shared pagination shape. No timing is
promised; build against the exact behaviour documented above until that
ships, and re-check this page before assuming an operation's shape once the
unification lands.
Related guides
- Errors — the error envelope and recovery
actions. - Versioning and changelog — how a
pagination-shape change would be communicated. - Bookings and trips — the booking list and page
operations in full, including their filters. - Assistant chat — the session and message list
operations in full.
Reporting issues
This page is maintained by the BizAway integration team. To request changes or
report an error, contact your BizAway integration contact. Edits made directly
in the ReadMe dashboard are overwritten on the next publish.
Updated 5 days ago