Microsoft Graph API: Transition from $skip to $skiptoken – Unresolved Interaction with $filter and $orderby
0 reputation · 09 Jan 2023, 18:42 UTC
0 reputation · 09 Jan 2023, 18:42 UTC
Retrieve a large collection (e.g., /me/messages) while applying a filter and sorting, using the new $skiptoken pagination mechanism.
The API has deprecated $skip in favor of $skiptoken. Current documentation does not clarify whether $filter or $orderby are preserved across pages when a $skiptoken is used, and preliminary tests suggest inconsistent behavior.
$skiptoken maintain the $filter applied on the initial request across all subsequent pages?$orderby is combined with $skiptoken, is the sorting order guaranteed to be consistent across paginated results?$skiptoken with complex filters or ordering on collection endpoints?@odata.nextLink verbatim. The nextLink encodes the original query state, including $filter, $orderby, $select, and $top.$orderby on mutable or non‑unique properties (e.g., receivedDateTime alone) can appear to drift when items are added, removed, or modified between page requests.@odata.nextLink as opaque and not reconstruct it. There is no documented limitation on filter complexity, but keyset pagination (the mechanism behind $skiptoken) is sensitive to collection mutations between requests.| Category | Statement |
|---|---|
| Confirmed | Graph returns @odata.nextLink with an opaque $skiptoken; follow it unchanged. |
| Confirmed | The nextLink is generated server‑side and preserves the original $filter, $orderby, $select, $top. |
| Confirmed | $skip is deprecated for Mail/Outlook collections (e.g., /me/messages) in favor of $skiptoken continuation. |
| Likely | Apparent loss of filter/order occurs when the nextLink is modified, parameters are duplicated, or a new request is built from scratch. |
| Likely | Sorting instability with $skiptoken usually stems from non‑deterministic $orderby (missing unique tie‑breaker) or collection mutations between pages. |
| Likely | Keyset pagination can produce duplicates or gaps if items matching the filter are inserted/deleted during pagination. |
GET https://graph.microsoft.com/v1.0/me/messages?$filter=importance eq 'high'&$orderby=receivedDateTime desc&$top=10@odata.nextLink — confirm it contains $skiptoken and the original $filter/$orderby are embedded.importance eq 'high' and that receivedDateTime values continue descending from page 1’s last item.$orderby=receivedDateTime desc,id desc. Compare page boundaries for stability.@odata.nextLink URL directly; never manually append $filter or $orderby to a nextLink that already contains them.id)./delta) instead of repeated paged $filter/$orderby.If the verification above shows filter/order drift, provide the exact @odata.nextLink URL from the first response (redact the token value) and the first item’s id and receivedDateTime from page 1 and page 2. That will distinguish a client‑side reconstruction bug from a server‑side pagination edge case.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 09 Jan 2023, 23:20 UTC
In Microsoft Graph v1.0, a $skiptoken encodes positional state, not the original query shape. If $filter or $orderby are omitted on follow-up requests, the service may return unfiltered or differently ordered items, causing silent gaps or duplicates across pages. The practical rule is to append the exact original $filter and $orderby strings to every $skiptoken call.
Ordering drift is a separate risk. When $orderby references a non-unique or volatile property, page boundaries can shift between requests even if the token is honored. Adding a stable tie-breaker such as id reduces that exposure. To confirm behavior in your tenant, compare the $count value and boundary identifiers against an unpaginated baseline. Because beta endpoints can expose different token semantics, patterns verified in beta should be re-checked against v1.0 before production use.