List reservations with filtering
Reservations · GET /v1/reservations
List reservations with filtering
/v1/reservationsRequest
curl https://api.repull.dev/v1/reservations?limit=50 \ -H "Authorization: Bearer $REPULL_API_KEY"
Response
{
"data": [
{
"id": "123",
"listingId": "123",
"guestId": "123",
"checkIn": "2026-06-01",
"checkOut": "2026-06-01",
"status": "confirmed",
"source": "airbnb",
"platform": "airbnb",
"confirmationCode": "Example text",
"totalPrice": "Example text",
"currency": "USD",
"guestDetails": {
"numberOfPets": 0,
"numberOfAdults": 0,
"numberOfGuests": 0,
"numberOfInfants": 0,
"numberOfChildren": 0
},
"primaryGuest": {
"id": "123",
"firstName": "Example",
"lastName": "Example",
"phone": "+15550000000",
"language": "Example text"
},
"occupancy": {
"adults": 0,
"children": 0,
"infants": 0,
"pets": 0,
"total": 0
},
"financials": {
"totalPrice": 0,
"currency": "USD",
"paymentStatus": "pending",
"cancellationPolicy": "Example text",
"host": {
"accommodation": 0,
"discounts": [],
"guestFees": [
{
"name": "Example",
"type": "guest_service",
"amount": 0,
"vat": 0
}
],
"hostFees": [
{
"name": "Example",
"type": "host_service",
"amount": 0,
"vat": 0
}
],
"taxes": [],
"revenue": 0
},
"guest": {
"totalPrice": 0,
"fees": [
{
"name": "Example",
"type": "cleaning",
"amount": 0
}
],
"taxes": []
}
},
"createdAt": "2026-06-01T12:00:00.000Z",
"updatedAt": "2026-06-01T12:00:00.000Z",
"bookedAt": "2026-06-01T12:00:00.000Z",
"guestName": "Example"
}
],
"pagination": {
"nextCursor": "eyJpZCI6MTIzfQ==",
"hasMore": true,
"total": 0
}
}Captured from a real response. Regenerated on every release, and CI fails if this shape stops matching the API.
Query Parameters
accountstringOnly one connected account's records, as `provider:externalAccountId` (e.g. `airbnb:79730216`, `booking:10167991`, `vrbo:36`, `hostaway:…`) — the pair on every webhook `account` block, on `connect.session.completed`, and on each record's `account`. See /for-platforms.
limitintegerPage size (max 100). Requests over the cap return 422.
cursorstringOpaque cursor returned in the previous response's `pagination.nextCursor`. Omit to fetch the first page.
offsetintegerFirst-class alias for cursor-based pagination. Mutually exclusive with `cursor` — passing both returns 422. Accepts integers in `[0, 10000]`; deeper walks must use `cursor` (constant per-page cost). The response always includes `pagination.nextCursor` so consumers can switch from offset → cursor mid-walk for deep pagination without re-keying.
platformstringFilter by booking platform
statusstringFilter by lifecycle status. **Case-insensitive** — `confirmed`, `Confirmed`, and `CONFIRMED` all match. Each public value expands to the full set of internal sub-states server-side: `confirmed` matches `accept`/`confirmed`/`modified`, `cancelled` matches every cancellation sub-state (`cancelled_by_host`, `declined`, `expired`, etc.), `pending` includes `inquiry`/`awaiting_payment`. `completed` is a derived state — combine `status=confirmed` with `check_out_before=<today>` to filter for past stays. `pending` lists only Airbnb booking requests that can still be answered, each with `respondBy`; one that lapsed (24 hours after the guest asked, or its check-in passed) appears under `cancelled` with `statusDetail: "request_expired"` instead.
listingIdintegerFilter to a single listing
check_in_afterdateCheck-in date >= this value
check_in_beforedateCheck-in date <= this value
check_out_afterdateCheck-out date >= this value
check_out_beforedateCheck-out date <= this value
checkInFromdateDeprecated alias for `check_in_after`.
checkInTodateDeprecated alias for `check_in_before`.
checkInAfterdateUse `check_in_after` (snake_case) instead.
checkInBeforedateUse `check_in_before` (snake_case) instead.
checkOutAfterdateUse `check_out_after` (snake_case) instead.
checkOutBeforedateUse `check_out_before` (snake_case) instead.
updated_sincedateIncremental sync: return only records whose `updatedAt` is at or after this instant. This is the only filter on record **mutation** time — every `check_*` filter targets guest **stay** dates. **Accepted formats.** ISO 8601, with `Z` or a numeric offset — both work: - `2026-08-01T00:00:00Z` - `2026-08-01T00:00:00.123Z` - `2026-08-01T00:00:00+00:00` - `2026-08-01T02:30:00-07:00` (offset colon optional: `-0700`) - `2026-08-01T00:00` (seconds optional) - `2026-08-01T00:00:00` — no zone designator, interpreted as **UTC** - `2026-08-01` — date only, means midnight UTC Anything else returns 422 `invalid_params` naming the field; the value is never silently ignored. **Ordering changes when you pass this.** Results are ordered `updatedAt ASC, id ASC` (instead of the endpoint default) and the cursor keys on the same pair. That is required for correctness: under the default ordering a record amended mid-walk can move behind the cursor and never be emitted — which is exactly the event you are polling for. Ascending mutation time is monotonic with the cursor, so anything touched during a walk resurfaces later in it or on the next poll. **Cursors are not interchangeable between the two orderings.** Keep `updated_since` on every page of an incremental walk; replaying a cursor from the other ordering returns 422 rather than a page that silently skips rows. **Watermark.** The bound is inclusive (`updatedAt >= value`), so the last row of the final page is the watermark for the next poll — re-polling with it re-emits that row. Delivery is at-least-once; upsert by `id`.
include_totalbooleanWhen `true` (default), the response's `pagination.total` carries the count of rows matching the current filter, across all pages. Pass `false` to skip the count for very large workspaces where the per-page COUNT(*) cost matters.