Guide to API Pagination

Last Updated Aug 21, 2021
Emma Jagger

Engineer, maker, Google alumna, CMU grad

API Pagination turns big archives of data into smaller, more digestible pieces. Clicking through an archive of pictures, or turning the page of a book, are examples of pagination. How is this important in API structure? When we use a GET API request to request information from a server via API endpoint, there could be thousands of entries in the returned JSON file. The API response sending us thousands of entries at once is a drain of resources and a waste of our time. We want to search through a database a little bit at a time, and paging helps us query databases efficiently. From the API owner's point of view, including pagination is a helpful tool to their API users, and prevents underfetching and overfetching of data. This results in less resource usage and happier API users. This guide to pagination will cover a few methods of pagination for your API.

Pagination Methods

There are a few different methods of pagination you can use depending on your API's needs. Consider how your customer will consume this data, and help them navigate it with intelligent pagination.

Offset Pagination

Offset pagination uses the `limit` and `offset` commands already present in the SQL library as query parameters. For example:`GET /paintings?offset=0&limit=10``offset` tells the server the number of items that should be skipped, while `limit` indicates the number of items to be returned. So, this will search ten paintings at a time, and skip 0 of them. One issue with this approach is the outcome if someone removes a record from the data set. This throws off your whole search, and you will miss one item.

Keyset Pagination

Keyset pagination takes a cue from SQL database searching. It passes a query parameter with a timestamp to thoroughly paginate through an API call.

  • The client requests most recent items with `GET /items?limit=20`
  • Upon clicking the next page (first page to second page), the query finds the minimum created date of 2019–01–20T00:00:00 (the first 20 results). This is then used to create a query `limit` filter for the next page:`GET /items?limit=20&created:lte:2019-01-20T00:00:00`

This will paginate until the last page is reached.

Seek Pagination

Seek pagination returns consistent ordering even when new items are added to the table. We add `after_id` or `start_id` URL parameters.

  • Client makes request for most recent items: `GET /items?limit=20`
  • Upon clicking the next page, client finds the last id of ‘20’ from previously returned results. and then makes second query using it as the starting id: `GET /items?limit=20&after_id=20`
  • Upon clicking the next page, client finds the last id of ‘40’ from previously returned results. and then makes third query using it as the starting id: `GET /items?limit=20&after_id=40`

Cursor Pagination

To use cursor 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 (like Wordpress below) limit this to a maximum of 100.

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:

```json

"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 user can use `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.

Pagination Tutorial

In the Wordpress documentation, Wordpress describes the pagination functionality they include in their APIs. You can imagine they deal with a great deal of API requests, and pagination of reams of blog pages would greatly increase efficiency in their use case. Their solution was to support common query parameters from requests to handle paging.

  • ?page=: specify the page of results to return.    

   For example, /wp/v2/posts?page=2 is the second page of posts results    

   By retrieving /wp/v2/posts, then /wp/v2/posts?page=2, and so on, you may access every available post through the API, one page at a time.

  • ?per_page=: specify the number of records to return in one request, specified as an integer from 1 to 100.    

   For example, /wp/v2/posts?per_page=1 will return only the first post in the collection

  • ?offset=: specify an arbitrary offset at which to start retrieving posts    

   For example, /wp/v2/posts?offset=6 will use the default number of posts per page, but start at the 6th post in the collection    

   ?per_page=5&page=4 is equivalent to ?per_page=5&offset=15

Wordpress also mentions that because large queries can hurt site performance, the number of results returned by `per_page` is capped at 100 records.

The API returns two header fields with every paginated response:

`X-WP-Total`: the total number of records in the collection`X-WP-TotalPages`: the total number of pages encompassing all available records

When you build your API, consider how the user might want to get data, and how your resources can handle them most efficiently, when designing pagination.

API Response Optimization

In addition to pagination, there are other sorting methods your API can use to optimize API responses and limit bloat.

Filtering

The easiest way to add basic filtering to RESTful APIs is with URL parameters. If you have an `/items` endpoint which are items for sale, you can filter via the property name such as `GET /items?state=active` or `GET /items?state=active&seller_id=1234`. This only works for exact matches, we will use ranges if we want to filter a range of results.

Ranges

A "range" header can be specified in the request to limit the number of items returned in a list. The header format must be as follows:

`Range: items = <start> - <end>`

<start> and <end> must be strictly positive numbers to specify the desired range of items, <start> and <end> are inclusive.

Other options for ranges include using LHS brackets with an operator: `price[lte]=200` (return results less than equal to 200) `status[ne]=past` (return results not equal to past). Operators include [regex] for [regular expressions](/what-is-regex.md) or [gte] for "greater than equal to."

Sorting

To enable sorting, add a `sort` or `sort_by` URL parameter that can take a field name as the value.

However, good API designs give the flexibility to specify `ascending` or `descending` order. Like filters, specifying the order requires encoding three components into a key/value pair.

For example: `GET /users?sort_by=asc(email)` and `GET /users?sort_by=desc(email)`

Conclusion

When building your API, keep pagination in mind. Use common variables like `self`, `first`, `next`, and `last` that are widely used by API developers.

Related articles

No items found.
Start using one of Abstract's 10+ API's for free today
Get started