Follow the cursor, not the page number.
List endpoints do not all page the same way. GET /monitors uses signed cursors,
the incident lists use page and per_page,
and most other lists return everything in one array.
| Endpoint | How it pages | Response |
|---|---|---|
GET /monitors | limit and cursor | data array and a real pagination object |
GET /incidents, GET /monitors/{id}/incidents | page and per_page | data object with items, page, per_page, total, counts; no pagination |
| Most other lists | None. The whole list comes back. | data array and a placeholder pagination object |
Some reference pages list limit and cursor on every list operation.
Only GET /monitors reads cursor; the others ignore it.
Cursor pages: GET /monitors
curl -s -G https://app.upfour.io/v1/monitors \
-H "Authorization: Bearer $UP4_API_KEY" \
--data-urlencode "limit=100" \
--data-urlencode "sort=name" \
--data-urlencode "status=DOWN"{
"data": [ { "id": "…", "name": "Lakeside Bakery site", … } ],
"pagination": {
"cursor": null,
"next_cursor": "eyJ2IjoxLC…",
"prev_cursor": null,
"limit": 100,
"has_more": true
}
}Repeat with cursor set to next_cursor until has_more is false:
cursor=""
while true; do
page=$(curl -s -G https://app.upfour.io/v1/monitors \
-H "Authorization: Bearer $UP4_API_KEY" \
--data-urlencode "limit=100" \
--data-urlencode "cursor=$cursor")
echo "$page" | jq -r '.data[].name'
cursor=$(echo "$page" | jq -r '.pagination.next_cursor // empty')
[ -z "$cursor" ] && break
doneParameters
| Parameter | Meaning |
|---|---|
limit | Page size. Default 50, at most 250; a larger value is lowered to 250, and a missing, zero or non-numeric value means 50. |
cursor | The next_cursor of the previous page. Leave it out, or empty, for the first page. |
sort | created_at, name or next_check_at; a leading - sorts descending. Default -created_at. Anything else is 400. |
status | UP, DOWN or PAUSED, not case-sensitive. state works as an alias. Anything else is 400. |
monitor_type | http, tcp, ping or dns. Anything else is 400. |
region | Monitors that run in this region. |
q | Case-insensitive text search over name, URL, type, TCP host, DNS query and region. |
monitor_group_id | A monitor group id. A value that is not a UUID is 400. |
severity, severity_group | Monitors with an open incident of this severity or severity group. |
client_id, all_clients | Which clients to list; see client scope. Not part of the cursor check. |
Response fields
| Field | Meaning |
|---|---|
data | The page of monitors. |
pagination.limit | The page size that was applied, after the clamp. |
pagination.has_more | True when another page exists. |
pagination.next_cursor | Pass it as cursor for the next page. null when has_more is false. |
pagination.cursor | The cursor you sent, or null on the first page. |
pagination.prev_cursor | Always null. Paging goes forward only. |
There is no total count. Rows are ordered by the sort field and then by id, so the order is stable.
The cursor
A cursor is an opaque, signed string. It is valid for 24 hours and is tied to the sort and to the filters of the request that produced it. Treat it as a value to hand back, not as something to build or read.
| error.code | Status | Cause |
|---|---|---|
cursor_invalid | 400 | The cursor was not issued by up4, was changed, or is malformed. |
cursor_expired | 400 | The cursor is older than 24 hours. |
cursor_parameters_changed | 400 | sort, status, monitor_type, region, q, monitor_group_id, severity or severity_group differ from the request that produced the cursor. |
After any of the three, start again without cursor.
Page numbers: incidents
GET /incidents and GET /monitors/{id}/incidents take
page (from 1) and per_page (default 25, at most 100).
The result is one object: the page of incidents in items, the position in
page and per_page, the number of incidents that match your filters in
total, and counts, summary counters that ignore the page and the list filters.
curl -s -G https://app.upfour.io/v1/incidents \
-H "Authorization: Bearer $UP4_API_KEY" \
--data-urlencode "per_page=50" \
--data-urlencode "page=2"Everything at once: the other lists
The remaining lists return every row in data. up4 still adds a
pagination block so every list has the same outline, but it is a placeholder:
limit is always 50, has_more is false and the cursors are null,
whatever the length of the array. A few lists take their own limit, for example
GET /heartbeats/{id}/pings, which returns 25 pings by default and at most 10,000; their reference pages say so.
Common mistakes
- Sending
pageorper_pageto/monitors. That is400 bad_request: the public API usescursorandlimitthere. - Changing sort or a filter between pages. The cursor check fails with
cursor_parameters_changed. Changinglimitis fine. - Changing
client_idorall_clientsbetween pages. They are not part of the cursor check, so up4 does not warn you; keep them the same. - Stopping when a page is shorter than
limit. Stop onhas_morebeing false ornext_cursorbeing null. - Believing the placeholder.
has_more: falseon a list outside the table above says nothing about its size. - Expecting a snapshot. Each page starts after the last row of the previous one. With the default sort, a monitor created while you page sorts before your position and is not in this run.
- Keeping a cursor for later. It expires after 24 hours.
Was this page helpful?