Skip to content
Developers

Pagination

Every list is a keyset page. You follow a cursor from page to page until there is none.

The shape of a list

A list answers an object with 3 members: data (the items of this page), next_cursor and has_more. Pass next_cursor back as cursor to get the next page. On the last page next_cursor is null and has_more is false.

An example list answer
{
  "data": [
    {
      "code": "missing_title",
      "label": "Missing title",
      "severity": "error",
      "category": "content",
      "help": "The page has no <title> element.",
      "why": "Search results show the title; without one they guess.",
      "fix": "Give every page a short, unique title."
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxfQ.opaque"
}

An endpoint may return fewer items than limit while has_more is true: a filter that matches few rows is searched in steps. Keep following the cursor until has_more is false.

Cursors

  • A cursor is opaque. Never build one, and never read one. Its format can change.
  • A cursor belongs to the endpoint, the filters and the workspace that made it. Use it on another endpoint, change a filter, or alter it, and the call answers invalid_cursor. Start the list again without a cursor.
  • Lists are keysets, never offsets. A page does not shift when rows are added, and there are no page numbers. A cursor can be at most 1024 characters.
  • Each list has one fixed order. For another order, download an export.

Page size

ParameterDefaultLargest
limit50200
limit on the issues of a crawl50100

A value outside the range is refused with validation_error.

Counts

A list carries no total: counting a large table on every page would be slow. The Site Pages list of a site takes include=count and then adds the counts behind the dashboard's chips. It is one more query, so it is a heavy call. Ask for it once, not on every page.

The crawl's own counts (pages checked, broken, working) are on the crawl. The first page of a crawl's issues carries its totals.

Following a list

The loop is the same for every list: ask, handle the page, and ask again with next_cursor while has_more is true. Rate limits apply to every page: see rate limits for the classes, and wait for Retry-After when a call answers rate_limited.

A worked loop is in the recipe Page through a list.