Pagination
List endpoints are cursor-paginated. Cursors are stable and efficient even over large datasets, and there is deliberately no total count.
List envelope
Section titled “List envelope”Every list response has the same shape:
{ "object": "list", "data": [ { "object": "reservation", "id": "resv_…" } ], "has_more": true}data— the page of results.has_more—trueif 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.
Default order
Section titled “Default order”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.
Parameters
Section titled “Parameters”| 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:
# First pagecurl "https://api.useservice.app/v1/reservations?limit=20" \ -H "Authorization: Bearer $SERVICE_API_KEY"
# Next page — pass the last id you sawcurl "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.
Filtering
Section titled “Filtering”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.
Incremental sync with updated_since
Section titled “Incremental sync with updated_since”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:
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.