Pagination
Many APIs return large datasets split across multiple pages. APIMatic supports pagination in generated SDKs through a flexible configuration mechanism in the OpenAPI specification. Developers can use a simple and consistent SDK interface to navigate through paginated results, regardless of the underlying pagination style used by the API.
Pagination Configuration in OpenAPI
Pagination in APIMatic is configured using the pagination OpenAPI extension. This extension allows specifying how to send pagination inputs in the request and where to extract results or tokens from the response. All pagination configuration fields accept JSON Pointers, enabling fine-grained customization of how request parameters and response structures are interpreted.
Example Usage
Once the OpenAPI pagination configuration is in place, the generated SDKs provide an easy-to-use interface for consuming paginated responses. The SDKs abstract away the logic of computing page tokens, building follow-up requests, and iterating over datasets.
- Java
- .NET
- Python
- Ruby
- Async
- Sync
For asynchronous API calls, the endpoint function will return an instance of PagedFlux
.
Integer page = 1;
Integer size = 25;
PagedFlux<Item, PagedResponse<Item, Page>> result = controller.fetchDataAsync(page, size);
// Iterating over items in all the pages.
result.subscribe(
item -> System.out.println(item),
error -> _error.printStackTrace());
// Iterating over all the pages.
result.pages().subscribe(
pagedResponse -> {
// Iterating over items in the current page.
pagedResponse.getItems().forEach(item -> System.out.println(item));
// Extracting paged response body.
System.out.println(pagedResponse.getResult());
// Extracting paged response headers.
System.out.println(pagedResponse.getHeaders().asSimpleMap());
// Extracting paged response status code.
System.out.println(pagedResponse.getStatusCode());
},
error -> _error.printStackTrace());
In this example:
- result: The paginated response from the endpoint, behaves as an
Flux<Item>
if subscribed directly. - result.pages(): An Flux for subscribing pages directly. Returns
Flux<PagedResponse<Item, Page>>
. - pagedResponse.getItems(): Returns the list of items in the current page.
- pagedResponse.getResult(): The actual instance of the current page with type
Page
. - pagedResponse.getHeaders(): The Http headers returned along with each page.
- pagedResponse.getStatusCode(): The Http status code returned along with each page.
- Error Handling: Provides the error handler lambda function for errors like
ApiException
andIOException
.
Learn more about Flux.
For synchronous API calls, the endpoint function will return an instance of PagedIterable
.
Integer page = 1;
Integer size = 25;
PagedIterable<Item, PagedResponse<Item, Page>> result = controller.fetchData(page, size);
try {
// Iterating over items in all the pages.
for (PagedSupplier<Item> itemSupplier : result) {
System.out.println(itemSupplier.get());
}
} catch (ApiException | IOException e) {
e.printStackTrace();
}
try {
// Iterating over all the pages.
for (PagedSupplier<PagedResponse<Item, Page>> pageSupplier : result.pages()) {
PagedResponse<Item, Page> pagedResponse = pageSupplier.get();
// Iterating over items in the current page.
pagedResponse.getItems().forEach(item -> System.out.println(item));
// Extracting paged response body.
System.out.println(pagedResponse.getResult());
// Extracting paged response headers.
System.out.println(pagedResponse.getHeaders().asSimpleMap());
// Extracting paged response status code.
System.out.println(pagedResponse.getStatusCode());
}
} catch (ApiException | IOException e) {
e.printStackTrace();
}
In this example:
- result: The paginated response from the endpoint, behaves as an
Iterable<PagedSupplier<Item>>
if used in a loop. - itemSupplier.get(): Returns the stored
Item
instance or throw anApiException
orIOException
. - result.pages(): An iterator for traversing pages manually. Returns
Iterable<PagedSupplier<PagedResponse<Item, Page>>>
. - pageSupplier.get(): Returns the stored
PagedResponse<Item, Page>
instance or throw anApiException
orIOException
. - pagedResponse.getItems(): Returns the list of items in the current page.
- pagedResponse.getResult(): The actual instance of the current page with type
Page
. - pagedResponse.getHeaders(): The Http headers returned along with each page.
- pagedResponse.getStatusCode(): The Http status code returned along with each page.
- Error Handling: Wrap pagination logic in
try-catch
blocks to catch and handleApiException
andIOException
.
- Async
- Sync
For asynchronous API calls, the endpoint function will return an instance of AsyncPageable
.
int? page = 1;
int? size = 25;
try
{
AsyncPageable<TItem, BasePagedResponse<TItem, TPage>> result = controller.FetchDataAsync(
page,
size
);
// Iterating over items in all the pages.
await foreach (var item in result)
{
Console.WriteLine(item);
}
// Iterating over all the pages.
await foreach (var pagedResponse in result.GetPagesAsync())
{
// Iterating over items in the current page.
foreach (var item in pagedResponse.Items)
{
Console.WriteLine(item);
}
// Extracting paged response body.
Console.WriteLine(pagedResponse.Data);
// Extracting paged response headers.
Console.WriteLine(pagedResponse.Headers);
// Extracting paged response status code.
Console.WriteLine(pagedResponse.StatusCode);
}
}
catch (ApiException e)
{
// Handle exceptions such as API errors or connectivity issues.
Console.WriteLine(e.Message);
}
In this example:
result
: The paginated response from the endpoint, behaves as anIAsyncEnumerable<TItem>
if iterated directly.result.GetPagesAsync()
: Returns anIAsyncEnumerable<BasePagedResponse<TItem, TPage>>
to iterate over pages.pagedResponse.Items
: Returns theIEnumerable<TItem>
of items in the current page.pagedResponse.Data
: The actual instance of the current page with typeTPage
.pagedResponse.Headers
: The Http headers returned along with each page.pagedResponse.StatusCode
: The Http status code returned along with each page.- Error Handling: Wraps pagination logic in a
try-catch
block to catchApiException
and handle errors.
For synchronous API calls, the endpoint function will return an instance of Pageable
.
int? page = 1;
int? size = 25;
try
{
Pageable<TItem, BasePagedResponse<TItem, TPage>> result = controller.FetchData(
page,
size
);
// Iterating over items in all the pages.
foreach (var item in result)
{
Console.WriteLine(item);
}
// Iterating over all the pages.
foreach (var pagedResponse in result.GetPagesAsync())
{
// Iterating over items in the current page.
foreach (var item in pagedResponse.Items)
{
Console.WriteLine(item);
}
// Extracting paged response body.
Console.WriteLine(pagedResponse.Data);
// Extracting paged response headers.
Console.WriteLine(pagedResponse.Headers);
// Extracting paged response status code.
Console.WriteLine(pagedResponse.StatusCode);
}
}
catch (ApiException e)
{
// Handle exceptions such as API errors or connectivity issues.
Console.WriteLine(e.Message);
}
In this example:
result
: The paginated response from the endpoint, behaves as anIEnumerable<TItem>
when iterated.result.GetPages()
: Returns anIEnumerable<BasePagedResponse<TItem, TPage>>
for iterating over full pages.pagedResponse.Items
: Returns theIEnumerable<TItem>
of items in the current page.pagedResponse.Data
: The actual instance of the current page with typeTPage
.pagedResponse.Headers
: The Http headers returned along with each page.pagedResponse.StatusCode
: The Http status code returned along with each page.- Error Handling: Wraps pagination logic in a
try-catch
block to catchApiException
and handle errors.
# Initial request to fetch the first page of orders with optional pagination inputs.
result = client.orders.list_orders(page=1, size=25)
# Iterate over all items across all pages transparently using the iterator interface.
try:
for item in result: # Iterates item-by-item across all paginated responses.
print(item)
except APIException as e:
print(f"API error: {e}")
# Alternatively, manually iterate page by page and then over items within each page.
try:
for page in result.pages(): # Iterates one API page at a time.
print(page.body) # Accesses raw page body content.
print(page.status_code) # Prints HTTP status code of the page.
print(page.headers) # Prints HTTP headers of the page.
for item in page.items(): # Iterates items within the current page.
print(item)
except APIException as e:
print(f"API error: {e}")
In this example:
- result: The paginated response from the endpoint, behaves as an iterator if used in a loop.
- result.pages(): An iterator for traversing pages manually.
- page.body: The full deserialized response body of the current page.
- page.status_code: The Http status code returned along with each page.
- page.headers: The Http headers returned along with each page.
- page.items(): Returns the list of items in the current page.
- Error Handling: Wrap pagination logic in
try
blocks to catch and handleAPIException
.
# Initial request to fetch the first page of orders with optional pagination inputs.
result = client.transaction.list_orders(
page: 1,
size: 25
)
# Iterate over all items across all pages transparently using the iterator interface.
begin
result.each do |item| # Iterates item-by-item across all paginated responses.
puts item
end
rescue APIException => e
puts "API error: #{e}"
end
# Alternatively, manually iterate page by page and then over items within each page.
begin
result.pages.each do |page| # Iterates one API page at a time.
puts page.data # Accesses raw page body content.
puts page.status_code # Prints HTTP status code of the page.
puts page.headers # Prints HTTP headers of the page.
page.items.each do |item| # Iterates items within the current page.
puts item
end
end
rescue APIException => e
puts "API error: #{e}"
end
In this example:
- result: The paginated response from the endpoint. It behaves as an enumerable, allowing direct iteration over all items across pages using
.each
. - result.pages: Returns an enumerable for traversing each page of the paginated response manually.
- page.data: The full deserialized response body of the current page.
- page.status_code: The Http status code returned for the current page.
- page.headers: The Http headers returned for the current page.
- page.items: Returns the list of items contained in the current page.
- Error Handling: Wrap pagination logic in begin ... rescue blocks to catch and handle
APIException
errors gracefully.
Page Meta Data
When pagination is enabled for an SDK, each page of the API response will include metadata in addition to the actual response data. This metadata helps developers understand which request parameters were responsible for generating that specific page of results.
To include this metadata, the response is wrapped in a PagedResponse type, which may take one of the following forms:
- CursorPagedResponse
- LinkPagedResponse
- NumberPagedResponse
- OffsetPagedResponse
The following code samples illustrate how to retrieve metadata from each variant of PagedResponse, processing it page by page.
- Java
- .NET
- Python
- Ruby
- CursorPagedResponse
- LinkPagedResponse
- NumberPagedResponse
- OffsetPagedResponse
String cursor = "id_123";
Integer limit = 25;
PagedFlux<Item, CursorPagedResponse<Item, Page>> result = controller.fetchDataAsync(cursor, limit);
// Iterating over all the pages and extracting cursor value that's used to fetch each page.
result.pages().subscribe(
pagedResponse -> {
System.out.println(pagedResponse.getNextCursor());
});
In this example:
- pagedResponse.getNextCursor(): The next cursor from the previous response used to fetch the current page.
Integer page = 1;
Integer size = 25;
PagedFlux<Item, LinkPagedResponse<Item, Page>> result = controller.fetchDataAsync(page, size);
// Iterating over all the pages and extracting next link value that's used to fetch each page.
result.pages().subscribe(
pagedResponse -> {
System.out.println(pagedResponse.getNextLink();
});
In this example:
- pagedResponse.getNextLink(): The next link from the previous response used to fetch the current page.
Integer page = 1;
Integer size = 25;
PagedFlux<Item, NumberPagedResponse<Item, Page>> result = controller.fetchDataAsync(page, size);
// Iterating over all the pages and extracting page number of each page.
result.pages().subscribe(
pagedResponse -> {
System.out.println(pagedResponse.getPageNumber());
});
In this example:
- pagedResponse.getPageNumber(): Page number used to fetch the current page.
Integer offset = 0;
Integer limit = 25;
PagedFlux<Item, OffsetPagedResponse<Item, Page>> result = controller.fetchDataAsync(offset, limit);
// Iterating over all the pages and extracting offset of the first item of each page.
result.pages().subscribe(
pagedResponse -> {
System.out.println(pagedResponse.getOffset());
});
In this example:
- pagedResponse.getOffset(): Offset used to fetch the current page.
- CursorPagedResponse
- LinkPagedResponse
- NumberPagedResponse
- OffsetPagedResponse
string cursor = "id_123";
int? limit = 25;
AsyncPageable<TItem, CursorPagedResponse<TItem, TPage>> result = controller.FetchDataAsync(
cursor,
limit
);
// Iterating over all the pages and extracting cursor value that's used to fetch each page.
await foreach (var pagedResponse in result.GetPagesAsync())
{
Console.WriteLine(pagedResponse.NextCursor);
}
In this example:
pagedResponse.NextCursor
: The next cursor from the previous response used to fetch the current page.
int? page = 1;
int? size = 25;
AsyncPageable<TItem, LinkPagedResponse<TItem, TPage>> result = controller.FetchDataAsync(
page,
size
);
// Iterating over all the pages and extracting next link value that's used to fetch each page.
await foreach (var pagedResponse in result.GetPagesAsync())
{
Console.WriteLine(pagedResponse.NextLink);
}
In this example:
pagedResponse.NextLink
: The next link from the previous response used to fetch the current page.
int? page = 1;
int? size = 25;
AsyncPageable<TItem, NumberPagedResponse<TItem, TPage>> result = controller.FetchDataAsync(
page,
size
);
// Iterating over all the pages and extracting page number of each page.
await foreach (var pagedResponse in result.GetPagesAsync())
{
Console.WriteLine(pagedResponse.PageNumber);
}
In this example:
pagedResponse.PageNumber
: Page number used to fetch the current page.
int? offset = 0;
int? limit = 25;
AsyncPageable<TItem, OffsetPagedResponse<TItem, TPage>> result = controller.FetchDataAsync(
offset,
limit
);
// Iterating over all the pages and extracting offset of the first item of each page.
await foreach (var pagedResponse in result.GetPagesAsync())
{
Console.WriteLine(pagedResponse.Offset);
}
In this example:
pagedResponse.Offset
: Offset used to fetch the current page.
- CursorPagedResponse
- LinkPagedResponse
- NumberPagedResponse
- OffsetPagedResponse
result = client.orders.list_orders(cursor="id_123", limit=25)
# Iterating over all the pages and extracting cursor value that's used to fetch each page.
for page in result.pages():
print(page.next_cursor)
In this example:
- page.next_cursor: The next cursor from the previous response used to fetch the current page.
result = client.orders.list_orders(page=1, size=25)
# Iterating over all the pages and extracting next link value that's used to fetch each page.
for page in result.pages():
print(page.next_link)
In this example:
- page.next_link: The next link from the previous response used to fetch the current page.
result = client.orders.list_orders(page=1, size=25)
# Iterating over all the pages and extracting page number of each page.
for page in result.pages():
print(page.page_number)
In this example:
- page.page_number: Page number used to fetch the current page.
result = client.orders.list_orders(offset=0, limit=25)
# Iterating over all the pages and extracting offset of the first item of each page.
for page in result.pages():
print(page.offset)
In this example:
- page.offset: Offset used to fetch the current page.
- CursorPagedResponse
- LinkPagedResponse
- NumberPagedResponse
- OffsetPagedResponse
result = client.orders.list_orders(cursor: 'id_123', limit: 25)
# Iterating over all the pages and extracting cursor value that's used to fetch each page.
result.pages.each do |page|
puts page.next_cursor
end
In this example:
- page.next_cursor: The next cursor from the previous response used to fetch the current page.
result = client.orders.list_orders(page: 1, size: 25)
# Iterating over all the pages and extracting next link value that's used to fetch each page.
result.pages.each do |page|
puts page.next_link
end
In this example:
- page.next_link: The next link from the previous response used to fetch the current page.
result = client.orders.list_orders(page: 1, size: 25)
# Iterating over all the pages and extracting page number of each page.
result.pages.each do |page|
puts page.page_number
end
In this example:
- page.page_number: Page number used to fetch the current page.
result = client.orders.list_orders(offset: 0, limit: 25)
# Iterating over all the pages and extracting offset of the first item of each page.
result.pages.each do |page|
puts page.offset
end
In this example:
- page.offset: Offset used to fetch the current page.
Benefits
- Uniform SDK Experience: Same interface regardless of pagination strategy.
- No Extra Logic Needed: No need to parse tokens or handle loop termination manually.
- OpenAPI-Driven: Automatically applied based on
x-pagination
configuration. - Customizability: Flexible placement of input and output using JSON Pointers.