When I use Microsoft Graph API to search emails via Graph Explorer with the following query:
https://graph.microsoft.com/v1.0/me/messages?$search="keyword"&$top=10
I receive a list of messages with the @odata.nextLink property at the end of the response. This link includes a skiptoken parameter, which allows me to fetch the next set of results by using it directly in Graph Explorer, like this:
"@odata.nextLink": "https://graph.microsoft.com/v1.0/me/messages?$search="keyword"&$top=10&$skiptoken=MSZZVlF4YkZwVVRUUk9SR04zVG1reGExbDZRWGhNVkZKdFRVZEpkRmxVVFRKWmFUQXlXbXBCTUU1RVNtbE9SR2Q0V1dwRmJXTjZNSGhOUVQwOQ%3d%3d"
Problem: I am trying to implement a pagination system that would allow me to perform a search and move directly to any page (e.g., jump from page 1 to page 3) without sequentially fetching each page. However, since $search
and $skip
cannot be used together in Microsoft Graph API, attempting to combine them results in a searchAndSkip
error.
To work around this limitation, I am considering whether it is possible to construct the skiptoken manually to skip directly to a desired page while still using $search.
Questions:
skiptoken
manually?
skiptoken
is encoded or encrypted, is there any way to decode it and understand its structure?skiptoken
based on certain parameters allow me to effectively combine $search
and pagination functionality, bypassing the searchAndSkip limitation?
skiptoken
for Graph API pagination?
Understanding the structure of skiptoken or any method to customize it would be extremely helpful for implementing a more flexible pagination system with $search, especially given the current limitation where $search
and $skip
cannot be used together.Any help or guidance would be appreciated. Thank you!
As an alternative, you can use the Microsoft Search API to search Outlook messages. It's very similar to searching implemented by Outlook clients.
POST https://graph.microsoft.com/v1.0/search/query
{
"requests": [
{
"entityTypes": [
"message"
],
"query": {
"queryString": "keyword"
},
"from": 0,
"size": 10
}
]
}
To get results from other page, increment from
by size
.
POST https://graph.microsoft.com/v1.0/search/query
{
"requests": [
{
"entityTypes": [
"message"
],
"query": {
"queryString": "keyword"
},
"from": 10,
"size": 10
}
]
}
The Microsoft Graph .NET SDK:
var requestBody = new QueryPostRequestBody
{
Requests = new List<SearchRequest>
{
new SearchRequest
{
EntityTypes = new List<EntityType?>
{
EntityType.Message,
},
Query = new SearchQuery
{
QueryString = "keyword",
},
From = 0,
Size = 10
}
}
};
// first page
var searchResponse = await graphClient.Search.Query.PostAsQueryPostResponseAsync(requestBody);
foreach (var hit in searchResponse.Value[0].HitsContainers[0].Hits)
{
if (hit.Resource is Message message)
{
Console.WriteLine($"Message Id: {message.Id}");
}
}
// check if there are more results
while (searchResponse.Value[0].HitsContainers[0].MoreResultsAvailable.GetValueOrDefault())
{
// next page
requestBody.Requests[0].From += requestBody.Requests[0].Size;
searchResponse = await graphClient.Search.Query.PostAsQueryPostResponseAsync(requestBody);
foreach (var hit in searchResponse.Value[0].HitsContainers[0].Hits)
{
if (hit.Resource is Message message)
{
Console.WriteLine($"Message Id: {message.Id}");
}
}
}
Limitation is that you can't sort messages. Search results are sorted by receivedDateTime
in descending order.
https://learn.microsoft.com/en-us/graph/search-concept-messages