Pagination

All list endpoints in the ibakepro API use cursor-based pagination.

By default, list endpoints return up to 50 results. You can adjust this with the limit parameter (maximum 100). When there are more results available, the response includes a cursor for the next page.

Pagination response format

All paginated responses follow this structure:

Paginated response

{
  "data": [
    { "id": "Qm4TdvR7yKcB2sNxLpE9" },
    { "id": "HR7Pcz4Arg6kvZKseSil" }
  ],
  "pagination": {
    "limit": 50,
    "has_more": true,
    "next_cursor": "eyJpZCI6IkhSN1BjejRBcmc2a3ZaS3NlU2lsIn0",
    "prev_cursor": null
  }
}

prev_cursor is present in every paginated envelope but is always null. Pagination is forward-only. To revisit an earlier page, restart the list and page forward again, or keep the cursors you have already used.

Pagination parameters

  • Name
    limit
    Type
    integer
    Description

    Number of results to return per page. Default: 50, maximum: 100. A value above 100 is clamped to 100. A missing or non-numeric value falls back to 50.

  • Name
    cursor
    Type
    string
    Description

    Pagination cursor from a previous response. Pass the next_cursor value verbatim to get the next page. Omit it for the first page.

Fetching the next page

When has_more is true, use the next_cursor value to fetch the next page of results.

Send every other query parameter (filters, sort, date ranges) unchanged alongside the cursor. A cursor is only valid against the same endpoint with the same filters and sort.

Request

GET
/api/v2/orders
curl -G https://au.api.ibakepro.com/api/v2/orders \
  -H "Authorization: Bearer {api_key}" \
  -d cursor="eyJpZCI6IkhSN1BjejRBcmc2a3ZaS3NlU2lsIn0" \
  -d limit=50

Loop on has_more, never on page size

A page can contain fewer items than limit while has_more is still true.

Some list conditions are applied after the matching records are gathered (soft-deleted records, storefront visibility, name search, large status filters). On /orders, /products, /customers and /expenses the API tops a page up where it can, but on a heavily filtered list it will return a short page rather than keep going. When it does, has_more stays true and next_cursor resumes exactly where it stopped, rather than pretending the list has ended.

  • Treat has_more as the only signal that more data exists.
  • Never stop because data.length < limit, and never treat a short page as the end of the list.
  • Never treat an empty data array as the end either, if has_more is true.

Iterating through all results

Follow next_cursor until has_more is false:

async function fetchAllOrders(apiKey) {
  const orders = []
  let cursor = null

  do {
    const params = new URLSearchParams({ limit: 100 })
    if (cursor) params.set('cursor', cursor)

    const response = await fetch(
      `https://au.api.ibakepro.com/api/v2/orders?${params}`,
      { headers: { 'Authorization': `Bearer ${apiKey}` } }
    )

    const result = await response.json()
    orders.push(...result.data)

    // has_more is the only termination signal - a short page is not the end.
    cursor = result.pagination.has_more ? result.pagination.next_cursor : null
  } while (cursor)

  return orders
}

Invalid and stale cursors

A cursor is an opaque string. It currently encodes the id of the last record on the page you received, but that is an implementation detail: do not decode, construct, or edit one. Pass back exactly what you were given.

On every list endpoint a cursor is rejected with 400 in two cases:

  • It does not decode. Error code invalid_cursor, field cursor, message Invalid cursor.
  • It decodes but the record it points at no longer exists, because it was deleted or hard-removed. Error code invalid_cursor, field cursor, message Cursor expired or invalid. Restart the list without a cursor.

Error response (400)

{
  "error": {
    "type": "validation_error",
    "code": "invalid_cursor",
    "message": "Cursor expired or invalid. Restart the list without a cursor.",
    "field": "cursor"
  }
}

A stale cursor is an error, not a silent restart. The API does not fall back to page one. Handle invalid_cursor by restarting the list from no cursor, which re-reads everything from the beginning of that ordering.

This is uniform across every list endpoint, including /webhooks and /webhooks/{id}/events. No list endpoint falls back to page one on a cursor it cannot resolve.

Notes

  • Cursors are position-specific. Reuse them only against the same endpoint with the same filters and sort.
  • Pages are a moving window. Records created, deleted, or reordered between calls can be seen twice or missed. Deduplicate by id when a complete snapshot matters.
  • has_more is never a false negative. On every list endpoint has_more and next_cursor are derived from the records that survived post-read filtering, not from the raw batch size, so a page trimmed by soft-deleted or hidden records still reports has_more: true and hands back a cursor.
  • Retry-After is returned on a 429. Reads are rate limited separately from writes.
  • updated_after (supported on products, customers, and expenses) returns only records whose updated_at is greater than or equal to the value supplied, and forces the sort to updated_at. The comparison is inclusive. See Products.