/offers, /rentals, /rentals/{id}/events — is
cursor-paginated and returns the same envelope:
meta.next_cursor until it is null.
A short page is not the last page
Some filters are applied after the page is read from the database, so a page of 50 rows can arrive as 12 after filtering. The loop that stops onlen(page) < limit will silently drop the rest of your results — and it will do
it intermittently, which is worse than doing it always.
Cursors are bound to their query
A cursor encodes the position within a specific query. Replay it against different filters or a different sort and you get400 invalid_cursor rather
than a silently restarted or scrambled result set:
Limits
Above 200 you get
400 invalid_request. Very deep paging is refused with
pagination_limit_exceeded — at that depth the answer is a narrower filter, not
more pages.
There is no total count
Deliberately. A count over a live marketplace is stale the moment it is computed — offers appear and disappear as suppliers publish and buyers rent — so atotal: 412 would be a number we cannot stand behind, printed with the authority
of one we can.
If you need a count for display, count what you actually received and say so
(“showing 87 offers”). If you need it for a progress bar, prefer a spinner: an
honest indeterminate beats a precise fiction.
Ordering
Within a single cursor walk, ordering is stable. Across walks it is not: the
catalog changes underneath you, and an offer that was on page 1 an hour ago may
be gone entirely.