Skip to content
Aranisdevelopers
Sign in

Pagination

Every collection is cursor-paged. There is no offset, deliberately.

The envelope

Collections return data and meta. A single resource returns the object directly, with no envelope.

GET /v1/suppliers?limit=2
{
  "data": [ { "id": "3f1a8c2e-...", "name": "Acme Cloud Inc" }, { "id": "7b2d4f9a-...", "name": "Globex" } ],
  "meta": {
    "has_more": true,
    "next_cursor": "eyJjIjoiMjAyNi0wNy0zMFQxMjowMDowMFoiLCJpIjoiLi4uIn0",
    "feature_status": "available"
  },
  "currency": "USD"
}

Walking the pages

Pass meta.next_cursor back as cursor. When it is null, you have reached the end.

bash
cursor=""
while :; do
  page=$(curl -s "$ARANIS/suppliers?limit=100&cursor=$cursor" \
    -H "Authorization: Bearer $ARANIS_API_KEY")
  echo "$page" | jq -r '.data[].name'
  cursor=$(echo "$page" | jq -r '.meta.next_cursor // empty')
  [ -z "$cursor" ] && break
done

The SDK does this for you:

TypeScript
for await (const supplier of aranis.paginate(p => aranis.listSuppliers(p))) {
  console.log(supplier.name)
}

// Or, for a set you know is small:
const all = await aranis.collect(p => aranis.listSuppliers(p))
Cursors are opaque. Do not construct, parse or store assumptions about their contents — the encoding is an implementation detail and will change without notice.

Why not offset?

Because your data keeps moving while you page through it. With ?offset=200, a supplier created during your run shifts every later row down by one — so you read one record twice and skip another entirely. That failure is silent: the totals look right.

Cursors encode the sort key of the last row you saw, so the next page resumes from that exact point regardless of what was inserted meanwhile. They also stay fast at depth, where OFFSET gets linearly slower.

Limits

limit defaults to 50 and accepts 1–200. A value above 200 is rejected, not silently clamped — clamping would let you believe you received everything you asked for.

Empty collections

An empty collection is never an error. meta.feature_status says why:

ValueMeaning
availableThe feature is active. You have no rows.
not_configuredThe feature exists but this workspace has not set it up. meta.message explains.
coming_soonNot released yet.

This is the difference between an integrator filing a bug and reading one sentence. A 404 would say the endpoint does not exist; a 500 would say we broke. Neither is true when you simply have not configured webhooks yet.

Collections that are not cursor-paged

Three endpoints return a bounded set in one response and always report has_more: false:

  • /assessments/{id}/gaps — one assessment holds at most the full control pool.
  • /risk-matrix — ordered by risk, not time, over two tables with no shared key. limit applies per scope.
  • /alerts — merges two sources; limit applies per source, so a page can hold up to twice it.