Page results using OData
Use the Prefer: odata.maxpagesize
request header to control the number of records returned. If you don't specify a number, up to 5,000 records may be returned for each request. You can't request a page size larger than 5,000.
Note
Dataverse doesn't support the $skip
query option, so you can't use the combination of $top
and $skip
for paging. Learn about using the $top query option to limit the number of rows
The following example returns just the first two contact records:
Request:
GET [Organization URI]/api/data/v9.2/contacts?$select=fullname
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Prefer: odata.maxpagesize=2
Response:
HTTP/1.1 200 OK
OData-Version: 4.0
Preference-Applied: odata.maxpagesize=2
{
"@odata.context": "[Organization URI]/api/data/v9.2/$metadata#contacts(fullname)",
"value": [
{
"@odata.etag": "W/\"72201545\"",
"fullname": "Yvonne McKay (sample)",
"contactid": "49b0be2e-d01c-ed11-b83e-000d3a572421"
},
{
"@odata.etag": "W/\"80648695\"",
"fullname": "Susanna Stubberod (sample)",
"contactid": "70bf4d48-34cb-ed11-b596-0022481d68cd"
}
],
"@odata.nextLink": "[Organization URI]/api/data/v9.2/contacts?$select=fullname&$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%2522%253e%253ccontactid%2520last%253d%2522%257bD5026A4D-D01C-ED11-B83E-000D3A572421%257d%2522%2520first%253d%2522%257b49B0BE2E-D01C-ED11-B83E-000D3A572421%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E"
}
When there are more records than requested, the @odata.nextLink
annotation provides a URL you can use with GET
to return the next page of data, as shown in the following example:
Request:
GET [Organization URI]/api/data/v9.2/contacts?$select=fullname&$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%2522%253e%253ccontactid%2520last%253d%2522%257bD5026A4D-D01C-ED11-B83E-000D3A572421%257d%2522%2520first%253d%2522%257b49B0BE2E-D01C-ED11-B83E-000D3A572421%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Prefer: odata.maxpagesize=2
Response:
HTTP/1.1 200 OK
OData-Version: 4.0
Preference-Applied: odata.maxpagesize=2
{
"@odata.context": "[Organization URI]/api/data/v9.2/$metadata#contacts(fullname)",
"value": [
{
"@odata.etag": "W/\"80648710\"",
"fullname": "Nancy Anderson (sample)",
"contactid": "72bf4d48-34cb-ed11-b596-0022481d68cd"
},
{
"@odata.etag": "W/\"80648724\"",
"fullname": "Maria Campbell (sample)",
"contactid": "74bf4d48-34cb-ed11-b596-0022481d68cd"
}
],
"@odata.nextLink": "[Organization URI]/api/data/v9.2/contacts?$select=fullname&$skiptoken=%3Ccookie%20pagenumber=%223%22%20pagingcookie=%22%253ccookie%2520page%253d%25222%2522%253e%253ccontactid%2520last%253d%2522%257bF2318099-171F-ED11-B83E-000D3A572421%257d%2522%2520first%253d%2522%257bBB55F942-161F-ED11-B83E-000D3A572421%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E"
}
You should cache the results returned, or the @odata.nextLink
URL value, and use it to return to previous pages.
Don't change the @odata.nextLink
URL value or append any query options to it. For every subsequent request for more pages, you should use the same odata.maxpagesize
preference value used in the original request. You can continue paging through the data until no @odata.nextLink
annotation is included in the results.
In the earlier examples, encoded information was set as the value of the $skiptoken
parameter in the @odata.nextLink
URL value. The server sets this encoded information to control paging. You shouldn't modify the encoded information or encode it further. Use the URL value provided to retrieve the next page.
Don't use the $top query option while paging records
Use the $top
query option to limit the number of results returned. Don't use $top
with the Prefer: odata.maxpagesize
request header. If you include both, $top
is ignored.
The following example returns just the first three account rows:
GET [Organization URI]/api/data/v9.2/accounts?$select=name,revenue&$top=3
Ordering and paging
How a page is ordered makes a big difference when paging data. If the information about how the results are ordered is ambiguous, Dataverse can't consistently or efficiently return paged data.
Specify an order for your query. With FetchXml, if you don't add any order elements to your query, Dataverse adds an order based on the primary key of the table. However QueryExpression does not, and when your query specifies distinct
results, no primary key values are returned, so Dataverse can't add this default order. You must specify a paging order. Without any order specified, distinct
query results might be returned in random order. OData doesn't provide any option to return distinct results, but you should still apply an order when retrieving paged results.
Paging is dynamic. Each request is evaluated independently as they're received. A paging cookie tells Dataverse the previous page. With this paging cookie data, Dataverse can start with the next record after the last one on the preceding page.
Paging works best going forward. If you go back and retrieve a page you previously retrieved, the results can be different because records could be added, deleted, or modified during since you last retrieved the page. In other words, if your page size is 50 and you go back, you get 50 records, but they might not be the same 50 records. If you keep progressing forward through the pages of a data set, you can expect all the records are returned in a consistent sequence.
Deterministic ordering is important
Deterministic ordering means that there's a way to calculate an order consistently. With a given set of records, the records are always returned in the same order. If you need consistent orders and paging, you must include some unique values or combination of column values that are and specify an order for them to be evaluated.
Nondeterministic example
Let's look at an example that is nondeterministic. This data set contains only State and Status information and is filtered to only return records in an open State. The results are ordered by Status. The first three pages are requested. The results look like this:
State | Status | Page |
---|---|---|
Open | Active | 1 Start |
Open | Active | 1 |
Open | Active | 1 End |
Open | Active | |
Open | Active | |
Open | Inactive | |
Open | Inactive |
The paging cookie saves information about the last record on the page. When the next page is requested, the last record from the first page isn't included. However, given the nondeterministic data, there's no guarantee that the other two records on the first page aren't included in the second page.
To achieve deterministic ordering, add orders on columns that contain unique values, or values that are semi-unique.
Deterministic example
This query is like the nondeterministic one, but it includes the Case ID column that includes unique values. It's also ordered by Status, but also ordered using Case ID. The results look like this:
State | Status | Case ID | Page |
---|---|---|---|
Open | Active | Case-0010 | 1 Start |
Open | Active | Case-0021 | 1 |
Open | Active | Case-0032 | 1 End |
Open | Active | Case-0034 | |
Open | Active | Case-0070 | |
Open | Inactive | Case-0015 | |
Open | Inactive | Case-0047 |
In the next page, the cookie will have Case-0032
stored as the last record in the first page, so page two will start with the next record after that record. The results look like this:
State | Status | Case ID | Page |
---|---|---|---|
Open | Active | Case-0010 | 1 Start |
Open | Active | Case-0021 | 1 |
Open | Active | Case-0032 | 1 End |
Open | Active | Case-0034 | 2 Start |
Open | Active | Case-0070 | 2 |
Open | Inactive | Case-0015 | 2 End |
Open | Inactive | Case-0047 |
Because this query orders unique column values, the order is consistent.
Best practices for orders when paging data
Note
When possible, queries should order on the primary key for the table because Dataverse is optimized for ordering on the primary key by default. Ordering by non-unique or complex fields cause excess overhead and slower queries.
When you retrieve a limited set of data to display in an application, or if you need to return more than 5,000 rows of data, you need to page the results. The choices you make in determining the order of the results can determine whether the rows in each page of data you retrieve overlaps with other pages. Without proper ordering, the same record can appear in more than one page.
To prevent the same record from appearing in more than one page, apply the following best practices:
It's best to include a column that has a unique identifier. For example:
- Table primary key columns
- Autonumber columns
- User/contact IDs
If you can't include a column with a unique identifier, include multiple fields that will most likely result in unique combinations. For example:
- First name + last name + email address
- Full name + email address
- Email address + company name
Anti-patterns for orders when paging data
The following are ordering choices to avoid:
Orders that don't include unique identifiers
Orders on calculated fields
Orders that have single or multiple fields that aren't likely to provide uniqueness such as:
- Status and state
- Choices or Yes/No
- Name values by themselves. For example
name
,firstname
,lastname
- Text fields like titles, descriptions, and multi-line text
- Non unique number fields
Next steps
Learn how to aggregate data.