Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.travelbase.ai/llms.txt

Use this file to discover all available pages before exploring further.

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.
ParameterTypeDefaultDescription
limitinteger20Number of records per page. Maximum is 100.
cursorstringβ€”Opaque cursor returned by the previous response. Omit for the first page.
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.
{
  "success": true,
  "message": "Bookings retrieved successfully",
  "data": {
    "items": [ ...],
    "pagination": {
      "total": 340,
      "limit": 20,
      "hasMore": true,
      "nextCursor": "cur_eyJpZCI6ImJrXzAwNDIifQ",
      "prevCursor": null
    }
  }
}

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.
curl -X GET "https://sandbox.travelbase.ai/v1/bookings?limit=20" \
  -H "Authorization: Bearer tb_test_xxxxxxxxxxxxxxxxx"

Next Page

Take the nextCursor from the previous response and pass it as cursor.
curl -X GET "https://sandbox.travelbase.ai/v1/bookings?limit=20&cursor=cur_eyJpZCI6ImJrXzAwNDIifQ" \
  -H "Authorization: Bearer tb_test_xxxxxxxxxxxxxxxxx"
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

async function fetchAllBookings() {
  const bookings = [];
  let cursor = null;

  do {
    const url = new URL("https://sandbox.travelbase.ai/v1/bookings");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, {
      headers: { Authorization: "Bearer tb_test_xxxxxxxxxxxxxxxxx" },
    });

    const { data } = await res.json();
    bookings.push(...data.items);

    cursor = data.pagination.nextCursor;
  } while (cursor);

  return bookings;
}

Python

import requests

def fetch_all_bookings():
    bookings = []
    cursor = None
    base_url = "https://sandbox.travelbase.ai/v1/bookings"
    headers = {"Authorization": "Bearer tb_test_xxxxxxxxxxxxxxxxx"}

    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor

        response = requests.get(base_url, headers=headers, params=params)
        data = response.json()["data"]

        bookings.extend(data["items"])

        cursor = data["pagination"]["nextCursor"]
        if not cursor:
            break

    return bookings
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

CodeHTTPMeaning
INVALID_CURSOR400The cursor value is malformed or has expired. Restart from the first page.
LIMIT_OUT_OF_RANGE400limit is less than 1 or greater than 100.
RATE_LIMIT_EXCEEDED429Too many requests. Respect the Retry-After header.
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.