Skip to content

Pagination

List endpoints are cursor-paginated. Cursors are stable and efficient even over large datasets, and there is deliberately no total count.

Every list response has the same shape:

{
"object": "list",
"data": [ { "object": "reservation", "id": "resv_…" } ],
"has_more": true
}
  • data — the page of results.
  • has_more — true if more results exist after this page.

There is no total_count: computing an exact total is expensive and race-prone on live data. Page until has_more is false.

Without updated_since, each list endpoint has a stable default sort, with the resource id as a tie-breaker so the keyset cursor is total:

Endpoint Default order
GET /v1/reservations by service_date descending, then id descending — the most recent service dates first. Within one service date, bookings come in the order they were created, newest first, not in order of start_time.
GET /v1/holds by created_at descending, then id descending — most recently taken holds first.
GET /v1/waitlist-entries by created_at descending, then id descending — most recently created entries first.
GET /v1/guests by created_at descending, then id descending — most recently created guests first.
GET /v1/reservations/{id}/events by created_at ascending, then id ascending — oldest event first (chronological).

Passing updated_since to /v1/reservations, /v1/guests or /v1/waitlist-entries changes the sort to (updated_at, id) ascending (see below), so a “changed since” feed reads oldest-change-first.

Parameter Description
limit Page size. Default 20, maximum 100. (When you expand objects, the maximum is lowered.)
starting_after The id of a resource in the list (resv_…, hold_…, wl_…, gst_…, evt_…). Returns the page of results after that object.
ending_before The id of a resource in the list. Returns the page of results before that object.

A cursor is always the id of a resource you received. Every list endpoint, the events list included, accepts both parameters.

Use the id of the last item in a page as starting_after to fetch the next page:

Terminal window
# First page
curl "https://api.useservice.app/v1/reservations?limit=20" \
-H "Authorization: Bearer $SERVICE_API_KEY"
# Next page — pass the last id you saw
curl "https://api.useservice.app/v1/reservations?limit=20&starting_after=resv_8xKQ2m4Vd0pErJ7sN1aZ9bQ" \
-H "Authorization: Bearer $SERVICE_API_KEY"

Keep going while has_more is true.

List endpoints accept filters alongside the pagination parameters (see the API reference for the full set per endpoint). A few matching rules worth knowing:

  • email (guests) is matched case-insensitively as an exact address — [email protected] and [email protected] return the same guest.
  • phone (guests) is normalized to E.164 before matching, using the restaurant’s country. Pass the number however the guest gave it (local or international); it is canonicalized before the lookup.
  • query (guests) is an accent-insensitive substring match over first name, last name, and email.

Filters combine with AND. An unknown filter value (for example an unknown status or a malformed date) returns a 400 with the offending param — see Errors.

To keep a local copy in sync without re-reading everything, pass updated_since (an ISO-8601 timestamp) to GET /v1/reservations, GET /v1/guests or GET /v1/waitlist-entries. You get back only records changed at or after that time, sorted by (updated_at, id) ascending. Combine it with cursor pagination as usual:

Terminal window
curl "https://api.useservice.app/v1/reservations?updated_since=2026-06-27T00:00:00Z&limit=100" \
-H "Authorization: Bearer $SERVICE_API_KEY"

On the next sync, use the most recent updated_at you have seen as the new updated_since.