Skip to content

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.

The examples list your VPS with GET /api/v2/vps. Other lists work the same way. Lists that work differently names the exceptions.

Ask for the first page without a cursor. limit=2 keeps the example short.

GET /api/v2/vps
curl "https://cloud.hostup.se/api/v2/vps?limit=2" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"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
FieldWhat it is
dataThe items on this page. Always an array, empty when nothing matches.
hasMoretrue when more pages follow.
nextCursorWhat 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.

Send nextCursor back as cursor, with the same limit.

GET /api/v2/vps
curl -G "https://cloud.hostup.se/api/v2/vps?limit=2" \
--data-urlencode "cursor=$CURSOR" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"data": [
{
"id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m5",
"name": "app-03",
"…": "…"
}
],
"hasMore": false,
"nextCursor": null
}

hasMore is false. This is the last page.

  • limit sets the page size. Most lists take 1 to 100 and default to 20. Some set their own range. GET /api/v2/domains and GET /api/v2/dns-zones take up to 1000 and return 1000 by default. Each endpoint page shows its range and default.
  • cursor takes the nextCursor from 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.

This script prints the ID and name of every VPS on the account, 100 at a time:

list-all-vps.sh
#!/usr/bin/env bash
cursor=""
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 URL
done

If 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]++'
  • GET /api/v2/audit-log also returns total, the number of events that match your filters.
  • GET /api/v2/billing/activity has no cursor. It takes a page number in page, starting at 0. It returns its rows in entries, with hasMore. Add 1 to page while hasMore is true.

Lists do not all answer a bad limit or cursor the same way:

Listlimit outside the rangeA cursor the list cannot read
Most lists400 invalid_pagination_limit400 invalid_cursor
Invoices, DNS history, DNS changes, DNS snapshots400 invalid_request400 invalid_request
Mail relay messagesBelow 1: 400 invalid_request. A value over 100 becomes 100.400 invalid_request
Audit log400 invalid_requestNo error. You get the first page.
Support ticketsNo 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.

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