Docs / Shared / Pagination & errors

Pagination & errors

Cursor pagination and the same five error codes on every dataset.

Pagination

Collections are cursor-paginated. Follow meta.next until it is null; cursors are opaque, so do not construct them yourself, and keep every other filter identical between pages — a cursor is only valid for the query and sort order that produced it. Offset pagination is deliberately not offered: it skips and repeats rows when the underlying data moves between pages, and these datasets are rewritten by a scraper every few hours.

# page 1
GET https://api.softon.dev/v1/jobs?limit=25
# page 2 — pass the cursor back, keep the other filters identical
GET https://api.softon.dev/v1/jobs?limit=25&cursor=eyJvIjoyfQ

limit defaults to 25 and is capped at 100. Asking for more is a 400, not a silent truncation, so a client that expected 500 rows finds out rather than concluding the dataset is small.

Errors

Five codes, the same on every dataset. The HTTP status is authoritative; error.code is the stable string to branch on. An unrecognised query parameter is a 400 naming the parameter and listing the accepted ones — a mistyped filter must not look like an empty result set.

StatusCodeMeaningRetry?
400invalid_requestA parameter is missing, malformed or out of range. The message names it.No — fix the request. The message names the parameter.
401unauthorizedThe key is missing, malformed or revoked.No — check the Authorization header.
403forbiddenThe key is valid but your plan does not include this dataset.No — upgrade the plan or drop the dataset.
404not_foundNo resource with that id on this dataset.No.
429rate_limitedPlan rate exceeded. Retry-After gives the wait in seconds.Yes — after Retry-After.

A 5xx is always safe to retry with backoff. A 4xx never is: the request itself needs changing, and replaying it just spends quota. 403 is in the contract so your client can handle it, but it cannot occur today — every plan reaches every live dataset, and per-dataset entitlements are not built yet.