Skip to main content

Pagination

Most list endpoints return paginated responses to handle large datasets efficiently. This guide covers how to work with paginated results.

Pagination Types

The API supports two pagination strategies:

TypeBest ForParameters
Offset-basedSmall datasets, random accesspage, limit
Cursor-basedLarge datasets, real-time datacursor, limit

Offset-Based Pagination

Request Parameters

ParameterTypeDefaultMaxDescription
pageinteger1-Page number (1-indexed)
limitinteger20100Items per page

Example Request

curl -X GET "https://platform.powerverse.com/inventory-service/sites?page=2&limit=25" \
-H "Authorization: Bearer YOUR_TOKEN"

Response Format

{
"data": [
{ "id": "sit_026", "name": "Site 26" },
{ "id": "sit_027", "name": "Site 27" }
],
"pagination": {
"page": 2,
"limit": 25,
"total": 150,
"total_pages": 6,
"has_next": true,
"has_prev": true
}
}
def fetch_all_sites(api_client):
"""Fetch all sites across all pages."""
all_sites = []
page = 1

while True:
response = api_client.get('/inventory-service/sites', params={
'page': page,
'limit': 100
})

all_sites.extend(response['data'])

if not response['pagination']['has_next']:
break

page += 1

return all_sites

Cursor-Based Pagination

Cursor pagination is recommended for large or frequently changing datasets.

Request Parameters

ParameterTypeDescription
cursorstringOpaque cursor from previous response
limitintegerItems per page (max 100)

Example Request

# First request (no cursor)
curl -X GET "https://platform.powerverse.com/asset-controls/events?limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"

# Subsequent requests (with cursor)
curl -X GET "https://platform.powerverse.com/asset-controls/events?cursor=eyJpZCI6MTAwfQ&limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"

Response Format

{
"data": [
{ "id": "evt_1", "type": "session_started", "status": "completed" },
{ "id": "evt_2", "type": "schedule_triggered", "status": "pending" }
],
"pagination": {
"cursor": "eyJpZCI6MTAwfQ",
"next_cursor": "eyJpZCI6MTUwfQ",
"has_next": true,
"limit": 50
}
}
def fetch_all_events(api_client):
"""Fetch all events using cursor pagination."""
all_events = []
cursor = None

while True:
params = {'limit': 100}
if cursor:
params['cursor'] = cursor

response = api_client.get('/asset-controls/events', params=params)
all_events.extend(response['data'])

if not response['pagination']['has_next']:
break

cursor = response['pagination']['next_cursor']

return all_events

Sorting

Combine pagination with sorting:

# Sort by creation date, newest first
curl -X GET "https://platform.powerverse.com/inventory-service/assets?sort=-created_at&limit=25" \
-H "Authorization: Bearer YOUR_TOKEN"

Sort Syntax

SyntaxDescription
fieldAscending order
-fieldDescending order
field1,-field2Multiple sort fields

Sortable Fields

Each endpoint documents its sortable fields. Common ones include:

  • created_at - Creation timestamp
  • updated_at - Last update timestamp
  • name - Alphabetical by name
  • status - By status value

Filtering

Combine pagination with filters:

curl -X GET "https://platform.powerverse.com/inventory-service/assets?\
status=active&\
created_at[gte]=2024-01-01&\
limit=25" \
-H "Authorization: Bearer YOUR_TOKEN"

Filter Operators

OperatorDescriptionExample
eqEquals (default)status=active
neNot equalsstatus[ne]=decommissioned
gtGreater thancapacity[gt]=100
gteGreater or equalcreated_at[gte]=2024-01-01
ltLess thancapacity[lt]=1000
lteLess or equalcreated_at[lte]=2024-12-31
inIn liststatus[in]=active,maintenance

Best Practices

1. Use Appropriate Page Sizes

// ❌ Too small - many requests
const response = await api.get('/inventory-service/assets', { limit: 5 });

// ❌ Too large - slow responses
const response = await api.get('/inventory-service/assets', { limit: 1000 });

// ✅ Balanced
const response = await api.get('/inventory-service/assets', { limit: 50 });

2. Don't Rely on Total Count for Large Datasets

For performance, total may be approximate or omitted for very large datasets:

{
"pagination": {
"page": 1,
"limit": 100,
"total": null,
"has_next": true
}
}

3. Handle Empty Pages

async function processAssets(callback) {
let page = 1;

while (true) {
const response = await api.get('/inventory-service/assets', { page });

// Handle empty results
if (response.data.length === 0) {
break;
}

for (const asset of response.data) {
await callback(asset);
}

if (!response.pagination.has_next) {
break;
}

page++;
}
}

4. Use Cursor Pagination for Real-Time Data

Offset pagination can miss or duplicate items when data changes:

// ❌ With offset pagination, if a new item is inserted while paginating,
// you might see duplicates or miss items

// ✅ Cursor pagination maintains consistent position
let cursor = null;
while (true) {
const response = await api.get('/asset-controls/events', { cursor });
// Process events...
cursor = response.pagination.next_cursor;
if (!response.pagination.has_next) break;
}

5. Implement Parallel Fetching (When Order Doesn't Matter)

async function fetchAllSitesParallel(totalPages) {
const pagePromises = [];

for (let page = 1; page <= totalPages; page++) {
pagePromises.push(
api.get('/inventory-service/sites', { page, limit: 100 })
);
}

const responses = await Promise.all(pagePromises);
return responses.flatMap(r => r.data);
}

Some endpoints provide pagination links in headers:

HTTP/1.1 200 OK
Link: <https://platform.powerverse.com/inventory-service/sites?page=3>; rel="next",
<https://platform.powerverse.com/inventory-service/sites?page=1>; rel="prev",
<https://platform.powerverse.com/inventory-service/sites?page=10>; rel="last"

Parse and use these links:

function parseLinkHeader(header) {
if (!header) return {};

return header.split(',').reduce((links, part) => {
const [url, rel] = part.split(';');
const match = rel.match(/rel="(.+)"/);
if (match) {
links[match[1]] = url.trim().slice(1, -1);
}
return links;
}, {});
}

Next Steps