Pagination in OpenAPI
Describing a collection of resources in an API may be simple at first, but certain features like pagination can make it more complex. Pagination is a common requirement for APIs that return large sets of data, which allows for a subsection of a collection to be returned (maybe only 20-100 resources) to avoid overwhelming the server, the client, and all the network components in between.
This is usually implemented as a query parameter, such as ?page=1 or
?cursor=abc123, so that a client can request a specific page of results. The
API can then return a subset of the data, and depending on the specific
pagination strategy used there could also be metadata about the total number of
items and the total number of pages. That metadata allows a client to display
pagination controls in the interface such as “Page 1”, “Page 2”, or simply
“Next” and “Previous” buttons.
You can learn more about various approaches to pagination in the API design guide here.
This guide will show you how to implement pagination in OpenAPI, regardless of which strategy the API has chosen.
Pagination with query parameters
Query parameters are a common way to implement pagination in APIs. The most
common query parameters for pagination are page and limit, which specify the
page number and the number of items per page, respectively.
This is a common approach for paginating through large sets of data, and is
often used in REST APIs.
paths:
/stations:
get:
summary: Get a list of train stations
description: Returns a paginated and searchable list of all train stations.
operationId: get-stations
parameters:
- name: page
in: query
description: The page number to return
required: false
schema:
type: integer
minimum: 1
default: 1
example: 1
- name: limit
in: query
description: The number of items to return per page
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 10
example: 10Adding the page and limit query parameters advertises to the API consumers
that the API supports pagination. Pagination is mentioned in the description
too, increasing the chance of it being noticed by any API client developers.
The page parameter specifies the page number the API should return, and the
optional limit parameter specifies the number of items to return for that
page. This is helpful for mobile apps that want to return a grid of data to the
user. For example a 3x3 grid of items, which would require 9 items to be
returned, instead of the default 10 giving the user a blank space in the grid.
Describing pagination metadata
When implementing pagination, a common practice is to include metadata in the response to provide information about the total number of items, the current page, and the total number of pages. This metadata can be included in the response body, or sometimes is done with custom HTTP headers (which is frowned upon but done anyway).
Using a meta object in an array would not work, so APIs often wrap the
collection with an “envelope” which might be something like data.
{
"data": [
...
],
"meta": {
"page": 2,
"size": 10,
"total_pages": 100
}If the API is doing this, the response can be described with the following OpenAPI:
responses:
'200':
description: A paginated list of train stations.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Station'
meta:
type: object
properties:
page:
type: integer
example: 2
size:
type: integer
example: 10
total_pages:
type: integer
example: 100This meta object can be defined in components and referenced to avoid repeating it
in every endpoint.
Learn more about components here.
Describing pagination with response links
Adding a query parameter to the API is a good start, but asking API clients to construct URLs from little bits of data is always confusing and a recipe for disaster. REST APIs offer the ability to send links which can be used to crawl the API following links like a browser would.
If the API supports pagination links they
need to be described so clients can use them. The most common way to do this is to
include a links object in the response.
{
"data": [
...
],
"links": {
"self": "https://api.example.com/stations?page=2",
"next": "https://api.example.com/stations?page=3",
"prev": "https://api.example.com/stations?page=1"
}
}That links object can be described in OpenAPI like using the following approach:
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/Wrapper-Collection'
- properties:
data:
type: array
items:
$ref: '#/components/schemas/Station'
- properties:
links:
allOf:
- $ref: '#/components/schemas/Links-Self'
- $ref: '#/components/schemas/Links-Pagination'
components:
Station:
description: A train station.
type: object
properties:
id:
type: string
format: uuid
description: Unique identifier for the station.
examples:
- efdbb9d1-02c2-4bc3-afb7-6788d8782b1e
- b2e783e1-c824-4d63-b37a-d8d698862f1d
# ... snip ...
Links-Self:
description: The link to the current resource.
type: object
properties:
self:
type: string
format: uri
Links-Pagination:
description: Links to the next and previous pages of a paginated response.
type: object
properties:
next:
type: string
format: uri
prev:
type: string
format: uri
Wrapper-Collection:
type: object
properties:
data:
description: The wrapper for a collection is an array of objects.
type: array
items:
type: object
links:
description: A set of hypermedia links which serve as controls for the client.
type: object
readOnly: trueIn this example, the links object contains three links: self, next, and
prev. The self link points to the current page of results, while the next
and prev links point to the next and previous pages of results, respectively.
The links object is described in the OpenAPI specification using the links
object, which is a common way to describe hypermedia links in OpenAPI.
Describing pagination with HTTP headers
Some APIs use custom HTTP headers to provide pagination information instead of trying to wedge the information into the response body and using data and meta. This has the benefit of keeping the JSON clean and tidy, but can be confusing as there is no standard for pagination headers. The Link header is a standard HTTP header that can be used to provide links for general HATEOAS purposes but also for pagination specifically, but it does not have anywhere to pass pagination metadata about numbers of pages.
For example, an API might use something like X-Total-Count header to indicate the total number of items in
the collection, and the X-Page and X-Per-Page headers to indicate the
current page and the number of items per page, then next and previous links in the Link header.
paths:
/stations:
get:
summary: Get a list of train stations
description: Returns a paginated and searchable list of all train stations.
operationId: get-stations
responses:
'200':
description: A paginated list of train stations.
headers:
X-Total-Count:
description: The total number of items in the collection.
schema:
type: integer
example: 1000
X-Page:
description: The current page number.
schema:
type: integer
example: 2
X-Per-Page:
description: The number of items per page.
schema:
type: integer
example: 10
Links:
description: A set of hypermedia links which serve as controls for the client.
type: string
example: |
<https://api.example.com/stations?page=2>; rel="self",
<https://api.example.com/stations?page=3>; rel="next",
<https://api.example.com/stations?page=1>; rel="prev"
However the API is doing pagination, OpenAPI can describe it. The most important thing is to be consistent and clear about how pagination works in the API reference documentation, and if possible write a custom guide for explaining pagination in an API more specifically so that API consumers can get it right.
Speakeasy SDK pagination
Speakeasy SDKs support pagination out of the box, and can be configured to automatically handle pagination for any API, allowing clients to focus on working with data instead of learning about specific pagination strategies.
To configure pagination, add the x-speakeasy-pagination extension to the OpenAPI description:
/stations:
get:
parameters:
- name: page
in: query
schema:
type: integer
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
type: integer
required:
- data
x-speakeasy-pagination:
type: offsetLimit
inputs:
- name: page
in: parameters
type: page
outputs:
results: $.dataThe x-speakeasy-pagination configuration supports offsetLimit, cursor, and url implementations of pagination, and allows the generated SDKs to extract the proper response data from the API instead of having to split up data and metadata manually.
Learn more about pagination with Speakeasy SDKs here.
Last updated on