Room availability & rates API
Extract comprehensive room details and listed rates from Booking.com hotels by hotel ID.
Copy for your AI agent
Paste into Claude, ChatGPT, Cursor or Copilot — endpoint, parameters, response fields and a curl example in Markdown.
Point of sale and display settings
Pass market=ES for Spain (Spanish and EUR defaults) or market=US for the United States (English and USD). The optional language and currency parameters override these defaults. Market is an ISO 3166-1 two-letter country code; unknown codes and conflicting market/country_market values return 400.
Legacy country_market remains supported and keeps English/USD defaults. Omitting both retains US targeting and English/USD. For search by URL, explicit language/currency query parameters override values embedded in the URL; explicit URL values override market defaults.
Defaults: ES es/EUR, US en-us/USD, GB en-gb/GBP, DE de/EUR, FR fr/EUR, IT it/EUR, PT pt-pt/EUR, NL nl/EUR, IE en-gb/EUR, CA en-us/CAD, AU en-gb/AUD, JP ja/JPY. Other valid countries use en-us/USD unless overridden.
data.effective_market reports requested_country, nullable targeted_country, language, and currency. Targeting is best-effort: country_verified is false and verified_country is null because the response does not independently verify geography. A null target means country targeting was not applied. MCP returns the same object as effective_market.
{"requested_country":"ES","targeted_country":"ES","country_verified":false,"verified_country":null,"language":"es","currency":"EUR"}
For prices, total_price_value follows the displayed gross basis and is not guaranteed to include every charge. taxes_and_charges_value is the included portion (gross minus net); inspect tax_breakdown[].included for excluded taxes.
Compare the same stay, occupancy, room, rate conditions and currency. Market can change tax inclusion and displayed totals without changing the all-in price; it does not guarantee a discount or availability. In the verified Spain/US comparison, the same rate displayed US$455 including taxes in Spain and $341 excluding taxes in the US, with the same $455.46 all-in amount.
Overview
Live data
Returns normalized live room inventory and rate data.
The Room availability and rates endpoint returns static per-room information (descriptions, photos, bed configurations, highlights) alongside the listed rates available for a specific date range (prices, refund policies, meal plans, availability). For a catalog independent of dates and occupancy, use the Room types endpoint.
Endpoint URL
Need a hotel ID?
Use /v1/booking/hotel/url-to-id to convert a Booking.com URL or slug to a numeric hotel ID.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| hotel_id | integer (> 0) | Required |
Numeric Booking.com hotel ID (for example, 346648).
|
| checkin | string (YYYY-MM-DD) | Optional | Arrival date. Defaults to today + 14 days. |
| checkout | string (YYYY-MM-DD) | Optional | Departure date. Defaults to today + 15 days. |
| adults | integer | Optional | Number of adults (1–30, default: 2). |
| children | integer | Optional | Number of children (0–10, default: 0). |
| rooms | integer | Optional | Number of rooms (1–8, default: 1). |
| currency | string (ISO 4217) | Optional |
Display currency override (market default, otherwise USD). Examples: EUR, GBP, THB.
|
| country_market | string (ISO-3166) | Optional |
Legacy two-letter country target (default: US), preserving English/USD defaults. It must match market if both are supplied. The echo is a request value, not verified geography.
|
| children_ages | string | Optional | Comma-separated integer ages 0–17, for example 5,8. Required when children is positive; the age count must equal children. Omit or leave empty for zero children. |
Response Structure
Top-level fields
success— boolean, alwaystruefor 2xx responses.data.hotel_info— basic property info (id,currency_code).data.rooms— array of room types with static data (see below).data.rates— array of listed blocks for the requested dates (see below).data.availability_summary— deduplicated inventory totals acrossdata.rates:availability_status—availablewhen at least one listed rate was returned, otherwiseno_inventory.total_available_rooms— sum of the maximumroom_count_availableperroom_id, so a room type sold under several rate plans is counted once.nullwhen there are no listed rates.available_rate_blocks— number of entries indata.rates.available_room_types_counted— number of distinctroom_ids that contributed a count.room_counts_by_room_id— the per-room-type counts that were summed.rates_without_room_count_available— rate blocks returned without a usable count.calculation_method— human-readable formula string.
data.search_config— echoes the guest/date configuration you requested.data.metadata—hotel_id,total_rooms,total_blocks,arrival_date,departure_date.retrieved_at— ISO-8601 UTC timestamp.
Per-room fields (data.rooms[])
id— stable room-type identifier.name— human-readable room-type name (derived from the first associated rate).description— full room description (may contain HTML formatting).ai_description— short AI-generated comparative description (may contain<b>tags).room_size_m2,room_size_ft2— room size in metric and imperial units.is_smoking,is_dormitory— booleans.bathroom_count— number of bathrooms;nullwhen Booking.com doesn't list a count (common for hotel rooms).private_bathroom—truefor a private bathroom,falsefor a shared one,nullwhen unknown.photos[]— array withid,url_original,url_max300,url_square60,url_640x200, andratio.bed_configurations[]— each has abedsarray withbed_type,name,name_with_count,count,description,description_imperial.highlights[]— each hasid,name,icon.
Per-rate fields (data.rates[])
Each entry represents one listed "block" — a specific combination of a room type, a cancellation/refund policy, and a meal plan. A single room type can appear multiple times (for example, refundable vs non-refundable).
block_id— unique identifier for this listed option.room_id— links this rate to an entry indata.rooms[].name— full rate name, e.g. "Laguna Twin Room - Non-refundable".room_name— the room-type name without the policy suffix.max_occupancy,nr_adults,nr_children.refundable,refundable_until— cancellation policy. cancellation_policy_title is a localized plain-text policy summary, or null when unavailable. cancellation_deadline_date is the property-local calendar date of the free-cancellation deadline in YYYY-MM-DD format, or null when unavailable or not applicable. It does not specify an exact cutoff time or time zone and is not a promise that the whole day is refundable. Refer to the rate policy for conditions. refundable_until remains an exact timestamp or null; cancellation_policy is unchanged.breakfast_included,half_board,full_board,all_inclusive,mealplan— meal configuration.room_count_available— how many rooms of this type Booking.com will sell for the requested dates. It is bounded by the property’s allotment on Booking.com, not its physical inventory, and is typically identical across rate blocks sharing the sameroom_id, so do not sum it across rate blocks. Usedata.availability_summary.total_available_roomsfor a deduplicated total, and compare values only within one property across dates.genius_discount_percentage— Booking.com Genius loyalty discount if applicable.price— object withtotal,per_night,currency,formatted, plusbefore_discount(Booking.com's struck-through price, e.g."$642") anddiscount_name(e.g."Bonus savings") when the property offers a discount, otherwisenull. Booking.com occasionally returns a rate without the discount, so a higher price with a nullbefore_discountis an undiscounted quote.
Usage Tips
Static vs. dynamic data
rooms[] changes rarely (new photos, re-categorisation) — safe to cache for hours or days. rates[] is dynamic (availability, pricing) — refresh per user query.
Best Practices
- Join
rates[].room_idback torooms[].idto display photos next to each listed rate. - The hotel name may be unavailable here — call /v1/booking/hotel/details if you need it.
- Empty
roomsandratesarrays with a 200 status mean the property has no availability for the requested dates — try different dates rather than retrying. - If you only have a URL, call
/v1/booking/hotel/url-to-idonce and cache the resultinghotel_id. - To override the market currency, pass the
currencyparameter —hotel_info.currency_codeis the property's native currency when known, otherwise null. Userates[].price.currencyfor every quoted amount.
Error handling and status codes
Status codes: 200 on success; errors use non-2xx responses with RFC 7807 Problem Details.
- 422: Missing or invalid
hotel_id(not a positive integer). - 404
NOT_FOUND: Upstream reports the hotel does not exist. - 502
UPSTREAM_ERROR: Upstream non-2xx or protocol error. - 503
SERVICE_UNAVAILABLE: Upstream WAF challenge or maintenance. - 504
GATEWAY_TIMEOUT: Upstream response timed out. - 500
INTERNAL_ERROR: Unexpected processing error.
Error responses (Problem Details)
All Booking.com hotel endpoints return errors using RFC 7807 Problem Details.
{
"type": "https://api.stayapi.com/errors/invalid-url",
"title": "Invalid URL",
"status": 400,
"detail": "Parameter 'url' must be a full Booking.com hotel URL of the form https://www.booking.com/hotel/{cc}/{slug}.html.",
"error_code": "INVALID_URL",
"provided": "baan-coconut"
}
Media type: application/problem+json
Standard fields: type, title, status, detail
Extensions: error_code and any endpoint-specific fields
2xx policy: 2xx is returned only when data extraction succeeds.
Family occupancy
For two adults and a child aged eight, send adults=2&children=1&children_ages=8. Multiple ages use a single CSV value, for example children=2&children_ages=5%2C8. Missing ages, count mismatches, noninteger ages and ages outside 0–17 return HTTP 400 Problem Details with error_code: INVALID_OCCUPANCY. Invalid count types or counts outside 0–10 return HTTP 422. Adults-only requests can omit both child parameters.
Each data.rates[] includes fits_requested_occupancy: true means that one rate accommodates the entire requested party, false means it does not, and null means the fit is unknown. Room alternatives may accommodate only part of your party. Desktop-listed family and multiroom offers report null because no guest allocation is verified. The prices summary selects a verified full-family rate; if none can be verified, prices returns HTTP 502 instead of a misleading family quote. Prices describe one room for the stay, not a combined multiroom reservation total. The rooms parameter does not establish an allocation of guests across rooms.
Recommended offers
When data.inventory_scope is recommended, the response contains a limited selection of recommended offers. Counts and summaries describe only the returned offers, not complete property inventory. desktop_listed identifies the returned desktop offer selection, without a guarantee of complete inventory or bookability. The field may be omitted for other responses. If offers cannot be obtained reliably, the API returns a non-2xx Problem Details error, including 502 UPSTREAM_ERROR, rather than a false empty result.
Use the quoted amounts and tax fields as returned. Do not calculate an anonymous public price from a Genius percentage or a strikethrough amount.
| Field | Meaning |
|---|---|
data.inventory_scope |
Optional string: recommended or desktop_listed. Counts cover returned offers only. |
data.rates[].price.requires_sign_in |
Boolean or null: true for an observed sign-in condition; null when unknown. |
Quote taxes and missing values
When supplied, price.tax_basis is included, excluded, mixed, or unknown. price.taxes_included is boolean or null. charges_included and charges_excluded are null or objects with nullable amount, formatted, currency, and item_type. Preserve these amounts separately; do not add included charges again. Unknown taxes are not zero taxes.
Room facilities are available from the static room-type catalog, not rooms[]. Unobserved cancellation, meal, room, and source-date values can be null; null cancellation is not non-refundable. Requested dates remain in search_config. occupancy_total, when present, is the priced guest count, without an inferred adult/child split.
Rate labels
each data.rates[].price object includes labels (an array of readable strings), genius_discount_pct (a number or null), and is_mobile_rate (a boolean). Labels describe the quoted rate, for example Mobile rate or Bonus savings. Unlabelled rates return [].
Quotes can carry sign-in conditions. requires_sign_in: true identifies an explicitly observed sign-in condition; null means eligibility is unknown, not a public-rate guarantee. Genius and member labels alone do not establish a sign-in requirement or prove a discount percentage. A null percentage means no rate-specific percentage was observed; is_mobile_rate: false means no explicit mobile-rate marker was observed. Labels can vary by market and occupancy and do not guarantee eligibility for logged-in member prices. Existing discount fields remain available.