Search hotel properties in Germany, Austria, Switzerland, the United Kingdom and the United States, and price them for a stay in the same call.
ASK BEFORE YOU SEARCH. Four things are required and this tool answers nothing without them: `checkIn`, `checkOut`, `adults` and `maxPricePerNight`. "Hotels in Berlin for a couple" is not yet a search — it names a place and a party and nothing else, so ask which nights they want, who is coming — adults AND any children, by age — and roughly what they want to spend, in ONE message, and search once they answer. Never fill these in for them: dates you chose are not their dates, and a budget read off "cheap" or off a star rating hides the very hotels they could have afforded. A call missing any of them comes back with no rows and a `hint` naming what is still to be asked.
PLACES. Pass `city` with any place name we cover to get properties around it, nearest first, each with `distanceKm` — despite the name it takes a place of any size, so a village, a resort or a borough ("Silvaplana", "Wals", "Zell am See", "Brooklyn") works as well as a city. `radius` is a value with its unit — {"value": 10, "unit": "mi"} — so you never have to convert; it defaults to 20 km and may not exceed 100 km. Prefer `city` over `query` for place-based searches: it understands about 151,800 places and returns neighbouring towns, whereas `query` only matches words in a name or description. An unresolvable place name returns `placeNotRecognised` — that means the NAME was not understood, not that the place has no hotels. When a radius returns nothing you get `nearestBeyondRadiusKm` so you can widen deliberately rather than guess.
SOMEWHERE SMALLER THAN A TOWN — A STREET, A LANDMARK, A STATION, A DISTRICT — GOES IN `coordinates`, NOT `city`. Sent as a `city`, such a name is either refused or resolved to a different place of the same name, which comes back as a confident answer about somewhere else. Estimate the point yourself, pair it with a small `radius`, and tell the guest you estimated it.
A STAY. `checkIn`, `checkOut` and `adults` are required, and when you ask how many adults, ASK WHETHER ANY CHILDREN ARE COMING TOO and send their ages in `childrenAges` — ages, not a count. A child left out is priced as though they are not coming, so the guest can book a room that will not sleep them. The moment the guest mentions a child, put it in `childrenAges` even if you do not yet know the age — send `null` for that entry: THE SEARCH IS THEN REFUSED until you replace it with the real age, which is what stops a family being quoted as a couple. With those, the answer comes back in TWO groups. `priced` holds the hotels we read real rates for: every room and rate plan with its own `price` for the WHOLE stay, in the `currency` you sent, its `cancelTerms` where we hold any and null where we do not, its photos where we have them, a `bookingUrl` carrying the guest's dates WHERE THIS ENDPOINT PUBLISHES ONE — read that field's own description rather than assuming it is there, because an endpoint that can book the room for you publishes no link for it — and `stayTotal` / `pricePerNight` for the cheapest offer. EVERY PRICE IS IN THE `currency` YOU SENT. They come back in a deliberate order that is NOT nearest first and NOT cheapest first — show them in the order you are given. Show all the rooms you are handed, not just the cheapest. `unpriced` holds everything else: hotels that do not publish rates anywhere we can read, that did not answer us, or that were past the read ceiling. Those are the same thing to you — NONE of them carries a booking link of any kind. What a row carries instead is `websiteUrl`, the property's own site, or an EMPTY `websiteUrl` when we hold no site either and there is nothing to open at all. WHEN YOU RECOMMEND A HOTEL, RECOMMEND A PRICED ONE. We hold no rate for anything in `unpriced`, so you cannot say what it costs and it may be well over what the guest wants to spend; naming one as your suggestion answers their budget with a number nobody has. Bring one up only as "I could not get a rate for X, here is their website". Offer that site as the property's website and NEVER as a way to book or check rates, since we do not know it sells rooms online — and where it is empty, say we have nowhere to send them rather than going looking for a page of your own.
AN EMPTY `priced` IS NOT "SOLD OUT", AND CHECK `aboveBudget` BEFORE CALLING IT UNREADABLE. With a `maxPricePerNight`, an empty `priced` means every hotel we priced costs more than that, and those hotels are in `aboveBudget` with real prices — quote them. Only when `aboveBudget` is empty too does an empty `priced` mean no rates could be read, and even then we get the same empty answer for a connection that failed, a party the property will not sell to, and a genuinely full hotel, and it never says which — so report the price as unknown and hand over the links, NEVER that a property is full. No ROW in this response can tell you a hotel has no space. One thing can: the `hint`, which says how many properties were left out because they told us outright that they are full — that is why they are in no group at all, and it is the only "full" this tool ever reports. It is never about a hotel you can see.
A BUDGET IS A FILTER, NOT A LABEL — AND IT IS THE GUEST'S NUMBER, NEVER YOURS. `maxPricePerNight` is required, so ask them for a figure before searching rather than sending one of your own. Words like "budget", "cheap" or "affordable" are not amounts: read as one they filter out the very hotels the guest wanted, and the answer then tells them their limit was missed when they set no limit. A guest who says they have no limit still has a most-they-would-pay — ask for that and send it. `maxPricePerNight` decides what you are handed, at both levels. `priced` holds ONLY the hotels at or under it, each carrying ONLY its rooms at or under it — so every price you read out is one the guest already agreed to, and `priced` may be shorter than the `limit` you asked for. Dearer hotels are NOT in `priced`, NOT in `unpriced`, and not returned at all while any hotel fits: do not tell the guest this is every hotel in the area, and read the `hint`, which says how many were left out. Only when NOT ONE hotel fits do they come back, in `aboveBudget` — then say what the nearest options actually cost, never "nothing is available". A room in `priced` keeps ALL of its rate plans, including one dearer than the budget, so a breakfast or flexible rate is still there to offer as an upgrade. The comparison is `pricePerNight`, the stay total divided by nights — a stay AVERAGE, not a rate charged on every night, so say it that way.
HOW MUCH ONE SEARCH RETURNS. `limit` is the ceiling across BOTH groups, priced first — an answer can be shorter when properties were dropped as full, defaulting to 10 and capped at 20. `unpriced` carries AT MOST 5 however large a `limit` you ask for, so raising `limit` buys you more PRICED hotels and never a longer list of names we hold no price for. THERE IS NO PAGING: one search returns one set of rows and there is no second page to ask for, so to reach anything past 20 properties, narrow the search — a nearer `city`, a smaller `radius`, a `query`, `country` or `starRating`. Calling again with the same filters returns the same rows. No count of the whole match comes back either, so do not tell the guest how many properties exist in a place — say what you can show them.
Also supports country (de, at, ch, us, gb), starRating (1-star, 2-star, 3-star, 4-star, 5-star) and style (boutique), which are independent of one another. Some place names exist in more than one of those countries, and `city` alone resolves them to THE MOST POPULOUS of them — "Cambridge" is England, not Massachusetts; "Bellevue" and "Salem" stay European. It is a proxy for the place a guest means, and it is sometimes wrong: "Kendal" resolves to the Kendall in Miami, not the Cumbrian town. Pass `country` alongside whenever you know which you mean, and ALWAYS when the guest named the country — it overrides the contest outright. Returns public fields only.
BOOKING A ROOM FROM THIS ANSWER. Every priced room carries an `offerId` and NO booking link, because we can book it for the guest ourselves — call `hotel_reserve` with the `offerId` rather than sending them to a website. It needs the guest's full name, email, phone and address, so ask for those once they have chosen a room. It answers one of two ways: a `confirmationNumber` means the room is booked and the guest pays the hotel at the desk, and a `paymentUrl` means the room is not held yet and that link is where the guest pays to get it booked. Where a room's `cancelTerms` is null we hold no cancellation terms for it: say so plainly, say the hotel's own terms apply and come with its confirmation, and book once the guest agrees on that basis — a blank is not a refusal.