Searching and booking stays
Look up destinations, search stays, pick a fare, and create a stay booking.
Stays follow the standard four-step flow: search → fares → build → book.
Search and fares stream results as Server-Sent Events (SSE); build and book
return JSON. All requests require the consumer HMAC and a traveler bearer
token — omitted from the snippets below for brevity.
("Stays" is Mercury's canonical name for lodging — hotels, apartments, and other
accommodation.)
1. Look up destinations
Resolve free-text input to destination and property identifiers.
GET /v1/stays/destinations?q=rome&limit=5
Queries shorter than two characters return an empty result. See the
API Reference for the response shape.
2. Search
POST /v1/stays/search streams stay solutions for a destination and date range
as SSE. If the requested stay exceeds the configured maximum nights, the stream
terminates with event: error and code MAX_NIGHTS_EXCEEDED; other provider
failures use PROVIDER_ERROR.
Request body (StaySearchRequest) — provide either a destination or a
specific property:
{
"destination": "rome-city-id",
"checkIn": "2026-02-10",
"checkOut": "2026-02-12",
"rooms": [{ "adults": 2, "childrenAges": [] }],
"passengers": { "adults": 2 },
"nationality": "IT"
}Each streamed solution's id is the searchSolutionId for the next step.
catalog-item frames (flag-gated, additive)
catalog-item frames (flag-gated, additive)When catalog-item streaming is enabled for your account, the stream begins
with a burst of event: catalog-item frames a few seconds before the first
priced event: solution frames: one frame per candidate hotel (relevance
order — negotiated tier first, then the curated catalog priority rank, with
static tiebreakers: review score, star rating, image presence, distance —
capped), carrying the static property subset — same field names and
casing as the solution shape, minus id and every price/fare-derived field:
event: catalog-item
data: {"name": "...", "city": "...", "countryCode": "IT", "checkIn": {...},
"checkOut": {...}, "rating": 4, "images": [...], "propertyId": "018f...",
"searchSolutionId": "018f...", "distanceFromSearchMeters": 420, ...}
- Join key: the later
solutionframe for the same hotel carries the
samepropertyId— treatcatalog-itemas insert-if-absent and
solutionas upsert-by-propertyIdto hydrate prices in place. - Tappable immediately: a
catalog-item'ssearchSolutionId+
propertyIdare valid forPOST /v1/stays/faresright away, before any
price has streamed. - Additive: clients that do not recognise the event type can ignore it;
solutionframes are unchanged. Some hotels may never receive a price
during the search — their cards simply never hydrate.
3. List fares
POST /v1/stays/fares streams the bookable room fares for a chosen solution.
Request body (StayFaresBody):
{
"searchSolutionId": "018f...",
"checkIn": "2026-02-10",
"checkOut": "2026-02-12",
"rooms": [{ "adults": 2, "childrenAges": [] }],
"isPersonal": false
}External fares are withheld by default. Pay-at-property (external) fares
are not surfaced unless your environment enables them. Fares returned here are
prepaid through BizAway.
4. Build the fare
POST /v1/stays/fares/build locks a specific fare and returns it as JSON
(201).
Request body (StayBuildFareBody):
{ "fareId": "018f...", "searchSolutionId": "018f...", "isPersonal": false }The response carries a totalPrice as { "amount": "320.00", "currency": "EUR" }. See the API Reference for the full fare shape.
5. (Optional) Protection plans
GET /v1/stays/fares/{fare_id}/protection-plans
6. Book
POST /v1/stays/bookings creates the booking from the built fareId. Send a
unique Idempotency-Key header (required — see
Bookings and trips).
Request body (BookStayRequest):
{
"fareId": "018f...",
"passengers": [
{
"firstName": "Ada",
"lastName": "Lovelace",
"type": "adult",
"isHolder": true
}
],
"isPersonal": false,
"tripId": null
}For stays, the booking holder's phone number is taken from the authenticated
traveler's profile. Manage the booking lifecycle with the shared endpoints in
Bookings and trips.
Some fares require a birthday on every adult guest — if one is missing,
Mercury returns 422 with field-level code GUEST_BIRTHDAY_REQUIRED,
naming the specific guest, before any provider call is made.
Multi-room bookings
If you searched and priced a fare across two or more rooms (step 2's
rooms array had more than one entry), send rooms on the booking too so
Mercury knows which guest sleeps in which room. Each entry lists the
zero-based indices of passengers assigned to that room:
{
"fareId": "018f...",
"passengers": [
{ "firstName": "Ada", "lastName": "Lovelace", "type": "adult", "isHolder": true },
{ "firstName": "Grace", "lastName": "Hopper", "type": "adult", "isHolder": false }
],
"rooms": [{ "passengerIndices": [0] }, { "passengerIndices": [1] }],
"isPersonal": false
}Every adult in passengers must be referenced by exactly one room, and
exactly one referenced passenger must have isHolder: true — Mercury
returns 422 (with PASSENGER_COUNT_MISMATCH, STAY_ROOM_COUNT_MISMATCH,
or STAY_ROOM_OCCUPANCY_MISMATCH as the field-level code) if the room split
doesn't add up or doesn't match the fare you built. Omit rooms entirely
for a single-room booking — behavior is unchanged.
See the API Reference for the complete request and
response schemas.
Reporting issues
This page is maintained by the BizAway integration team. To request changes or
report an error, contact your BizAway integration contact. Edits made directly
in the ReadMe dashboard are overwritten on the next publish.
Updated 16 days ago