Pagination guide
A comprehensive guide to managing large data sets with Recurly’s built-in pagination, including request parameters, response formats, and client library pagers for convenient record retrieval.
Overview
Prerequisites & limitations
- Familiarity with Recurly’s API v3
- Valid Recurly API credentials
- Basic knowledge of RESTful APIs and JSON responses
- Understanding of client library usage (e.g., Python, Ruby, Node.js, etc.)
Definition
Pagination in Recurly allows you to efficiently retrieve large sets of records in small “pages.” By iterating over these pages—either manually or via each client library’s built-in Pager class—you can avoid performance pitfalls and manage your data flows more effectively.
When your site has thousands of records, fetching all records in a single request becomes inefficient and may negatively impact performance. To address this, the Recurly API provides paginated list_* operations, allowing you to retrieve records in manageable chunks across multiple API requests.
Request parameters
Most list_* operations accept the following parameters to control pagination:
- limit (optional): Specifies the maximum number of records to return in each response. The default value may vary depending on the endpoint.
- order (optional): Determines the order in which the records are returned, typically sorted by creation date or another relevant field. Valid values are
asc(ascending) anddesc(descending). - begin_time (optional): Returns only records created after this date. This parameter allows you to filter records by a specific time range.
- end_time (optional): Returns only records created before this date, enabling further control over the range of data returned.
- sort (optional): Specifies the field by which the results are sorted.
Pagination results include metadata in the response to help you manage subsequent API calls, such as:
- next: A URL to retrieve the next set of results.
- has_more: A boolean value indicating whether more records are available beyond the current page.
ImportantThe timestamp specified in the
begin_timeandend_timeparameters will default to UTC if the value does not contain a time zone.The timestamp
2020-01-01will be treated as2020-01-01T00:00:00Zby Recurly API, which may not match your expectations.If the
sortparameter is set toupdated_at, then theordershould likely be set toascto avoid concurrently updated records from being moved behind the cursor and therefore excluded from the results.
The API Reference documentation will provide detailed information about any additional parameters that a particular list_* operation supports.
JSON response format
Each response from Recurly API will contain the following keys in the JSON object that is returned:
| Key | Description |
|---|---|
object | This will always be the value list. |
has_more | Boolean indicating whether there are more pages of responses. |
next | The URL to request the next page of results. |
data | An array of requested records containing between 0 and limit records per-response. |
ImportantThe
nextURL will contain all of the query parameters in the original request, as well as acursorparameter that is used to identify the starting point of the next page of results.
Client library pagers
The client libraries for Recurly API provide a Pager class that provides the capability to iterate over all of the records for a given endpoint without the need to manually perform each pagination request.
An instance of a Pager class is immediately returned when calling any of the list_* methods. A request to Recurly API will only occur when your code begins to iterate over the results or one of the other convenience methods is invoked.
ImportantThe client libraries accept the
idsrequest parameter as an array of values that will be automatically converted to the comma separated string that Recurly API expects.
Iterating with pagers
The primary purpose of the Pager is to provide a mechanism to automatically iterate over all of the records that are available. The Pager will continue to perform requests to Recurly API for more pages of results, as necessary, until the response indicates that there are no more pages available (has_more is false).
ImportantThe
limitparameter will only impact the number of records that are returned in each API page response. ThePagerwill continue to request additional pages until the entire result set has been exhausted.A lower
limitvalue might be preferable to reduce request/response times.
const accounts = client.listAccounts({ limit: 200 })
for await (const account of accounts.each()) {
console.log(account.code)
}accounts = client.list_accounts(limit=200).items()
for account in accounts:
print(account.code)var accounts = client.ListAccounts(limit: 200);
foreach(Account account in accounts)
{
Console.WriteLine(account.Code);
}accounts = @client.list_accounts(limit: 200)
accounts.each do |account|
puts account.code
endQueryParams params = new QueryParams();
params.setLimit(200);
Pager<Account> accounts = client.listAccounts(params);
for (Account account : accounts) {
System.out.println(account.getCode());
}$accounts = $client->listAccounts([ 'limit' => 200 ]);
foreach($accounts as $account) {
echo $account->getCode() . PHP_EOL;
}listParams := &recurly.ListAccountsParams{
Limit: recurly.Int(200),
}
accounts := client.ListAccounts(listParams)
for accounts.HasMore {
err := accounts.Fetch()
if e, ok := err.(*recurly.Error); ok {
fmt.Printf("Failed to retrieve next page: %v", e)
break
}
for _, account := range accounts.Data {
fmt.Println(account.Code)
}
}Total record count
The total number of records for a given set of request parameters can be quickly determined using the count method of the Pager without needing to fetch and iterate over the entire result set. This will perform a HEAD request to Recurly API and return the Recurly-Total-Records header value.
const beginTime = new Date('January 1, 2020')
const accounts = await client.listAccounts({
limit: 200,
sort: 'updated_at',
order: 'asc',
beginTime: beginTime
})
const account = await accounts.first()
console.log(`First Account updated since ${beginTime}: ${account.code}`)
var beginTime = new DateTime(2020, 1, 1);
var accounts = client.ListAccounts(
limit: 200,
sort: "updated_at",
order: "asc",
beginTime: beginTime
);
var account = accounts.First();
Console.WriteLine($"First Account updated since {beginTime}: {account.Code}");var beginTime = new DateTime(2020, 1, 1);
var accounts = client.ListAccounts(
limit: 200,
sort: "updated_at",
order: "asc",
beginTime: beginTime
);
var account = accounts.First();
Console.WriteLine($"First Account updated since {beginTime}: {account.Code}");begin_time = DateTime.new(2020, 1, 1)
accounts = @client.list_accounts(
limit: 200,
sort: 'updated_at',
order: 'asc',
begin_time: begin_time
)
account = accounts.first
puts "First Account updated since #{begin_time}: #{account.code}"DateTime beginTime = new DateTime(2020, 1, 1, 0, 0);
QueryParams params = new QueryParams();
params.setLimit(200);
params.setSort("updated_at");
params.setOrder("asc");
params.setBeginTime(beginTime);
Pager<Account> accounts = client.listAccounts(params);
Account account = accounts.getFirst();
System.out.println("First Account updated since " + beginTime + ": " + account.getCode());$beginTime = new DateTime("2020-01-01 00:00:00");
$accounts = $client->listAccounts([
'limit' => 200,
'sort' => 'updated_at',
'order' => 'asc',
'begin_time' => $beginTime
]);
$account = $accounts->getFirst();
print("First Account updated since {$beginTime->format('Y-m-d')}: {$account->getCode()}");
ImportantThe client library will modify the supplied
limitparameter when performing the request for thefirstrecord to minimize request/response times. Subsequent use of the samePagerinstance will use the originally providedlimitvalue.
Getting the first record
If you are only interested in the first record that matches the supplied request parameters, then the first method of the Pager can be used. If there are no matching records, then a null value will be returned.
const beginTime = new Date('January 1, 2020')
const accounts = await client.listAccounts({
limit: 200,
sort: 'updated_at',
order: 'asc',
beginTime: beginTime
})
const account = await accounts.first()
console.log(`First Account updated since ${beginTime}: ${account.code}`)begin_time = datetime(2020, 1, 1, 0, 0, 0)
accounts = client.list_accounts(
limit=200,
sort='updated_at',
order='asc',
begin_time=begin_time
)
account = accounts.first()
print(f"First Account updated since {begin_time}: {account.code}")var beginTime = new DateTime(2020, 1, 1);
var accounts = client.ListAccounts(
limit: 200,
sort: "updated_at",
order: "asc",
beginTime: beginTime
);
var account = accounts.First();
Console.WriteLine($"First Account updated since {beginTime}: {account.Code}");begin_time = DateTime.new(2020, 1, 1)
accounts = @client.list_accounts(
limit: 200,
sort: 'updated_at',
order: 'asc',
begin_time: begin_time
)
account = accounts.first
puts "First Account updated since #{begin_time}: #{account.code}"DateTime beginTime = new DateTime(2020, 1, 1, 0, 0);
QueryParams params = new QueryParams();
params.setLimit(200);
params.setSort("updated_at");
params.setOrder("asc");
params.setBeginTime(beginTime);
Pager<Account> accounts = client.listAccounts(params);
Account account = accounts.getFirst();
System.out.println("First Account updated since " + beginTime + ": " + account.getCode());$beginTime = new DateTime("2020-01-01 00:00:00");
$accounts = $client->listAccounts([
'limit' => 200,
'sort' => 'updated_at',
'order' => 'asc',
'begin_time' => $beginTime
]);
$account = $accounts->getFirst();
print("First Account updated since {$beginTime->format('Y-m-d')}: {$account->getCode()}");
ImportantThe client library will modify the supplied
limitparameter when performing the request for thefirstrecord to minimize request/response times. Subsequent use of the samePagerinstance will use the originally providedlimitvalue.
Getting the last record
While there is not an explicity defined last method, the equivalent request can be accomplished by changing the order parameter of the request when using the first method.
If you are only interested in the first record that matches the supplied request parameters, then the first method of the Pager can be used. If there are no matching records, then a null value will be returned.
const endTime = new Date('January 1, 2020')
const accounts = await client.listAccounts({
limit: 200,
sort: 'updated_at',
order: 'desc',
endTime: endTime
})
const account = await accounts.first()
console.log(`Last Account updated before ${endTime}: ${account.code}`)end_time = datetime(2020, 1, 1, 0, 0, 0)
accounts = client.list_accounts(
limit=200,
sort='updated_at',
order='desc',
end_time=end_time
)
account = accounts.first()
print(f"Last Account updated before {end_time}: {account.code}")var endTime = new DateTime(2020, 1, 1);
var accounts = client.ListAccounts(
limit: 200,
sort: "updated_at",
order: "desc",
endTime: endTime
);
var account = accounts.First();
Console.WriteLine($"Last Account updated before {endTime}: {account.Code}");end_time = DateTime.new(2020, 1, 1)
accounts = @client.list_accounts(
limit: 200,
sort: 'updated_at',
order: 'desc',
end_time: end_time
)
account = accounts.first
puts "Last Account updated before #{end_time}: #{account.code}"DateTime endTime = new DateTime(2020, 1, 1, 0, 0);
QueryParams params = new QueryParams();
params.setLimit(200);
params.setSort("updated_at");
params.setOrder("desc");
params.setEndTime(endTime);
Pager<Account> accounts = client.listAccounts(params);
Account account = accounts.getFirst();
System.out.println("Last Account updated before " + endTime + ": " + account.getCode());$endTime = new DateTime("2020-01-01 00:00:00");
$accounts = $client->listAccounts([
'limit' => 200,
'sort' => 'updated_at',
'order' => 'desc',
'end_time' => $endTime
]);
$account = $accounts->getFirst();
print("Last Account updated before {$endTime->format('Y-m-d')}: {$account->getCode()}");
ImportantThe client library will adjust the supplied
limitparameter when performing the request for thefirstrecord to minimize request/response times. For subsequent requests using the samePagerinstance, the originally providedlimitvalue will be used.
Updated 4 days ago