Pagination
Checked against the API code 3 November 2026
A list that can grow long comes one page at a time. Each page tells you whether more follow and how to ask for the next one.
Before you start
Section titled “Before you start”The examples list your VPS with GET /api/v2/vps. Other lists work the same way. Lists that work differently names the exceptions.
- An API key with the scope
read:vm. Authentication and scopes - The requests read the key from
$HOSTUP_API_KEY, as set in the Quickstart. jqfor the script under Read every page.
A page
Section titled “A page”Ask for the first page without a cursor. limit=2 keeps the example short.
curl "https://cloud.hostup.se/api/v2/vps?limit=2" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "data": [ { "id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3", "name": "app-01", "…": "…" }, { "id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m4", "name": "app-02", "…": "…" } ], "hasMore": true, "nextCursor": "eyJsYXN0SWQiOiJ2cHNfMDFoeGEzYjRjNWQ2ZTdmOGc5aDBqMWsybTQifQ"}Keep nextCursor for the next page:
export CURSOR="…" # nextCursor above| Field | What it is |
|---|---|
data | The items on this page. Always an array, empty when nothing matches. |
hasMore | true when more pages follow. |
nextCursor | What to send as cursor for the next page. null on the last page. |
Most lists have no total count and no page number. Read pages until hasMore is false. A page can hold fewer items than limit, even none, while hasMore is still true. Do not stop on a short or empty page.
The next page
Section titled “The next page”Send nextCursor back as cursor, with the same limit.
curl -G "https://cloud.hostup.se/api/v2/vps?limit=2" \ --data-urlencode "cursor=$CURSOR" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "data": [ { "id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m5", "name": "app-03", "…": "…" } ], "hasMore": false, "nextCursor": null}hasMore is false. This is the last page.
Parameters
Section titled “Parameters”limitsets the page size. Most lists take 1 to 100 and default to 20. Some set their own range.GET /api/v2/domainsandGET /api/v2/dns-zonestake up to 1000 and return 1000 by default. Each endpoint page shows its range and default.cursortakes thenextCursorfrom the previous page. Leave it out for the first page.
A cursor is opaque. Do not decode it or build one. Pass it back as it came, URL-encoded, even when it looks URL-safe.
Send the same limit and the same filters, such as name or primaryIp, with every page. A cursor belongs to the list, the filters and the limit that produced it. Change any of them and the next page can skip or repeat items.
Read every page
Section titled “Read every page”This script prints the ID and name of every VPS on the account, 100 at a time:
#!/usr/bin/env bashcursor=""while :; do page=$(curl -s --fail-with-body \ "https://cloud.hostup.se/api/v2/vps?limit=100${cursor:+&cursor=$cursor}" \ -H "Authorization: Bearer $HOSTUP_API_KEY") || { echo "$page" >&2; exit 1; }
jq -r '.data[] | "\(.id) \(.name)"' <<<"$page"
[ "$(jq -r '.hasMore' <<<"$page")" = "true" ] || break cursor=$(jq -r '.nextCursor | @uri' <<<"$page") # @uri encodes the cursor for the URLdoneIf the last item of a page is deleted before you ask for the next page, many lists start again from the first item. Nothing in the response says so. If a VPS is deleted while the script runs, it can print some VPS twice. Remove repeats by ID before you act on the output:
./list-all-vps.sh | awk '!seen[$1]++'Lists that work differently
Section titled “Lists that work differently”A total or a page number
Section titled “A total or a page number”GET /api/v2/audit-logalso returnstotal, the number of events that match your filters.GET /api/v2/billing/activityhas no cursor. It takes a page number inpage, starting at 0. It returns its rows inentries, withhasMore. Add 1 topagewhilehasMoreistrue.
A wrong limit or cursor
Section titled “A wrong limit or cursor”Lists do not all answer a bad limit or cursor the same way:
| List | limit outside the range | A cursor the list cannot read |
|---|---|---|
| Most lists | 400 invalid_pagination_limit | 400 invalid_cursor |
| Invoices, DNS history, DNS changes, DNS snapshots | 400 invalid_request | 400 invalid_request |
| Mail relay messages | Below 1: 400 invalid_request. A value over 100 becomes 100. | 400 invalid_request |
| Audit log | 400 invalid_request | No error. You get the first page. |
| Support tickets | No error. A value below 1 becomes 20. A value over 100 becomes 100. | No error. You get the first page. |
Handle a 400 from a list by its status, not only by its code. On the audit log and the ticket list, a damaged cursor starts the list over without an error. A loop that keeps sending a damaged cursor never ends.
Lists without pages
Section titled “Lists without pages”Some lists are always short. They return everything at once and take no limit or cursor. Most put their items in a named field, such as jobs in GET /api/v2/vps/{id}/active-jobs. Three return data with no hasMore or nextCursor: GET /api/v2/vps/{id}/snapshots, GET /api/v2/cdn/zones/{id}/proxy-rules and GET /api/v2/products/shared-hosting/storage-addons.
Next: Rate limits