Skip to main content
Search results and the changes feed come in pages. Each page tells you whether there is more, and gives you a cursor for the next page. The Pagination reference defines every field; this guide shows how to use them.

The loop

  1. Send the request with your filters.
  2. Process data.
  3. If has_more is false, stop.
  4. Otherwise send the same request again, adding cursor set to next_cursor, and go to step 2.
The cursor is bound to your filters and sort, so the next request must repeat them. Only limit, include and include_total may change between pages. A cursor sent with different filters gets 400 cursor-mismatch.

Short and empty pages

Do not use the page size to decide whether you are done. A page can hold fewer records than limit, or none at all, while has_more is still true:
  • a broad search returns what each scanned window contains, and some windows are sparse;
  • records that cannot be assembled at that moment are left out of the page.
Stop only when has_more is false. Continuation pages with no records are not billed.

Consistency between pages

Pages are read live, not from a snapshot. A record that changes while you page can appear on two pages, or move past your position. Treat results as a set keyed by id: upsert, don’t append. To capture changes that happen during a long walk, take a changes feed cursor before you start and follow it afterwards.

Cursors

  • Treat cursors as opaque strings; their format can change at any time.
  • A search cursor is only meant for continuing that search. Store it if you want to resume a long walk later, but do not depend on old search cursors.
  • A feed cursor is meant to be stored: it is your position in the feed. Store it after every page.
  • A cursor that is damaged, truncated, or signed with a key that has been retired gets 400 invalid-cursor. Restart the search, or for the feed, restart with since (see Syncing).

Page sizes

Use limit=100 for bulk work. Every page is one billed call, whatever its size.