Pagination
How list endpoints split long results into pages, and which ones return everything at once.
Lists that can grow long come back one page at a time. You choose the page with two query parameters, and the response tells you how many results there are in total.
Page parameters
| Parameter | What it does | Default | Allowed |
|---|---|---|---|
limit | How many results to return | 25 | 1 to 100 |
offset | How many results to skip first | 0 | 0 or more |
Both are optional. A limit outside the allowed range is refused with a 400. An offset past the end returns an empty page.
curl "https://api.lahuta.org/v1/events?limit=10&offset=10" \
-H "X-Api-Key: $LAHUTA_API_KEY"The page envelope
A paginated response wraps the results with the numbers you need to build "previous" and "next" links:
{
"items": [{ "shortCode": "k7Qm2x", "name": "Intro to the wheel" }],
"total": 14,
"limit": 10,
"offset": 10
}itemsis this page's results.totalis how many results there are across all pages.limitandoffsetecho what the server used, defaults included.
There's a next page while offset + limit is less than total.
Which endpoints are paginated
| Endpoint | limit default | limit max | Notes |
|---|---|---|---|
GET /v1/events | 25 | 100 | Upcoming events, soonest first |
GET /v1/blog/posts | 25 | 100 | Newest first, with an optional tag filter |
GET /v1/groups/{group}/rows | 100 | 500 | Also needs view. Results are in rows, not items |
Group rows allow bigger pages because a site usually shows a whole group at once, like a team directory. See Contacts and groups.
Endpoints that return everything
These return the full list in one response, with no page parameters:
| Endpoint | Shape |
|---|---|
GET /v1/products | { "items": [...] } |
GET /v1/collections | { "items": [...] } |
GET /v1/services | { "items": [...] } |
GET /v1/bookings/types | A plain array |
GET /v1/bookings/types/{slug}/slots | A plain array for the date range you ask for, up to 62 days |
Fetch every page
Most sites only need the first page. If you do need everything, for example to build a static archive of your blog, loop until you've read total results:
async function allPosts() {
const posts = []
let offset = 0
while (true) {
const res = await fetch(
`https://api.lahuta.org/v1/blog/posts?limit=100&offset=${offset}`,
{ headers: { "X-Api-Key": process.env.LAHUTA_API_KEY } }
)
const page = await res.json()
posts.push(...page.items)
offset += page.limit
if (offset >= page.total) return posts
}
}New posts or events can appear between two requests, so a result may shift by one position across pages. For a public listing that's rarely noticeable. If it matters, fetch the whole list at once with a larger limit.