scim.cloud

Cursor Pagination

RFC 9865 Published Updates 7643, 7644 rfc-editor.org/rfc/rfc9865

Cursor-based pagination for SCIM queries, for service providers whose underlying store already pages by cursor.

RFC 7644 pages results by index: the client asks for a startIndex and a count. That does not map cleanly onto every backend. Many existing codebases, databases, and APIs already page by cursor, and translating between the two models is awkward and often expensive. RFC 9865 adds cursor-based pagination as an alternative, so those providers can expose what they already do.

A service provider may implement either method or both. When it supports both, the client picks by sending either startIndex or cursor, and the provider MUST choose a default for requests that specify neither. A provider retrofitting cursors onto an existing index-based implementation should keep index as the default, so clients that expect the old behaviour are not broken.

Query parameters

Parameter Description
cursor The nextCursor value from a previous result page. Empty or omitted on the first request. Restricted to the unreserved character set of RFC 3986.
count The desired maximum number of results per page. A negative value is treated as 0, which returns only totalResults. The provider must not return more than requested, but may return fewer.

Response attributes

Attribute Description
nextCursor Cursor for the next page. Providers supporting cursor pagination must include it in every paged response except the last — its absence is what signals that there are no more pages.
previousCursor Cursor for the previous page. Optional, and never returned with the first page. Its presence tells the client the provider supports reverse traversal.

Cursor values are URL-safe strings that are opaque to the client. To fetch another page the client repeats the original query exactly, changing only the cursor value.

First page — empty cursor
GET /v2/Users?filter=userName%20sw%20J&cursor&count=10 HTTP/1.1
Host: example.com
Accept: application/scim+json
Authorization: Bearer U8YJcYYRMjbGeepD
Response — nextCursor, no previousCursor on page one
HTTP/1.1 200 OK
Content-Type: application/scim+json

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 100,
  "itemsPerPage": 10,
  "nextCursor": "VZUTiyhEQJ94IR",
  "Resources": [
    { "id": "2819c223-7f76-453a-919d-413861904646", "userName": "jsmith" },
    { "id": "c3a26dd3-27a0-4dec-a2ac-ce211e105f97", "userName": "jhalpert" }
  ]
}

The next request reuses every parameter and sets cursor to the nextCursor just returned:

Subsequent page
GET /v2/Users?filter=userName%20sw%20J&cursor=VZUTiyhEQJ94IR&count=10 HTTP/1.1
Host: example.com
Accept: application/scim+json
Authorization: Bearer U8YJcYYRMjbGeepD
Response — forward and reverse traversal
HTTP/1.1 200 OK
Content-Type: application/scim+json

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 100,
  "itemsPerPage": 10,
  "previousCursor": "ze7L30kMiiLX6x",
  "nextCursor": "YkU3OF86Pz0rGv",
  "Resources": [
    { "id": "a4a25dd3-17a0-4dac-a2ac-ce211e125f57", "userName": "jlevinson" }
  ]
}

Providers should still return an accurate totalResults across all pages, but one that cannot estimate it may omit the attribute entirely. The same parameters work with POST /.search, passed in the request body alongside filter and attributes rather than on the URL.

Errors

These scimType values extend the detail error keywords in RFC 7644 §3.12 and are returned with HTTP 400:

scimType Meaning
invalidCursor The cursor value is not valid for this query.
expiredCursor The cursor has expired. The client waited longer than the provider's cursorTimeout between page requests.
invalidCount The count is outside 0maxPageSize, or differs from the count used in the initial query.

Discovery

A provider implementing cursor pagination should advertise a pagination complex attribute from /ServiceProviderConfig:

cursor boolean, required
Whether cursor-based pagination is supported.
index boolean, required
Whether index-based pagination is supported.
defaultPaginationMethod "cursor" | "index"
Which method applies when the client specifies neither.
defaultPageSize integer
Page size used when the query omits count.
maxPageSize integer
Ceiling on results per page, whatever count asks for.
cursorTimeout seconds
Minimum time a cursor stays valid between page requests.
Absent does not mean unlimited

Every sub-attribute except cursor and index is optional, and a provider may legitimately publish none of them — page limits can vary per resource type, or depend on response size rather than a resource count. Clients must not read a missing value as "no limit" or "no default".