When you're working with lists of resources in the Sprinklr API—cases, messages, accounts, profiles—you'll often encounter datasets too large to return in a single response. Pagination breaks these large datasets into manageable chunks, making your integration faster, more memory-efficient, and more user-friendly.
Imagine requesting all customer service cases from a large enterprise. They might have tens of thousands of cases. If the API tried to return all of them at once:
- The response would be massive - Several megabytes of JSON data
- The request would be slow - Could take 10-30 seconds or timeout entirely
- Your application would struggle - Parsing and processing that much data at once consumes memory
- The user would wait - No results until everything loads
Pagination solves all of these problems by breaking the data into smaller "pages" that you can fetch and process one at a time—or in parallel if you need speed.
Think of pagination like reading a book. You don't need to read all 300 pages at once to start understanding the story. You read one page, then the next, progressively building your understanding. API pagination works the same way.
The Sprinklr API uses offset-based pagination (also called "page number pagination"). This is the most common and straightforward pagination approach.
Here's how it works:
- You specify which page number you want (starting from 0)
- You specify how many items per page (the page size)
- The API returns that page of results plus metadata telling you about the overall dataset
Example:
Dataset: 150 cases total
Your request: page=0, size=20
API returns:
- Items 1-20 (first page)
- Metadata: "You're on page 0 of 8 pages, 150 total items"
Next request: page=1, size=20
- Items 21-40 (second page)
- Metadata: "You're on page 1 of 8 pages, 150 total items"Every paginated endpoint accepts these query parameters:
| Parameter | Type | Default | Range/Constraints | Description |
|---|---|---|---|---|
page | integer | 0 | 0 to totalPages-1 | Page number (0-indexed, first page is 0) |
size | integer | 20 | 1 to 100 | Number of items per page |
sort | string | varies | field,direction | Sort field and direction (asc or desc) |
Node.js:
const fetch = require('node-fetch');
async function getCases(page = 0, size = 20, sort = 'createdTime,desc') {
const url = new URL('https://api-v3.sprinklr.com/api/v3/case/search');
url.searchParams.append('page', page);
url.searchParams.append('size', size);
url.searchParams.append('sort', sort);
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
status: 'OPEN'
})
});
return response.json();
}
// Get first page (20 cases)
const firstPage = await getCases(0, 20);
console.log(`Loaded ${firstPage.data.length} cases`);
console.log(`Total: ${firstPage.pagination.totalElements} cases across ${firstPage.pagination.totalPages} pages`);Python:
import requests
from urllib.parse import urlencode
def get_cases(page=0, size=20, sort='createdTime,desc'):
params = {
'page': page,
'size': size,
'sort': sort
}
response = requests.post(
f'https://api-v3.sprinklr.com/api/v3/case/search?{urlencode(params)}',
headers={
'Authorization': f'Bearer {access_token}',
'Key': api_key,
'Content-Type': 'application/json'
},
json={'status': 'OPEN'}
)
return response.json()
# Get first page (20 cases)
first_page = get_cases(0, 20)
print(f"Loaded {len(first_page['data'])} cases")
print(f"Total: {first_page['pagination']['totalElements']} cases across {first_page['pagination']['totalPages']} pages")PHP:
<?php
function getCases($page = 0, $size = 20, $sort = 'createdTime,desc') {
global $accessToken, $apiKey;
$url = 'https://api-v3.sprinklr.com/api/v3/case/search?' .
http_build_query([
'page' => $page,
'size' => $size,
'sort' => $sort
]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken,
'Key: ' . $apiKey,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['status' => 'OPEN']));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Get first page (20 cases)
$firstPage = getCases(0, 20);
echo "Loaded " . count($firstPage['data']) . " cases\n";
echo "Total: " . $firstPage['pagination']['totalElements'] . " cases across " .
$firstPage['pagination']['totalPages'] . " pages\n";
?>Java:
import java.net.http.*;
import java.net.URI;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class PaginationExample {
private static final ObjectMapper mapper = new ObjectMapper();
public static Map<String, Object> getCases(int page, int size, String sort)
throws Exception {
String url = String.format(
"https://api-v3.sprinklr.com/api/v3/case/search?page=%d&size=%d&sort=%s",
page, size, sort
);
String requestBody = mapper.writeValueAsString(
Map.of("status", "OPEN")
);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Authorization", "Bearer " + accessToken)
.header("Key", apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return mapper.readValue(response.body(), Map.class);
}
public static void main(String[] args) throws Exception {
// Get first page (20 cases)
Map<String, Object> firstPage = getCases(0, 20, "createdTime,desc");
List<Map> data = (List<Map>) firstPage.get("data");
Map<String, Object> pagination = (Map<String, Object>) firstPage.get("pagination");
System.out.println("Loaded " + data.size() + " cases");
System.out.println("Total: " + pagination.get("totalElements") +
" cases across " + pagination.get("totalPages") + " pages");
}
}Every paginated response includes both your data and a pagination object with metadata:
{
"success": true,
"data": [
{
"caseId": "CASE-12345",
"subject": "Customer inquiry about refund",
"status": "OPEN",
"createdTime": "2024-03-01T10:30:00Z"
},
{
"caseId": "CASE-12346",
"subject": "Product delivery delayed",
"status": "OPEN",
"createdTime": "2024-03-01T10:25:00Z"
}
// ... 18 more cases
],
"pagination": {
"page": 0,
"size": 20,
"totalElements": 157,
"totalPages": 8,
"first": true,
"last": false
}
}| Field | Type | Description | Example |
|---|---|---|---|
page | integer | Current page number (0-indexed) | 0 (first page) |
size | integer | Items per page | 20 |
totalElements | integer | Total number of items across all pages | 157 |
totalPages | integer | Total number of pages | 8 |
first | boolean | true if this is the first page | true on page 0 |
last | boolean | true if this is the last page | true on page 7 (for 8 pages) |
Using this metadata:
const response = await getCases(0, 20);
const { data, pagination } = response;
// Check if there are more pages
if (!pagination.last) {
console.log('There are more pages to fetch');
const nextPage = await getCases(pagination.page + 1, pagination.size);
}
// Calculate how many items are on this page
const itemsOnThisPage = data.length;
// Calculate range of items being displayed
const startItem = pagination.page * pagination.size + 1;
const endItem = startItem + itemsOnThisPage - 1;
console.log(`Showing items ${startItem}-${endItem} of ${pagination.totalElements}`);Use the sort parameter to control the order of results. This is crucial for pagination because the sort order determines which items appear on which pages.
Format: field,direction
- field: The property name to sort by
- direction:
asc(ascending, A→Z, 0→9, oldest→newest) ordesc(descending, Z→A, 9→0, newest→oldest)
| Field | Description | Example Values |
|---|---|---|
createdTime | When the item was created | createdTime,desc (newest first) |
modifiedTime | When the item was last updated | modifiedTime,desc |
name | Alphabetical by name | name,asc (A to Z) |
status | By status (alphabetical) | status,asc |
priority | By priority level | priority,desc (high to low) |
Get newest cases first:
const newestFirst = await getCases(0, 20, 'createdTime,desc');
// Returns: March 15, March 14, March 13, ...Get cases alphabetically by subject:
const alphabetical = await getCases(0, 20, 'subject,asc');
// Returns: "Account issue", "Billing question", "Complaint", ...Get recently updated cases:
const recentlyUpdated = await getCases(0, 20, 'modifiedTime,desc');
// Returns cases sorted by last modification timeImportant: Always use the same sort order when fetching multiple pages. If you change the sort order between requests, items might appear on multiple pages or be skipped entirely.
The most common pattern—fetch pages one at a time until you've got everything.
Node.js:
async function getAllCases() {
const allCases = [];
let page = 0;
let hasMore = true;
while (hasMore) {
const response = await getCases(page, 50); // 50 items per page
// Add this page's cases to our collection
allCases.push(...response.data);
// Check if there are more pages
hasMore = !response.pagination.last;
page++;
console.log(`Fetched page ${page}, total cases so far: ${allCases.length}`);
}
console.log(`Complete! Fetched ${allCases.length} cases across ${page} pages`);
return allCases;
}
// Usage
const allCases = await getAllCases();
// Processes all cases: page 1, page 2, page 3, ... until donePython:
def get_all_cases():
all_cases = []
page = 0
has_more = True
while has_more:
response = get_cases(page, 50) # 50 items per page
# Add this page's cases
all_cases.extend(response['data'])
# Check if there are more pages
has_more = not response['pagination']['last']
page += 1
print(f"Fetched page {page}, total cases so far: {len(all_cases)}")
print(f"Complete! Fetched {len(all_cases)} cases across {page} pages")
return all_cases
# Usage
all_cases = get_all_cases()When to use this:
- You need all the data for analysis or reporting
- You're doing a one-time data export
- The total dataset is manageable (under 10,000 items)
When NOT to use this:
- Very large datasets (100,000+ items) - consider streaming instead
- Real-time UI displays - use progressive loading instead
- Rate limit concerns - fetch only what you need
Instead of loading everything into memory, process each page as you fetch it.
Node.js:
async function processAllCases(processFn) {
let page = 0;
let hasMore = true;
let totalProcessed = 0;
while (hasMore) {
const response = await getCases(page, 50);
// Process this page immediately
for (const case of response.data) {
await processFn(case);
totalProcessed++;
}
hasMore = !response.pagination.last;
page++;
console.log(`Processed page ${page}, ${totalProcessed} cases total`);
}
return totalProcessed;
}
// Usage - process each case as we fetch it
const processed = await processAllCases(async (caseItem) => {
// Do something with each case
await updateDatabase(caseItem);
await sendToQueue(caseItem);
});
console.log(`Processed ${processed} cases`);Python:
def process_all_cases(process_fn):
page = 0
has_more = True
total_processed = 0
while has_more:
response = get_cases(page, 50)
# Process this page immediately
for case in response['data']:
process_fn(case)
total_processed += 1
has_more = not response['pagination']['last']
page += 1
print(f"Processed page {page}, {total_processed} cases total")
return total_processed
# Usage
def handle_case(case):
# Do something with each case
update_database(case)
send_to_queue(case)
processed = process_all_cases(handle_case)
print(f"Processed {processed} cases")Benefits:
- Memory efficient - Never holds entire dataset in memory
- Faster time-to-first-result - Start processing immediately
- Better for large datasets - Can handle millions of records
- Easier error recovery - If it fails on page 42, you've already processed pages 1-41
Sometimes you just need one page—like for a UI with page navigation.
Node.js:
async function getCasesForDisplay(pageNumber = 0, pageSize = 20) {
const response = await getCases(pageNumber, pageSize);
return {
cases: response.data,
pagination: {
currentPage: response.pagination.page + 1, // Convert to 1-indexed for display
totalPages: response.pagination.totalPages,
totalItems: response.pagination.totalElements,
hasNext: !response.pagination.last,
hasPrevious: !response.pagination.first
}
};
}
// Usage in a UI
const displayData = await getCasesForDisplay(2, 20); // Page 3 (0-indexed: 2)
console.log(`Page ${displayData.pagination.currentPage} of ${displayData.pagination.totalPages}`);
console.log(`Showing ${displayData.cases.length} cases`);
if (displayData.pagination.hasNext) {
console.log('Next button enabled');
}
if (displayData.pagination.hasPrevious) {
console.log('Previous button enabled');
}Fetch multiple pages at once for faster data retrieval.
Node.js:
async function getPageRange(startPage, endPage, pageSize = 20) {
// Create array of page numbers: [0, 1, 2, 3, 4]
const pages = Array.from(
{ length: endPage - startPage + 1 },
(_, i) => startPage + i
);
// Fetch all pages in parallel
const responses = await Promise.all(
pages.map(page => getCases(page, pageSize))
);
// Combine all results
const allCases = responses.flatMap(response => response.data);
return allCases;
}
// Usage - fetch pages 0-4 in parallel (5 pages = 100 cases if size=20)
const cases = await getPageRange(0, 4, 20);
console.log(`Fetched ${cases.length} cases in parallel`);⚠️ Important considerations:
- Rate limits: Parallel requests count against your rate limit. Don't fetch 100 pages at once.
- Memory: All pages load into memory simultaneously
- Order: Results come back in any order—sort after fetching if order matters
Good practice:
// Fetch 5 pages in parallel (reasonable)
const batch1 = await getPageRange(0, 4, 20);
// Wait a moment, then fetch next batch
await new Promise(resolve => setTimeout(resolve, 1000));
const batch2 = await getPageRange(5, 9, 20);Load pages as the user scrolls or requests more data.
Node.js class for infinite scroll:
class InfiniteScroll {
constructor(fetchFn, pageSize = 20) {
this.fetchFn = fetchFn;
this.pageSize = pageSize;
this.currentPage = 0;
this.allItems = [];
this.hasMore = true;
this.loading = false;
this.totalItems = 0;
}
async loadMore() {
// Prevent multiple simultaneous loads
if (this.loading || !this.hasMore) {
return [];
}
this.loading = true;
try {
const response = await this.fetchFn(this.currentPage, this.pageSize);
// Add new items
this.allItems.push(...response.data);
// Update state
this.hasMore = !response.pagination.last;
this.totalItems = response.pagination.totalElements;
this.currentPage++;
console.log(`Loaded page ${this.currentPage}, showing ${this.allItems.length} of ${this.totalItems} total`);
return response.data; // Return just the new items
} finally {
this.loading = false;
}
}
getAllItems() {
return this.allItems;
}
reset() {
this.currentPage = 0;
this.allItems = [];
this.hasMore = true;
this.totalItems = 0;
}
}
// Usage in a web app
const scroll = new InfiniteScroll(getCases, 20);
// Initial load
const firstBatch = await scroll.loadMore();
displayCases(firstBatch);
// User scrolls down
document.addEventListener('scroll', async () => {
const scrolledToBottom =
window.innerHeight + window.scrollY >= document.body.offsetHeight - 100;
if (scrolledToBottom && !scroll.loading) {
const newItems = await scroll.loadMore();
appendCases(newItems);
}
});Page size affects performance, memory usage, and user experience.
| Page Size | Pros | Cons | Best For |
|---|---|---|---|
| Small (10-20) | - Fast response times - Low memory usage - Quick time-to-first-render | - More API requests - More overhead - Higher rate limit usage | UI pagination, mobile apps |
| Medium (20-50) | - Balanced performance - Reasonable memory - Fewer requests | - Slightly slower responses | Most use cases, dashboards |
| Large (50-100) | - Fewer requests - Lower rate limit usage - Efficient for batch processing | - Slower responses - More memory usage - Longer wait for first result | Batch processing, exports |
// UI pagination (user clicking through pages)
const uiCases = await getCases(page, 20);
// Infinite scroll (loading as user scrolls)
const scrollCases = await getCases(page, 30);
// Background processing (analyzing all cases)
const batchCases = await getCases(page, 100);
// Mobile app (limited bandwidth)
const mobileCases = await getCases(page, 15);
// Real-time dashboard (frequent updates)
const dashboardCases = await getCases(page, 25);Performance comparison:
Fetching 1,000 cases:
Page size 10: 100 requests, ~30 seconds
Page size 20: 50 requests, ~15 seconds ✅ Good for UI
Page size 50: 20 requests, ~6 seconds ✅ Good for batch
Page size 100: 10 requests, ~4 seconds ✅ Fastest, but largest responsesAlways check if a page has any data:
async function getCasesWithEmptyCheck(page, size) {
const response = await getCases(page, size);
if (!response.data || response.data.length === 0) {
if (page === 0) {
console.log('No cases found at all');
} else {
console.log('Reached end of results');
}
return { isEmpty: true, data: [] };
}
return { isEmpty: false, data: response.data };
}Handle requests for pages that don't exist:
async function getSafePage(page, size) {
const response = await getCases(page, size);
// Check if requested page is beyond available pages
if (page >= response.pagination.totalPages) {
console.warn(`Requested page ${page} but only ${response.pagination.totalPages} pages exist`);
// Return last valid page instead
return getCases(response.pagination.totalPages - 1, size);
}
return response;
}The dataset might change while you're fetching pages:
Problem:
// Fetch page 0 at 10:00 AM - 50 cases returned
const page1 = await getCases(0, 50);
// New cases are created at 10:01 AM
// Fetch page 1 at 10:02 AM - you might skip or duplicate items!
const page2 = await getCases(1, 50);Solutions:
- Use timestamps to filter:
const startTime = new Date().toISOString();
// All requests use the same timestamp filter
const page1 = await getCases(0, 50, { createdBefore: startTime });
const page2 = await getCases(1, 50, { createdBefore: startTime });
// This ensures a consistent snapshot of dataUse cursor-based pagination for real-time data (covered in advanced section)
Fetch quickly to minimize window for changes:
// Fetch pages rapidly in parallel
const pages = await Promise.all([
getCases(0, 50),
getCases(1, 50),
getCases(2, 50)
]);For datasets that change frequently, cursor-based pagination is more reliable than offset-based.
Instead of page numbers, you use a cursor (a pointer to a specific item). The API returns the next cursor with each response.
Request 1: GET /cases?size=20
Response: Items 1-20, nextCursor="abc123"
Request 2: GET /cases?size=20&cursor=abc123
Response: Items 21-40, nextCursor="def456"
Request 3: GET /cases?size=20&cursor=def456
Response: Items 41-60, nextCursor="ghi789"Benefits over offset pagination:
- No duplicate items even if data changes during pagination
- No skipped items even if items are added/deleted
- More efficient for databases to process
Drawbacks:
- Can't jump to arbitrary pages (no "go to page 5")
- Can't calculate total pages upfront
async function iterateWithCursor(fetchFn, pageSize = 50) {
let cursor = null;
const allItems = [];
do {
const result = await fetchFn(cursor, pageSize);
allItems.push(...result.items);
cursor = result.nextCursor;
console.log(`Fetched ${result.items.length} items, total so far: ${allItems.length}`);
} while (cursor !== null);
return allItems;
}
// Usage
const allCases = await iterateWithCursor(async (cursor, size) => {
const params = new URLSearchParams({ size });
if (cursor) params.append('cursor', cursor);
const response = await fetch(
`https://api-v3.sprinklr.com/api/v3/case/search?${params}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({ status: 'OPEN' })
}
);
const data = await response.json();
return {
items: data.data,
nextCursor: data.pagination?.nextCursor || null
};
});Caching can dramatically improve performance for frequently accessed pages.
class PaginationCache {
constructor(ttl = 300000) { // 5 minutes default
this.cache = new Map();
this.ttl = ttl;
}
generateKey(page, size, sort, filters) {
return `${page}:${size}:${sort}:${JSON.stringify(filters)}`;
}
set(page, size, sort, filters, data) {
const key = this.generateKey(page, size, sort, filters);
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
get(page, size, sort, filters) {
const key = this.generateKey(page, size, sort, filters);
const cached = this.cache.get(key);
if (!cached) return null;
// Check if expired
if (Date.now() - cached.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return cached.data;
}
clear() {
this.cache.clear();
}
// Clear cache for specific filters (e.g., when data changes)
clearMatching(filters) {
const filterStr = JSON.stringify(filters);
for (const [key, _] of this.cache) {
if (key.includes(filterStr)) {
this.cache.delete(key);
}
}
}
}
// Usage
const cache = new PaginationCache(300000); // 5 minutes
async function getCasesWithCache(page, size, filters = {}) {
const sort = 'createdTime,desc';
// Check cache first
const cached = cache.get(page, size, sort, filters);
if (cached) {
console.log('Cache hit!');
return cached;
}
// Fetch from API
console.log('Cache miss, fetching from API...');
const response = await getCases(page, size);
// Cache the result
cache.set(page, size, sort, filters, response);
return response;
}
// When a case is updated, clear relevant cache entries
function onCaseUpdated(caseId) {
cache.clearMatching({ status: 'OPEN' });
console.log('Cleared cache for OPEN cases');
}Robust error handling is essential for pagination since you're making multiple requests.
async function getAllCasesWithErrorHandling() {
const allCases = [];
let page = 0;
let hasMore = true;
const maxRetries = 3;
while (hasMore) {
let attempt = 0;
let success = false;
let response;
// Retry logic for each page
while (attempt < maxRetries && !success) {
try {
response = await getCases(page, 50);
success = true;
} catch (error) {
attempt++;
console.error(`Error fetching page ${page}, attempt ${attempt}:`, error.message);
if (attempt >= maxRetries) {
throw new Error(`Failed to fetch page ${page} after ${maxRetries} attempts`);
}
// Exponential backoff
const waitTime = Math.pow(2, attempt) * 1000;
console.log(`Waiting ${waitTime}ms before retry...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
if (!response.success) {
console.error(`Page ${page} returned success=false`);
break;
}
allCases.push(...response.data);
hasMore = !response.pagination.last;
page++;
}
return {
cases: allCases,
completedPages: page,
complete: !hasMore
};
}
// Usage
try {
const result = await getAllCasesWithErrorHandling();
if (result.complete) {
console.log(`Successfully fetched all ${result.cases.length} cases`);
} else {
console.warn(`Partial fetch: got ${result.cases.length} cases from ${result.completedPages} pages`);
}
} catch (error) {
console.error('Pagination failed completely:', error);
}If the API supports field selection, specify only required fields to reduce response size:
const response = await fetch(
'https://api-v3.sprinklr.com/api/v3/case/search?page=0&size=50&fields=caseId,subject,status',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({ status: 'OPEN' })
}
);// Estimate total time for different strategies
function estimateFetchTime(totalItems, pageSize, requestTime = 300) {
const totalPages = Math.ceil(totalItems / pageSize);
const totalTime = totalPages * requestTime;
return {
totalPages,
totalTime: `${(totalTime / 1000).toFixed(1)} seconds`,
requestsPerMinute: Math.ceil((totalPages / totalTime) * 60000)
};
}
// Compare strategies for 5000 items
console.log('Page size 20:', estimateFetchTime(5000, 20)); // 250 pages, 75 seconds
console.log('Page size 50:', estimateFetchTime(5000, 50)); // 100 pages, 30 seconds
console.log('Page size 100:', estimateFetchTime(5000, 100)); // 50 pages, 15 secondsasync function fetchPagesInParallel(totalPages, pageSize, concurrency = 3) {
const allResults = [];
// Process pages in batches to respect rate limits
for (let batch = 0; batch < Math.ceil(totalPages / concurrency); batch++) {
const startPage = batch * concurrency;
const endPage = Math.min(startPage + concurrency, totalPages);
const batchPromises = [];
for (let page = startPage; page < endPage; page++) {
batchPromises.push(getCases(page, pageSize));
}
const batchResults = await Promise.all(batchPromises);
allResults.push(...batchResults);
console.log(`Fetched batch ${batch + 1}, pages ${startPage}-${endPage - 1}`);
// Small delay between batches
if (endPage < totalPages) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return allResults.flatMap(r => r.data);
}
// Fetch 30 pages, 3 at a time
const cases = await fetchPagesInParallel(30, 50, 3);Symptoms:
- Items appear twice in your results
- Items are missing when you fetch all pages
- Total count doesn't match expected number
Causes:
- Data changing between page fetches
- Inconsistent sort order between requests
- Starting page number from 1 instead of 0
Solutions:
// ✅ Always use 0-indexed pages
const correctPage = await getCases(0, 20); // First page
// ❌ Don't use 1-indexed
const wrongPage = await getCases(1, 20); // This is the SECOND page!
// ✅ Use consistent sort order
const page1 = await getCases(0, 20, 'createdTime,desc');
const page2 = await getCases(1, 20, 'createdTime,desc'); // Same sort!
// ✅ Use timestamp filters for consistent snapshots
const timestamp = new Date().toISOString();
const page1 = await getCases(0, 20, { createdBefore: timestamp });
const page2 = await getCases(1, 20, { createdBefore: timestamp });Symptoms:
- Taking too long to fetch all pages
- Requests timing out
- Memory issues with large datasets
Solutions:
// 1. Increase page size to reduce total requests
const largerPages = await getCases(page, 100); // Max size
// 2. Use parallel fetching (but respect rate limits)
const parallel = await Promise.all([
getCases(0, 50),
getCases(1, 50),
getCases(2, 50)
]);
// 3. Process progressively instead of loading all at once
await processAllCases(async (caseItem) => {
// Process each case immediately
await handleCase(caseItem);
});
// 4. Use appropriate caching
cache.set(page, size, sort, filters, response);Symptoms:
totalPagesis different on each request- Can't reach the last page
- Infinite loop in pagination
Causes:
- Items being added/deleted during pagination
- Filters changing between requests
Solutions:
// Use last flag instead of totalPages
async function getAllCasesSafely() {
const allCases = [];
let page = 0;
let hasMore = true;
const maxPages = 1000; // Safety limit
while (hasMore && page < maxPages) {
const response = await getCases(page, 50);
allCases.push(...response.data);
// Use 'last' flag, not totalPages
hasMore = !response.pagination.last;
page++;
}
if (page >= maxPages) {
console.warn('Hit max page limit, there may be more data');
}
return allCases;
}Now that you understand pagination, explore these related topics:
- API Basics - Learn about batch operations and other core API patterns
- Rate Limiting - Optimize pagination performance with rate limit strategies
- Error Handling - Handle pagination errors gracefully
- Best Practices - Production-ready patterns for API integrations
If you're experiencing pagination issues:
- Verify your page numbers - Remember, pages start at 0, not 1
- Check your sort order - Inconsistent sorting causes duplicate/missing items
- Review response metadata - The
paginationobject tells you what's happening - Monitor performance - Track fetch times and adjust page sizes
Support resources:
- Technical support: support.sprinklr.com
- API documentation: This guide and related docs
- Community forums: Share pagination patterns with other developers