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

ParameterWhat it doesDefaultAllowed
limitHow many results to return251 to 100
offsetHow many results to skip first00 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
}
  • items is this page's results.
  • total is how many results there are across all pages.
  • limit and offset echo what the server used, defaults included.

There's a next page while offset + limit is less than total.

Which endpoints are paginated

Endpointlimit defaultlimit maxNotes
GET /v1/events25100Upcoming events, soonest first
GET /v1/blog/posts25100Newest first, with an optional tag filter
GET /v1/groups/{group}/rows100500Also 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:

EndpointShape
GET /v1/products{ "items": [...] }
GET /v1/collections{ "items": [...] }
GET /v1/services{ "items": [...] }
GET /v1/bookings/typesA plain array
GET /v1/bookings/types/{slug}/slotsA 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.

On this page