Pagination
Oproto APIs use cursor-based pagination for efficient traversal of large result sets.
How It Works
List endpoints return paginated responses with a consistent structure:
{
"data": [
{ "id": "comp_abc123", "name": "Acme Corp" },
{ "id": "comp_def456", "name": "Globex Inc" }
],
"pagination": {
"hasMore": true,
"nextCursor": "eyJpZCI6ImNvbXBfZGVmNDU2In0",
"totalCount": 1250
}
}
| Field | Description |
|---|---|
data | Array of results for the current page |
pagination.hasMore | Whether more results exist |
pagination.nextCursor | Cursor to fetch the next page |
pagination.totalCount | Total number of results (when available) |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Number of results per page (max: 100) |
cursor | string | — | Cursor from previous response |
Example Request
# First page
curl -X GET "https://api.oproto.io/v1/companies?limit=50" \
-H "Authorization: Bearer {access_token}"
# Next page
curl -X GET "https://api.oproto.io/v1/companies?limit=50&cursor=eyJpZCI6ImNvbXBfZGVmNDU2In0" \
-H "Authorization: Bearer {access_token}"
Iterating Through Results
JavaScript Example
async function* fetchAllCompanies(limit = 50) {
let cursor = null;
do {
const params = new URLSearchParams({ limit });
if (cursor) params.set('cursor', cursor);
const response = await fetch(
`https://api.oproto.io/v1/companies?${params}`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
const { data, pagination } = await response.json();
for (const company of data) {
yield company;
}
cursor = pagination.hasMore ? pagination.nextCursor : null;
} while (cursor);
}
// Usage
for await (const company of fetchAllCompanies()) {
console.log(company.name);
}
Collecting All Results
async function getAllCompanies() {
const companies = [];
for await (const company of fetchAllCompanies()) {
companies.push(company);
}
return companies;
}
Best Practices
Use Appropriate Page Sizes
- Small pages (10-20): Interactive UIs with quick initial load
- Medium pages (50): Background sync operations
- Large pages (100): Bulk data exports
Don't Store Cursors Long-Term
Cursors are designed for immediate pagination and may expire. Don't persist them across sessions.
Handle Empty Pages
Always check hasMore rather than assuming empty data means no more results:
// ✅ Correct
while (pagination.hasMore) {
// fetch next page
}
// ❌ Incorrect - may miss results
while (data.length > 0) {
// fetch next page
}
Respect Rate Limits
When paginating through large datasets, add delays between requests:
async function fetchAllWithDelay(delayMs = 100) {
for await (const item of fetchAllCompanies()) {
process(item);
}
await sleep(delayMs); // Be nice to the API
}
Sorting
List endpoints support sorting via the sort parameter:
# Sort by name ascending
GET /v1/companies?sort=name
# Sort by creation date descending
GET /v1/companies?sort=-createdAt
| Prefix | Direction |
|---|---|
| (none) | Ascending |
- | Descending |
Common sortable fields:
createdAt— Creation timestampupdatedAt— Last modification timestampname— Alphabetical by name
note
Sorting is applied before pagination. Changing sort order requires starting from the first page.
Filtering
Combine pagination with filters to narrow results:
GET /v1/companies?status=active&limit=50
GET /v1/contacts?companyId=comp_abc123&limit=20
See individual endpoint documentation for available filters.