GET

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

GET https://api.stayapi.com/v1/booking/hotel/rooms

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, always true for 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 across data.rates:
    • availability_status — available when at least one listed rate was returned, otherwise no_inventory.
    • total_available_rooms — sum of the maximum room_count_available per room_id, so a room type sold under several rate plans is counted once. null when there are no listed rates.
    • available_rate_blocks — number of entries in data.rates.
    • available_room_types_counted — number of distinct room_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; null when Booking.com doesn't list a count (common for hotel rooms). private_bathroom — true for a private bathroom, false for a shared one, null when unknown.
  • photos[] — array with id, url_original, url_max300, url_square60, url_640x200, and ratio.
  • bed_configurations[] — each has a beds array with bed_type, name, name_with_count, count, description, description_imperial.
  • highlights[] — each has id, 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 in data.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 same room_id, so do not sum it across rate blocks. Use data.availability_summary.total_available_rooms for a deduplicated total, and compare values only within one property across dates.
  • genius_discount_percentage — Booking.com Genius loyalty discount if applicable.
  • price — object with total, per_night, currency, formatted, plus before_discount (Booking.com's struck-through price, e.g. "$642") and discount_name (e.g. "Bonus savings") when the property offers a discount, otherwise null. Booking.com occasionally returns a rate without the discount, so a higher price with a null before_discount is 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_id back to rooms[].id to display photos next to each listed rate.
  • The hotel name may be unavailable here — call /v1/booking/hotel/details if you need it.
  • Empty rooms and rates arrays 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-id once and cache the resulting hotel_id.
  • To override the market currency, pass the currency parameter — hotel_info.currency_code is the property's native currency when known, otherwise null. Use rates[].price.currency for 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.

Request
curl -X GET "https://api.stayapi.com/v1/booking/hotel/rooms?hotel_id=346648&checkin=2026-05-05&checkout=2026-05-06&adults=2" \
  -H "x-api-key: YOUR_API_KEY"
Response
{
  "success": true,
  "url": "hotel_id:346648",
  "data": {
    "hotel_info": {
      "id": "346648",
      "currency_code": "THB"
    },
    "rooms": [
      {
        "id": "34664813",
        "name": "One-bedroom Loft Suite",
        "description": "Offering double-height ceilings, the split-level loft overlooks the lagoon. It features a coffee machine, safe and flat-screen TV.",
        "ai_description": "<b>Double the size</b> with <b>outdoor furniture</b>, <b>patio</b>, and a <b>walk-in shower</b>.",
        "room_size_m2": 88,
        "room_size_ft2": 947.22,
        "is_smoking": false,
        "is_dormitory": false,
        "bathroom_count": 1,
        "private_bathroom": true,
        "photos": [
          {
            "id": 263386204,
            "url_original": "https://cf.bstatic.com/xdata/images/hotel/max500/263386204.jpg?k=...",
            "url_max300":  "https://cf.bstatic.com/xdata/images/hotel/max300/263386204.jpg?k=...",
            "url_square60":"https://cf.bstatic.com/xdata/images/hotel/square60/263386204.jpg?k=...",
            "url_640x200": "https://cf.bstatic.com/xdata/images/hotel/640x200/263386204.jpg?k=...",
            "ratio": 1.5
          }
        ],
        "bed_configurations": [
          {
            "beds": [
              { "bed_type": 6, "name": "Queen bed(s)", "name_with_count": "1 queen bed", "count": 1, "description": "151-180 cm wide", "description_imperial": "60-70 inches wide" }
            ]
          }
        ],
        "highlights": [
          { "id": null, "name": "Free WiFi",        "icon": "bui_wifi" },
          { "id": 11,   "name": "Air conditioning", "icon": "bui_weather_snowflake" }
        ]
      }
    ],
    "rates": [
      {
        "block_id": "34664883_88879319_2_2_0_600415",
        "room_id": "34664883",
        "name": "Laguna Twin Room - Non-refundable",
        "room_name": "Laguna Twin Room",
        "max_occupancy": 2,
        "nr_adults": 2,
        "nr_children": 0,
        "refundable": false,
        "refundable_until": null,
        "cancellation_deadline_date": null,
        "cancellation_policy_title": "Non-refundable",
        "breakfast_included": false,
        "half_board": false,
        "full_board": false,
        "all_inclusive": false,
        "mealplan": "Continental breakfast costs US$28 per person per night.",
        "room_count_available": 20,
        "genius_discount_percentage": 0,
        "fits_requested_occupancy": true,
        "price": {
          "total": 137.56,
          "per_night": 137.56,
          "currency": "USD",
          "formatted": "US$138",
          "before_discount": null,
          "discount_name": null,
          "labels": [],
          "genius_discount_pct": null,
          "requires_sign_in": null,
          "is_mobile_rate": false
        }
      }
    ],
    "availability_summary": {
      "availability_status": "available",
      "total_available_rooms": 20,
      "available_rate_blocks": 1,
      "available_room_types_counted": 1,
      "room_counts_by_room_id": { "34664883": 20 },
      "rates_without_room_count_available": 0,
      "calculation_method": "sum(max(room_count_available) grouped by room_id across bookable rate blocks)"
    },
    "search_config": {
      "nbRooms": 1,
      "nbAdults": 2,
      "nbChildren": 0,
      "childrenAges": [],
      "checkin": "2026-05-05",
      "checkout": "2026-05-06"
    },
    "metadata": {
      "hotel_id": "346648",
      "total_rooms": 6,
      "total_blocks": 24,
      "arrival_date": "2026-05-05",
      "departure_date": "2026-05-06"
    }
  },
  "message": "Successfully retrieved room details",
  "retrieved_at": "2026-04-21T11:06:59Z"
}
Browse documentation