Paginating search results
Use this guide to paginate search results for infinite scrolling, page navigation, or complete data exports.
To set where results start, specify at most one of offset, after, or before in your request. Choose the parameter based on your use case:
- Use cursors (
afterandbefore) for infinite scrolling or walking every result. - Use
offsetto jump directly to a result index. - Use
pagesto render numbered pagination.
Following cursors
Section titled “Following cursors”Cursors carry the position of the hit where the result window ended rather than a count. Following cursors costs the same at any depth and has no page depth cap. Use cursors for infinite scrolling and for walking every result.
To paginate with cursors:
-
Send your initial search request with a
limit. -
Find the opaque cursor strings in the response:
nextcontinues past the current result window.previousprecedes the current result window.
-
Send the cursor back in your next request using
afterorbefore:{ "query": [ ... ], "limit": 20, "after": "AW8..." }
When using cursors, keep the following behaviors in mind:
- If you change the sort order, the server rejects the cursor with
search:cursor:sort_mismatch. You can change the query while keeping the position. - If you reach a response through a cursor, the response omits
page.offsetbecause no results were counted to determine an offset. - Cursors do not carry freshness tokens. A cursor is a position, not a state. To keep pages at or after the state of the first page across nodes, pass the
freshnesstoken of each response asfreshness.atLeaston the next request. See Freshness.
Skipping with an offset
Section titled “Skipping with an offset”To skip a specific number of results, set the offset parameter in your request:
{ "query": [ ... ], "limit": 20, "offset": 40 }Skipping costs as much as ranking. The cap is EXOFIND_SEARCH_MAX_PAGE_DEPTH (10000 by default) and applies to offset plus limit, so a page that ends past the cap is refused with search:paging_too_deep even when its offset is below it. To read results past the cap, follow cursors.
Rendering numbered pages
Section titled “Rendering numbered pages”To render a user interface pager with numbered pages:
-
Add
"pages": {}to your search request. If you want to limit how many page entries return, set themaxfield:{ "query": [ ... ], "limit": 20, "offset": 0, "pages": { "max": 9 } }Requesting pages calculates an exact total match count because pages cannot be numbered against a lower bound. Numbered pages require a numbered position, so combine
pageswithoffsetor a page entry’s cursor, but not withafterorbefore. -
Inspect the
page.pagesobject in the response. The response splits entries intostart,middle, andendruns so that you can render ellipses at run boundaries, such as1 2 3 … 7. -
Use the cursor in a page entry to fetch that page. A page cursor is a count, so it works across different sorts and stays under the depth cap. The server does not offer pages past the cap, preventing navigation to jumps that would be refused.
Counting matches without fetching
Section titled “Counting matches without fetching”To return only the number of matching documents without fetching results:
- Set
limitto0in your request. - If you need an exact count instead of the default lower bound, add
"total": "exact"to the request.
Related
Section titled “Related”- Search API - The paging fields on the request and the response.
- Searching an index - The search request the paging options sit on.
- Reading documents back - Paging a whole index out rather than a result set.
- Searching by vector - How paging behaves for a nearest-neighbour search.
- Make a write visible to search - Why a count can change between pages.
Exofind is built by Level Four AB and is available under the Apache License 2.0.