The Zendesk Support, Talk, and Help Center APIs have several endpoints that return lists of items, such as tickets, users, or articles. For performance reasons, the API does not return large record sets all at once. It breaks up the results into smaller subsets and returns them in pages. The number of items per page varies by endpoint. For example, the tickets and users endpoints return 100 items per page while the articles endpoint returns 30 items per page.

This article explains how to paginate through the lists using cursor pagination. Cursor pagination is already supported by most resources, such as tickets and users, and will be progressively introduced to all resources. See the API documentation for the specific resource.

Zendesk recommends using cursor pagination instead of offset pagination where possible. Cursor pagination provides greatly improved performance when retrieving extremely large record sets. See Comparing cursor pagination and offset pagination for a comparison of the different pagination methods supported by the Zendesk APIs.

Topics covered:

Disclaimer: Zendesk provides this article for instructional purposes only. Zendesk does not support or guarantee the code. Zendesk also can't provide support for third-party technologies such as Python and Ruby. Please post any issue in the comments section or search for a solution online.

Using cursor pagination

To use cursor pagination instead of offset pagination, include the page[size] parameter in the request parameters. This parameter is also used to specify the number of items to return per page. Most endpoints limit this to a maximum of 100. See the API documentation for the specific resource.

For example, a request to the tickets endpoint with the URL https://example.zendesk.com/api/v2/tickets.json?page[size]=100 would return a response with the following format: 

"tickets": [ ... ],"meta": {    "has_more": true,    "after_cursor": "xxx",    "before_cursor": "yyy"},"links": {    "next": "https://example.zendesk.com/api/v2/tickets.json?page[size]=100&page[after]=xxx",    "prev": "https://example.zendesk.com/api/v2/tickets.json?page[size]=100&page[before]=yyy"}

The values xxx and yyy are example placeholder values.

Notice that the next and prev properties nested in the links JSON object are URLs. Use these to retrieve the next or previous page of results. Using the above example:

  • Make a request to the URL https://example.zendesk.com/api/v2/tickets.json?page[size]=100&page[after]=xxx to retrieve the next page of results.
  • Make a request to the URL https://example.zendesk.com/api/v2/tickets.json?page[size]=100&page[before]=yyy to retrieve the previous page of results.

Alternatively, use the after_cursor and before_cursor properties nested in the meta JSON object to construct the URL to retrieve the next or previous page of results. Using the above example:

  • Copy the value of after_cursor from the previous response to the page[after] request parameter of the subsequent request to retrieve the next page of results.
  • Copy the value of before_cursor from the previous response to the page[before] request parameter of the subsequent request to retrieve the previous page of results.

Repeat either of the above steps to continue paginating as desired, or until the has_more property nested in the meta JSON object is false. This indicates there are no further records and you should stop paginating. Save the value of after_cursor or next to retrieve new records in the future.

Occasionally, you may get an empty record set, where after_cursor, before_cursor, next and prev are null, even though has_more was true in the previous page. This occurs when the final record returned by the previous page is the final record in the entire record set. In this case, save the previous value of after_cursor for future usage.

Certain resources limit how long the value of after_cursor is valid for. See the API documentation for the specific resource.

Filtering results

Certain resources allow filtering the record set by certain attributes. For example, users may be filtered by roles. See the API documentation for the specific resource.

Sorting results

Certain resources allow sorting the record set by certain attributes. For example, tickets may be sorted by their creation date. See the API documentation for the specific resource.

Limitations

It is not possible to jump to an arbitrary page number or location in the record set when using cursor pagination.

The total number of records in the set is not provided when using cursor pagination. Certain resources provide a different endpoint to retrieve this value. For example, the total number of users can be retrieved from the URL https://example.zendesk.com/api/v2/users/count.json. See the API documentation for the specific resource.

Paginated data may be inaccurate due to the addition, removal or modification of records while you are paginating through the list. One way to reduce pagination inaccuracies -- though not eliminate them altogether -- is to sort the records by an attribute that cannot be modified, such as the id or the creation date, if this is supported by the resource.

Code examples

Note: The following examples do not configure the request to include authentication, which is required for most endpoints.

Python example

This example requires the third-party Requests library.

import requests
url = 'https://example.zendesk.com/api/v2/users.json?page[size]=100'
while url:    response = requests.get(url)    data = response.json()
    for user in data['users']:        print(user['name'])
    if data['meta']['has_more']:        url = data['links']['next']    else:        url = None

Ruby example

require 'net/http'require 'json'
url = URI('https://example.zendesk.com/api/v2/users.json?page[size]=100')
while url do  request = Net::HTTP::Get.new(url)
  response = Net::HTTP.start(url.hostname, url.port, use_ssl: true) do |http|    http.request(request)  end
  data = JSON.parse(response.body)
  data['users'].each { |user| puts user['name'] }
  url = if data['meta']['has_more']    URI(data['links']['next'])  else    nil  endend