Pagination
Collections are paged with an opaque cursor. Ask for a page, and if there is another the response tells you how to get it.
Last updated
How to page
curl 'https://api.cmskite.com/v1/blog/posts?limit=25' -H "authorization: Bearer $KEY"
# → "pagination": { "hasNext": true, "nextCursor": "eyJ2IjoxLCJzIjoi…", "limit": 25 }
curl 'https://api.cmskite.com/v1/blog/posts?limit=25&cursor=eyJ2IjoxLCJzIjoi…' \
-H "authorization: Bearer $KEY"async function* everyPost(key: string) {
let cursor: string | null = null
do {
const url = new URL('https://api.cmskite.com/v1/blog/posts')
url.searchParams.set('limit', '100')
if (cursor) url.searchParams.set('cursor', cursor)
const res = await fetch(url, { headers: { authorization: `Bearer ${key}` } })
if (!res.ok) throw new Error(`CMSKite: ${res.status}`)
const page = await res.json()
yield* page.data
cursor = page.pagination.nextCursor
} while (cursor)
}| Parameter | Default | Max | Notes |
|---|---|---|---|
limit | 25 | 100 | Outside the range is a 400, not a silent clamp |
cursor | — | — | Opaque. Pass back exactly what you were given |
Why there is no ?page=
OFFSET makes the database count and discard every row it skips. On a table with ten million rows, ?page=5000 is a ten-second query that any caller can issue at any time, and there is no fix for it that keeps the parameter.
It is also wrong in a way people do not notice: if something is created while you are paging, every later page shifts by one and you silently see a row twice or miss one entirely. A cursor points at a position in the ordering rather than at a count, so it is not affected by what arrives after you started.
What a cursor is
Base64url of a small versioned object: the sort value of the last row you saw, and its id as a tiebreaker. The version is in there from the first release, because a cursor is a public contract — a cursor from a future encoding is refused rather than misread.
The id tiebreaker is not decoration. Two posts published in the same millisecond with only a timestamp in the ordering come back in an unstable order, and a page boundary then skips one and repeats the other. Every sort carries the tiebreaker, and every timestamp a cursor is built from is stored at exactly the precision a cursor can carry.
Sorting
Sorts are a closed list, mapped to columns with indexes behind them. Anything else is a 400 UNSUPPORTED_SORT rather than a sequential scan a customer discovers as a slow endpoint.
| Value | Orders by |
|---|---|
-publishedAt | Newest published first — the default |
publishedAt | Oldest published first |
-createdAt / createdAt | When the row was made |
-updatedAt / updatedAt | When it last changed |
title / -title | Alphabetical |