Pagination
How list endpoints wrap data in a meta and links envelope, the 100-row page cap, and how to iterate a large result set.
Every paginated list endpoint returns a data array wrapped in a meta + links envelope. Pagination is page-based, capped at 100 rows per request.
Response shape
{
"data": [
{ "id": "7d1f...", "key": "CRM", "name": "CRM Replacement" },
{ "id": "9a3c...", "key": "PAY", "name": "Payments" }
],
"links": {
"first": "https://api.example.test/v1/projects?page=1",
"last": "https://api.example.test/v1/projects?page=4",
"prev": null,
"next": "https://api.example.test/v1/projects?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 4,
"per_page": 25,
"to": 25,
"total": 92
}
}The per_page and page parameters
Every paginated endpoint accepts:
per_page, the number of rows in the response (default25, maximum100).limitis accepted as an alias.page, the 1-based page index (default1).sort_byandsort_direction(ascordesc), where the endpoint documents sortable columns; an unknown column falls back to the default order.
curl "https://api.example.test/v1/projects?per_page=50&page=2" \
-H "Authorization: Bearer zg_live_..."| Endpoint | Default per_page | Order |
|---|---|---|
GET /v1/projects | 25 | Newest first; sort_by takes name, key, status, created_at |
GET /v1/tokens | 25 | Newest first |
GET /v1/webhook-endpoints | 15 | Newest first |
GET /v1/webhook-deliveries | 15 | Newest first |
GET /v1/me/notifications | 15 | Newest first |
GET /v1/webhook-events | 3 (max 10) | Newest first, no envelope |
Short lists are not paginated: GET /v1/teams, GET /v1/teams/{team}/members, GET /v1/roles, GET /v1/groups, GET /v1/me/alert-destinations and GET /v1/activity return every row in data with no meta.
Requesting per_page above the cap (100) clamps to the maximum silently.
Requesting a page past the last page returns an empty data array with
meta.total still populated, so a polling loop can detect the end without
guessing.
Iterating across pages
async function fetchAllProjects(apiUrl: string, token: string) {
const all = [];
let page = 1;
while (true) {
const res = await fetch(`${apiUrl}/v1/projects?per_page=100&page=${page}`, {
headers: { Authorization: `Bearer ${token}` },
});
const body = await res.json();
all.push(...body.data);
if (body.meta.current_page >= body.meta.last_page) break;
page += 1;
}
return all;
}Filtering before paginating
Combine per_page with the resource-specific filters to avoid long loops:
curl "https://api.example.test/v1/projects?status=in_development&per_page=100" \
-H "Authorization: Bearer zg_live_..."Related
- Rate limiting: how hard you can poll.
- Webhooks: the streaming alternative to polling.
How is this guide?
Last updated on