Skip to main content

Overview

When an endpoint returns a list of resources, results are split into pages to keep response sizes predictable and latency low. Travelbase uses cursor-based pagination — a pointer that marks your exact position in the dataset, so you never miss a record or see a duplicate even when data changes between requests.

Cursor-Based

A cursor encodes your exact position in the list. Far more reliable than offset/page-number pagination on live data.

Stable Order

Results are always sorted by createdAt descending. Inserting new records never shifts the pages you’ve already fetched.

Configurable Size

Control how many records come back per page with the limit parameter (default 20, max 100).

Query Parameters

Use these parameters on any list endpoint to control pagination.
Start without a cursor to get the first page. On every subsequent request, pass the nextCursor from the previous response until it comes back null.

Response Envelope

Every paginated response wraps its list inside a data object alongside a pagination block.

Pagination Fields

hasMore

true when at least one more page exists after the current one. Use this as your loop condition when fetching all records.

nextCursor

Pass this value as the cursor parameter in your next request. Returns null on the last page.

prevCursor

Lets you navigate backwards. null when you are on the first page.

total

The total number of records across all pages — useful for rendering progress indicators or record counts in your UI.

Fetching Pages

First Page

Omit cursor entirely to start at the beginning.

Next Page

Take the nextCursor from the previous response and pass it as cursor.
Cursors are opaque — never construct or modify them manually. Their internal format may change without notice. Always use the value exactly as returned by the API.

Iterating All Pages

JavaScript

Python

When pulling large datasets, set limit=100 to minimize the number of round-trips and reduce overall latency.

How Cursor Pagination Works

Unlike offset pagination (?page=3), cursors point to a specific record in the ordered dataset. This means:

✅ Consistent results

Records inserted or deleted between requests do not cause pages to shift, so every record appears exactly once across a full traversal.

✅ Scales to millions

The database skips directly to the cursor position — no expensive OFFSET counting that slows down on large tables.

❌ No random access

You cannot jump directly to page 47. Traverse sequentially, or cache cursors you’ve seen if you need to revisit a page.

❌ Cursors expire

Cursors are valid for 24 hours. Do not persist them across sessions or store them in a database for long-term use.

Error Reference

If you receive an INVALID_CURSOR error mid-traversal, your only safe option is to restart from the first page. Cursors cannot be repaired or reconstructed.

Best Practices

1

Always check hasMore, not nextCursor

Treat hasMore: false as your definitive stop condition.nextCursor being null is equivalent, but hasMore is more explicit and easier to read at a glance.
2

Use the maximum limit for bulk jobs

For background sync or data export tasks, set limit=100 to reduce round-trips. Reserve smaller limits for UI-driven pagination where you only need a few records at a time.
3

Handle INVALID_CURSOR gracefully

Cursors expire after 24 hours. Build retry logic that catches INVALID_CURSOR and restarts from the beginning rather than crashing your sync job.
4

Respect rate limits

Paginating hundreds of pages in a tight loop can trigger rate limiting. Add a small delay between requests or use exponential back-off when you receive a 429 response.