Pagination
Endpoints that return lists (such as GET /v1/funnels/{funnelId}/contacts) are paginated. You control which page of results you want using query parameters, and the response tells you how many items exist in total and whether more pages follow.
Query parameters
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
page | integer | 0 | min: 0 | Page number, 0-based |
limit | integer | 50 | 1–100 | Number of items per page |
sortField | string | ps_converted_at | enum (see below) | Field to sort by |
sortOrder | integer | -1 | -1 or 1 | Sort direction: -1 descending, 1 ascending |
Allowed values for sortField: ps_converted_at, email, firstName, lastName.
Note:
sortFieldandsortOrderare currently available on the contacts endpoint. Check the individual endpoint reference for which sort options apply.
The meta object
Every paginated response includes a top-level meta object alongside the data array:
{
"data": [ /* array of items */ ],
"meta": {
"total": 342,
"page": 0,
"limit": 50,
"hasNext": true
}
}
| Field | Type | Description |
|---|---|---|
total | number | Total number of items across all pages |
page | number | Current page number (0-based) |
limit | number | Items per page as used for this response |
hasNext | boolean | true if there are more pages; false on last page |
Fetching a specific page
- cURL
- JavaScript
# Page 0 (first page), 20 contacts per page
curl "https://api.perspective.co/v1/funnels/fnl_xyz789/contacts?page=0&limit=20" \
-H "x-perspective-api-key: $PERSPECTIVE_API_KEY"
# Page 1 (second page)
curl "https://api.perspective.co/v1/funnels/fnl_xyz789/contacts?page=1&limit=20" \
-H "x-perspective-api-key: $PERSPECTIVE_API_KEY"
const response = await fetch(
'https://api.perspective.co/v1/funnels/fnl_xyz789/contacts?page=0&limit=20',
{
headers: { 'x-perspective-api-key': process.env.PERSPECTIVE_API_KEY },
}
);
const { data, meta } = await response.json();
// meta.hasNext === true means there are more pages
Iterating through all pages
Use meta.hasNext to drive a loop. Increment page after each successful response and stop when hasNext is false.
- cURL
- JavaScript
# Shell loop — fetch all pages and append to allContacts.json
page=0
limit=100
has_next=true
while [ "$has_next" = "true" ]; do
response=$(curl -s \
"https://api.perspective.co/v1/funnels/fnl_xyz789/contacts?page=$page&limit=$limit" \
-H "x-perspective-api-key: $PERSPECTIVE_API_KEY")
echo "$response" >> allContacts.json
has_next=$(echo "$response" | jq -r '.meta.hasNext')
page=$((page + 1))
done
async function fetchAllContacts(funnelId) {
const allContacts = [];
let page = 0;
const limit = 100;
while (true) {
const response = await fetch(
`https://api.perspective.co/v1/funnels/${funnelId}/contacts?page=${page}&limit=${limit}`,
{
headers: { 'x-perspective-api-key': process.env.PERSPECTIVE_API_KEY },
}
);
if (!response.ok) {
const err = await response.json();
throw new Error(`${err.status}: ${err.error}`);
}
const { data, meta } = await response.json();
allContacts.push(...data);
if (!meta.hasNext) break;
page += 1;
}
return allContacts;
}
Tips
- Start at page 0. The API uses 0-based page numbers; page 1 is the second page, not the first.
- Respect rate limits. If you are looping through many pages, add a small delay between requests to stay within the 100-requests-per-minute limit. See Errors for rate limit details.
- Use
totalfor progress. Divide the number of items fetched so far bymeta.totalto show progress in a data-export flow.
Related pages
- Quickstart — end-to-end first call using paginated contacts
- Errors — 429 Too Many Requests and how to handle rate limiting
- Contacts API Reference — full query-parameter reference for the contacts endpoint