Skip to main content
List endpoints page with two query parameters and answer with two fields:
Both parameters are the same on every list in this API. page is 1-based, defaults to 1 and has no upper bound. page_size defaults to 25 and is capped at 200 — one default and one ceiling, on GET /v1/blocks, GET /v1/properties, GET /v1/bookings, GET /v1/listings/{listing_id}/reviews and GET /v1/webhooks/deliveries alike. Send a page_size above the ceiling and the request is refused with validation_failed naming the parameter — never silently clamped to 200, because a caller who believes they asked for 500 and quietly received 200 stops walking a set they have not finished. Keep asking for the next page while has_more is true. That is the entire loop:

A collection is items, paged or not

Every collection this API returns is called items, and a paged one carries has_more beside it. That is the only difference: GET /v1/catalog/amenities and GET /v1/listings/{listing_id}/calendar answer items too, and simply have no has_more, because a reference catalogue and a date window are bounded by something other than a page. If a field is called nights, it is a count — Block.nights is how many nights a block holds — never a collection.

A page is a page number, and the list moves

Every paginated read here is newest first, and a page is a number rather than a cursor — so a row that lands while you are walking shifts everything behind it. On a long walk that means you can see a row twice, or miss one entirely. Two things make it a non-problem, and both are worth doing:
  • Key what you collect by its own id, not by its position. Every row carries one, and every one of them is stable.
  • Narrow before you walk. GET /v1/blocks takes from/to, GET /v1/bookings takes status, listing_id and a check-in window, and GET /v1/webhooks/deliveries takes status and event_type. A set that cannot move while you page is the real fix.
Treat a walk as eventually complete rather than exact. There is no cursor and no snapshot today; if that changes it will be an addition, in the changelog.

There is no total, on purpose

The response tells you whether another page exists, not how many rows exist. Counting the whole set to answer one page is work neither side needs — and for a reconciliation read, “walk until has_more is false” is the correct loop anyway. If you need a count, count as you walk.

Narrowing before paging

Where an endpoint offers filters — from/to on GET /v1/blocks bound the result to blocks holding a night inside the window — prefer narrowing over walking the whole set. A block whose check_out equals from holds no night in the window (departure day is exclusive) and is not returned. Each page is bounded by the same filters, so the has_more loop composes with them unchanged.