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.
| Status | Code | Meaning | Retry? |
|---|---|---|---|
| 400 | invalid_request | A parameter is missing, malformed or out of range. The message names it. | No — fix the request. The message names the parameter. |
| 401 | unauthorized | The key is missing, malformed or revoked. | No — check the Authorization header. |
| 403 | forbidden | The key is valid but your plan does not include this dataset. | No — upgrade the plan or drop the dataset. |
| 404 | not_found | No resource with that id on this dataset. | No. |
| 429 | rate_limited | Plan 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.