Docs

Type a word or a question. Esc closes the search.

DE
Sign in Start free
Developers/Basics

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.

EndpointHow it pagesResponse
GET /monitorslimit and cursordata array and a real pagination object
GET /incidents, GET /monitors/{id}/incidentspage and per_pagedata object with items, page, per_page, total, counts; no pagination
Most other listsNone. 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
done

Parameters

ParameterMeaning
limitPage size. Default 50, at most 250; a larger value is lowered to 250, and a missing, zero or non-numeric value means 50.
cursorThe next_cursor of the previous page. Leave it out, or empty, for the first page.
sortcreated_at, name or next_check_at; a leading - sorts descending. Default -created_at. Anything else is 400.
statusUP, DOWN or PAUSED, not case-sensitive. state works as an alias. Anything else is 400.
monitor_typehttp, tcp, ping or dns. Anything else is 400.
regionMonitors that run in this region.
qCase-insensitive text search over name, URL, type, TCP host, DNS query and region.
monitor_group_idA monitor group id. A value that is not a UUID is 400.
severity, severity_groupMonitors with an open incident of this severity or severity group.
client_id, all_clientsWhich clients to list; see client scope. Not part of the cursor check.

Response fields

FieldMeaning
dataThe page of monitors.
pagination.limitThe page size that was applied, after the clamp.
pagination.has_moreTrue when another page exists.
pagination.next_cursorPass it as cursor for the next page. null when has_more is false.
pagination.cursorThe cursor you sent, or null on the first page.
pagination.prev_cursorAlways 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.codeStatusCause
cursor_invalid400The cursor was not issued by up4, was changed, or is malformed.
cursor_expired400The cursor is older than 24 hours.
cursor_parameters_changed400sort, 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 page or per_page to /monitors. That is 400 bad_request: the public API uses cursor and limit there.
  • Changing sort or a filter between pages. The cursor check fails with cursor_parameters_changed. Changing limit is fine.
  • Changing client_id or all_clients between 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 on has_more being false or next_cursor being null.
  • Believing the placeholder. has_more: false on 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.