{
  "openapi": "3.1.0",
  "info": {
    "title": "Booking.com API",
    "version": "1.0.0",
    "description": "Search Booking.com properties for a destination, read one property in full, and read its guest reviews. Search always prices — and prices every bookable room option separately, each with its own original price, occupancy, free-cancellation date, meal plan and crib flag — as {amount, currency} rather than a display string. Dates are optional: with none, search picks a stay window and reports it back on checkIn/checkOut and in words, so a price is never silently for dates you did not ask about. Reviews keep what a guest liked and what they disliked in separate fields, in the language they wrote them, beside the stay dates, nights and room type they refer to, and every response states the property's full review count next to how many came back. hotel-detail is the catalogue route: room types with sizes and bed configuration, the full photo gallery, amenities and the seven-category review breakdown — it does not price a stay, and the headline review score is published on search rows and reviews rather than here."
  },
  "servers": [
    {
      "url": "https://api.fetchlayer.dev/booking"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Send your FetchLayer key as Authorization: Bearer ss-your-key."
      }
    }
  },
  "paths": {
    "/search-hotels": {
      "post": {
        "operationId": "search-hotels",
        "tags": [
          "Properties"
        ],
        "summary": "Search properties by destination",
        "description": "Search Booking.com for a destination and a stay. `destination` is the only required field — a place name like \"Paris\" or \"Lisbon, Portugal\" — and every property comes back priced, with **every bookable room option priced separately** beside it, plus its star rating, review score, badges, coordinates and photos. Money is `{amount, currency}` — a number and a currency code, never a display string to unpick. A search row carries `price` for the cheapest bookable stay **plus `roomOptions[]`, each priced on its own**, with `originalPrice`, `occupancy`, `freeCancellationUntil`, `mealPlan` and `hasCrib`. So you can compare rooms inside one property as easily as properties against each other. Every property returned was priced across the runs measured — 21 of 21 in Paris, 26 of 26 in Lisbon. **Dates are optional, and this route always prices.** Give `checkIn`/`checkOut` and every property is priced for that stay. Omit them and the search still prices — for a default stay window it chooses — and reports that window straight back on the response's `checkIn`/`checkOut` *and* in `notes[]`, so a price is never silently for dates you did not ask about. Read those two fields before comparing prices across requests made on different days. **`resultsTotal` is a match count, not a walkable promise.** It is how many properties Booking.com says match the destination; it does not mean all of them can be paged through. Read `hasMore` and `nextCursor` to decide whether there is another page. One measured Lisbon query returned 26 with `hasMore: true` and a cursor, and the same query later returned 21 with `hasMore: false`. Never divide `resultsTotal` by a page size to plan a walk. Billing: 1 credit per page fetched, reported as `pagesFetched`. A page is about 25 properties — Booking.com's own page size, not ours — and costs the same whether it comes back full or half empty; `hotelCount` is what actually arrived. Measured on 17 September 2026, three runs each: Lisbon with dates returned 200 in 3.61-3.94s with 21 properties of a `resultsTotal` of 2403, and Paris returned 200 in 3.60-3.82s with 21 of 3000. All priced.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "destination": {
                    "type": "string",
                    "description": "A place name, e.g. \"Paris\" or \"Lisbon, Portugal\". The only required field."
                  },
                  "checkIn": {
                    "type": "string",
                    "description": "Stay start date, YYYY-MM-DD. Optional — without it the search prices a default stay window and reports that window back on the response's `checkIn`. Must be given together with `checkOut`."
                  },
                  "checkOut": {
                    "type": "string",
                    "description": "Stay end date, YYYY-MM-DD. Required if `checkIn` is given, and vice versa."
                  },
                  "adults": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Adult guests, up to 30.",
                    "example": "2"
                  },
                  "children": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Children, up to 30.",
                    "example": "0"
                  },
                  "rooms": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Rooms to book, up to 30.",
                    "example": "1"
                  },
                  "pages": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "How many pages of results to collect in one request, up to 20. Each page fetched is 1 credit. Pass -1 for the maximum. A walk stops when there are no pages left, and `pagesFetched` reports what was actually read.",
                    "example": "1"
                  },
                  "cursor": {
                    "type": "string",
                    "description": "Continue from a previous response's `nextCursor`."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "destination"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "destination": "Lisbon, Portugal",
                    "checkIn": "2026-10-15",
                    "checkOut": "2026-10-18"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"booking\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /hotel-reviews this is where the \"10 of the 3854\" line lives; on /search-hotels it is where a default stay window is declared when you passed no dates; on /hotel-detail it is where the \"this route does not price a stay\" answer lives."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "destination": {
                      "type": "string",
                      "description": "The destination that was searched, echoed back."
                    },
                    "hotelCount": {
                      "type": "number",
                      "description": "How many properties came back in this response."
                    },
                    "resultsTotal": {
                      "type": "number",
                      "description": "**How many properties Booking.com reports for the destination — a match count, not a page plan.** 2403 for Lisbon and 3000 for Paris on the runs measured. It is not a promise that all of them are walkable: use `hasMore`."
                    },
                    "hasMore": {
                      "type": "boolean",
                      "description": "**The field that decides whether to page.** Whether there are further results beyond this response. The same Lisbon query returned `true` on one run and `false` on another; read it rather than computing from `resultsTotal`."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Pass back as `cursor` to continue. Null when there is nothing more."
                    },
                    "checkIn": {
                      "type": "string",
                      "description": "**The stay window the prices are actually for.** Your date when you gave one; the default window the search chose when you did not — so a price is never silently for dates you did not ask about."
                    },
                    "checkOut": {
                      "type": "string",
                      "description": "The other end of that window, on the same terms."
                    },
                    "adults": {
                      "type": "number",
                      "description": "The adult count the prices are for, echoed back."
                    },
                    "children": {
                      "type": "number",
                      "description": "As `adults`."
                    },
                    "roomsRequested": {
                      "type": "number",
                      "description": "How many rooms the prices cover."
                    },
                    "hotels": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The compact `\"countryCode/pageName\"` id, e.g. `\"pt/chiado-meets-bairro-apartment\"`. Hand it straight to /hotel-detail or /hotel-reviews as `hotel`."
                          },
                          "url": {
                            "type": "string",
                            "description": "The property's booking.com address."
                          },
                          "name": {
                            "type": "string",
                            "description": "The property name as published."
                          },
                          "address": {
                            "type": "string",
                            "description": "The street line as published — the fuller, formatted address is on /hotel-detail."
                          },
                          "city": {
                            "type": "string",
                            "description": "The city the property is in, as published."
                          },
                          "countryCode": {
                            "type": "string",
                            "description": "Two-letter country code — the same one that opens the compact id."
                          },
                          "coordinates": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{latitude, longitude}`."
                          },
                          "starRating": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{value, scheme}`. `scheme` is what the stars actually mean — measured `\"STARS\"` on a hotel and `\"TILES\"` on an apartment, which are different rating systems and not comparable without it."
                          },
                          "reviewScore": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{average, count, label}`, e.g. `{average: 8.1, count: 3854, label: \"Very Good\"}`. **This is where the headline score is populated** — it comes back null on /hotel-detail, which carries the per-category breakdown instead."
                          },
                          "badges": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Booking.com's own labels on the row, e.g. `[\"Limited-time Deal\"]`. Empty where there are none."
                          },
                          "photos": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Photo URLs for the row. The full gallery is on /hotel-detail — 51 photos on the property measured there."
                          },
                          "price": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{amount, currency}` for the cheapest bookable stay at this property over the dates searched."
                          },
                          "roomOptions": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "additionalProperties": true,
                              "properties": {
                                "roomId": {
                                  "type": "string",
                                  "description": "Booking.com's own id for that room offer."
                                },
                                "occupancy": {
                                  "type": "number",
                                  "description": "How many guests the offer is for. Can be 0 where the source published no occupancy for the offer."
                                },
                                "price": {
                                  "type": "object",
                                  "additionalProperties": true,
                                  "description": "`{amount, currency}` for this room over the stay searched."
                                },
                                "originalPrice": {
                                  "type": "object",
                                  "additionalProperties": true,
                                  "description": "`{amount, currency}` before any discount. Equal to `price` when nothing is discounted, so a deal is `originalPrice.amount > price.amount` rather than a badge to parse."
                                },
                                "freeCancellationUntil": {
                                  "type": "string",
                                  "description": "The date free cancellation runs to, where the offer carries one. Null otherwise."
                                },
                                "mealPlan": {
                                  "type": "string",
                                  "description": "What is included in the rate, in Booking.com's own wording. Null where the offer is room-only or published none."
                                },
                                "hasCrib": {
                                  "type": "boolean",
                                  "description": "Whether a crib is part of the offer."
                                }
                              }
                            },
                            "description": "Every bookable room offer, each priced on its own. This is what makes the route a price comparison rather than a listing."
                          }
                        },
                        "additionalProperties": false
                      }
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "booking",
                      "notes": [
                        "These are 26 of the 2403 properties the source reports for this search. Continue with nextCursor."
                      ],
                      "pagesFetched": 1,
                      "destination": "Lisbon, Portugal",
                      "hotelCount": 26,
                      "resultsTotal": 2403,
                      "hasMore": true,
                      "nextCursor": "26",
                      "checkIn": "2026-10-15",
                      "checkOut": "2026-10-18",
                      "adults": 2,
                      "children": 0,
                      "roomsRequested": 1,
                      "hotels": [
                        {
                          "id": "pt/chiado-meets-bairro-apartment",
                          "url": "https://www.booking.com/hotel/pt/chiado-meets-bairro-apartment.html",
                          "name": "Chiado Meets Bairro Apartment",
                          "address": "Rua da Rosa 60 1 Floor",
                          "city": "Lisboa",
                          "countryCode": "pt",
                          "coordinates": {
                            "latitude": 38.7120426,
                            "longitude": -9.1452802
                          },
                          "starRating": {
                            "value": 3,
                            "scheme": "TILES"
                          },
                          "reviewScore": {
                            "average": 7.9,
                            "count": 17,
                            "label": "Good"
                          },
                          "badges": [
                            "Limited-time Deal"
                          ],
                          "photos": [
                            "https://cf.bstatic.com/xdata/images/hotel/square600/689982408.webp?k=000258df15bd9b9f89f40f679e6e2b3c263b555db2c0533a18bac6ac1362e50e&o="
                          ],
                          "price": {
                            "amount": 642.75,
                            "currency": "EUR"
                          },
                          "roomOptions": [
                            {
                              "roomId": "1425667101",
                              "occupancy": 2,
                              "price": {
                                "amount": 642.75,
                                "currency": "EUR"
                              },
                              "originalPrice": {
                                "amount": 642.75,
                                "currency": "EUR"
                              },
                              "freeCancellationUntil": null,
                              "mealPlan": null,
                              "hasCrib": false
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/hotel-detail": {
      "post": {
        "operationId": "hotel-detail",
        "tags": [
          "Properties"
        ],
        "summary": "Read one property in full",
        "description": "Read one Booking.com property in full: its formatted address and coordinates, star rating, the seven-category review breakdown, the whole photo gallery, amenities, languages spoken at the property, its check-in and check-out windows, and **every room type it publishes** — each with a description, floor area in square metres, bed configuration, occupancy and its own photos. Seven room types and 51 photos on the property measured. A property is named either by its booking.com URL or by its compact `\"countryCode/pageName\"` id — `\"fr/du-printemps-paris\"`. Every search row carries that id in `id`, ready to hand straight to this route, and [`/resolve-url`](/documentation/booking/endpoints/resolve-url) turns a pasted link into one for free. **This route does not price a stay, and it says so when you pass dates.** `checkIn`/`checkOut` are accepted and answered with a note rather than a price: \"This route does not return a live price for this stay. Use search-hotels with the same destination and dates to get this property's priced room options.\" That is the documented answer, not a failure. [`/search-hotels`](/documentation/booking/endpoints/search-hotels) is what prices a stay — and it prices every bookable room option, not just the property. **The category detail is here; the single headline number is not.** On this route `reviewScore` comes back `{average: null, count: null, label: null}` while `reviewScoreBreakdown` carries all seven category scores in full — measured on two unrelated properties. For a property's headline score and review count, read a [`/search-hotels`](/documentation/booking/endpoints/search-hotels) row, which carries `reviewScore` populated (`{average: 8.1, count: 3854, label: \"Very Good\"}` on the property below), or [`/hotel-reviews`](/documentation/booking/endpoints/hotel-reviews), which reports `totalReviewCount` and the same seven categories. **An id that names no property is a 404 with a message** — `{\"error\":\"no property was found for \\\"zz/no-such-hotel-xyz\\\"\"}` — not a 503. A wrong id is a wrong request, so there is nothing to retry. Billing: 1 credit per page fetched, reported as `pagesFetched`. Measured on 17 September 2026: `{\"hotel\":\"fr/du-printemps-paris\"}` returned 200 in 3.62-3.93s across three runs.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hotel": {
                    "type": "string",
                    "description": "A property: its booking.com URL, or its compact `\"countryCode/pageName\"` id as returned in every search row's `id`. The only required field."
                  },
                  "checkIn": {
                    "type": "string",
                    "description": "Stay start date, YYYY-MM-DD. Accepted, but **this route does not price a stay** — the response answers with a note instead. Use /search-hotels for a priced stay. Must be given together with `checkOut`."
                  },
                  "checkOut": {
                    "type": "string",
                    "description": "Stay end date, YYYY-MM-DD. Required if `checkIn` is given, and vice versa. Same caveat: no price comes back."
                  },
                  "adults": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Adult guests, up to 30.",
                    "example": "2"
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "hotel"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "hotel": "fr/du-printemps-paris"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"booking\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /hotel-reviews this is where the \"10 of the 3854\" line lives; on /search-hotels it is where a default stay window is declared when you passed no dates; on /hotel-detail it is where the \"this route does not price a stay\" answer lives."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "hotel": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "The compact `\"countryCode/pageName\"` id, echoed back — useful when you passed a URL."
                        },
                        "url": {
                          "type": "string",
                          "description": "The property's canonical booking.com address."
                        },
                        "name": {
                          "type": "string",
                          "description": "The property name as published."
                        },
                        "address": {
                          "type": "string",
                          "description": "The full formatted address, e.g. `\"1 RUE DE L'ISLY, 8th arr., 75008 Paris, France\"` — fuller than the street line a search row carries."
                        },
                        "city": {
                          "type": "string",
                          "description": "The city the property is in."
                        },
                        "countryCode": {
                          "type": "string",
                          "description": "Two-letter country code."
                        },
                        "coordinates": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "`{latitude, longitude}`."
                        },
                        "starRating": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "`{value, scheme}`. `scheme` can be null here where a search row carried one — the number is the reliable half on this route."
                        },
                        "reviewScore": {
                          "type": "string",
                          "description": "**`{average: null, count: null, label: null}` on this route**, measured on two unrelated properties. The headline score is published on a /search-hotels row and the review count on /hotel-reviews; what this route carries is the category breakdown below."
                        },
                        "reviewScoreBreakdown": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "description": "The property's score split seven ways — Staff, Facilities, Cleanliness, Comfort, Value for money, Location, Free Wifi — each as `{category, label, average}`, where `category` is the stable key (`hotel_staff`, `hotel_services`, `hotel_clean`, `hotel_comfort`, `hotel_value`, `hotel_location`, `hotel_free_wifi`) and `label` the human one. A property strong on location and weak on facilities does not look like its opposite: the measured example scores 9.44 on Location against 7.82 on Facilities behind a single 8.1."
                        },
                        "photos": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "description": "`[{url, caption}]` — the full gallery, 51 photos on the property measured, against the handful a search row carries. `caption` is the source's own description of the picture."
                        },
                        "amenities": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "description": "`[{title, group}]`. `title` is the amenity in the source's own wording (\"Breakfast\", \"Pet friendly\"); `group` is the heading it sits under where one is published, null otherwise."
                        },
                        "languagesSpoken": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "The languages the property says its staff speak. Empty where the property published none — it was empty on the property measured."
                        },
                        "rooms": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "description": "`[{name, description, sizeSquareMeters, bedConfiguration, occupancy, photos}]` — **the property's room catalogue**, seven entries on the property measured. `sizeSquareMeters` is a number (19, 21), `description` is the source's full paragraph, and each room carries its own photo list. `bedConfiguration` can be an empty string where the property published none. These are room *types*, not priced offers: for prices per room, call /search-hotels."
                        },
                        "checkIn": {
                          "type": "string",
                          "description": "The property's own check-in window, e.g. its earliest and latest arrival time. Null when you passed no dates."
                        },
                        "checkOut": {
                          "type": "string",
                          "description": "The check-out window, on the same terms."
                        }
                      },
                      "additionalProperties": false
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "booking",
                      "notes": [],
                      "pagesFetched": 1,
                      "hotel": {
                        "id": "fr/du-printemps-paris",
                        "url": "https://www.booking.com/hotel/fr/du-printemps-paris.html",
                        "name": "HOTEL DU PRINTEMPS",
                        "address": "1 RUE DE L'ISLY, 8th arr., 75008 Paris, France",
                        "city": "Paris",
                        "countryCode": "fr",
                        "coordinates": {
                          "latitude": 48.87476116422678,
                          "longitude": 2.326435976686483
                        },
                        "starRating": {
                          "value": 3,
                          "scheme": null
                        },
                        "reviewScore": {
                          "average": null,
                          "count": null,
                          "label": null
                        },
                        "reviewScoreBreakdown": [
                          {
                            "category": "hotel_staff",
                            "label": "Staff",
                            "average": 8.94481086730957
                          },
                          {
                            "category": "hotel_services",
                            "label": "Facilities",
                            "average": 7.824008941650391
                          },
                          {
                            "category": "hotel_clean",
                            "label": "Cleanliness",
                            "average": 8.511276245117188
                          },
                          {
                            "category": "hotel_comfort",
                            "label": "Comfort",
                            "average": 8.569668769836426
                          },
                          {
                            "category": "hotel_value",
                            "label": "Value for money",
                            "average": 8.086325645446777
                          },
                          {
                            "category": "hotel_location",
                            "label": "Location",
                            "average": 9.435894966125488
                          },
                          {
                            "category": "hotel_free_wifi",
                            "label": "Free Wifi",
                            "average": 8.791600227355957
                          }
                        ],
                        "photos": [
                          {
                            "url": "https://cf.bstatic.com/xdata/images/hotel/max200/327849635.jpg?k=c66b0bb35f9aeca8fb81238f4ad75ed87a0d480299e48ca825f458e3d6b99c72&o=",
                            "caption": "a large white building with people walking in front of it at HOTEL DU PRINTEMPS in Paris"
                          }
                        ],
                        "amenities": [
                          {
                            "title": "Breakfast",
                            "group": null
                          },
                          {
                            "title": "Pet friendly",
                            "group": null
                          }
                        ],
                        "languagesSpoken": [],
                        "rooms": [
                          {
                            "name": "Standard Double Room",
                            "description": "The double room provides air conditioning, soundproof walls, as well as a private bathroom featuring a bath and a hairdryer. The double room offers a mini-bar, a wardrobe, heating and a flat-screen TV. The unit offers 1 bed.",
                            "sizeSquareMeters": 19,
                            "bedConfiguration": "",
                            "occupancy": 2,
                            "photos": [
                              "https://cf.bstatic.com/xdata/images/hotel/max1024x768/330168497.jpg?k=0e12b098065a20392dca6cc6f2ac7d6b1c1cb2cbd3e780e272cb21291c56b55b&o="
                            ]
                          }
                        ],
                        "checkIn": null,
                        "checkOut": null
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/hotel-reviews": {
      "post": {
        "operationId": "hotel-reviews",
        "tags": [
          "Reviews"
        ],
        "summary": "Read a property’s guest reviews",
        "description": "Guest reviews for one property, by booking.com URL or compact id — **the route worth coming here for.** **What the guest liked and what they disliked are separate fields.** `positiveText` and `negativeText` arrive apart, either of them possibly null, in the language the guest wrote them, with `language` beside them — nothing is machine-translated over the guest's own words. So \"how many people complained about the bathroom\" is a count over one field rather than sentiment analysis over a paragraph. Beside them: the guest's `score`, the `stayCheckIn`/`stayCheckOut` dates and `nights` the review refers to, the `roomType` they actually stayed in, and their public first name, country and `travelerType` (\"Couple\", \"Family\", \"Solo traveller\"). **A reviews response tells you how complete it is, without a second call.** `totalReviewCount` is the property's full review count as Booking.com reports it, `reviewCount` is how many came back here, `hasMore` is the boolean, and `notes[]` states the pair in plain words. A real response measured on 17 September 2026: `notes: [\"These are 10 of the 3854 reviews the source reports for this property.\", \"More reviews are available. Continue with nextCursor.\"]`. That difference is the point: ten reviews and *the first ten of 3,854* are different answers, and you can tell which one you are holding from the response in your hand. The response also carries `categoryScores`: The property's score split seven ways — Staff, Facilities, Cleanliness, Comfort, Value for money, Location, Free Wifi — each as `{category, label, average}`, where `category` is the stable key (`hotel_staff`, `hotel_services`, `hotel_clean`, `hotel_comfort`, `hotel_value`, `hotel_location`, `hotel_free_wifi`) and `label` the human one. A property strong on location and weak on facilities does not look like its opposite: the measured example scores 9.44 on Location against 7.82 on Facilities behind a single 8.1. Billing: 1 credit per page fetched, reported as `pagesFetched`. A page is up to 25 reviews by default and up to 500 with `limit`; a walk stops early when `limit` is reached, so `pagesFetched` reports what was actually read rather than what was asked for. Measured on 17 September 2026: `{\"hotel\":\"fr/du-printemps-paris\",\"limit\":10}` returned 200 in 4.28-4.49s across three runs, and `{\"pages\":3,\"limit\":60}` returned 60 reviews with `pagesFetched: 3` in 6.14s.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hotel": {
                    "type": "string",
                    "description": "A property: its booking.com URL, or its compact `\"countryCode/pageName\"` id. The only required field."
                  },
                  "limit": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Maximum reviews returned in total, up to 500. Pass -1 for the maximum.",
                    "example": "25"
                  },
                  "pages": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "How many pages of reviews to collect in one request, up to 20. Each page fetched is 1 credit. Pass -1 for the maximum — combined with `limit: -1` that is how you pull as deep as the source goes.",
                    "example": "1"
                  },
                  "cursor": {
                    "type": "string",
                    "description": "Continue from a previous response's `nextCursor`."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "hotel"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "hotel": "fr/du-printemps-paris",
                    "limit": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"booking\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /hotel-reviews this is where the \"10 of the 3854\" line lives; on /search-hotels it is where a default stay window is declared when you passed no dates; on /hotel-detail it is where the \"this route does not price a stay\" answer lives."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "hotelId": {
                      "type": "string",
                      "description": "The property the reviews belong to, echoed back as its compact id — useful when you passed a URL."
                    },
                    "totalReviewCount": {
                      "type": "number",
                      "description": "**The property's full review count as Booking.com reports it.** Compare it to `reviewCount` and you know whether you are holding all of them, with no second call. 3854 on the property measured."
                    },
                    "reviewCount": {
                      "type": "number",
                      "description": "How many reviews came back in this response."
                    },
                    "hasMore": {
                      "type": "boolean",
                      "description": "Whether there are further reviews past this page."
                    },
                    "nextCursor": {
                      "type": "string",
                      "description": "Pass back as `cursor` to continue. Null when there is nothing more."
                    },
                    "categoryScores": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "The property's score split seven ways — Staff, Facilities, Cleanliness, Comfort, Value for money, Location, Free Wifi — each as `{category, label, average}`, where `category` is the stable key (`hotel_staff`, `hotel_services`, `hotel_clean`, `hotel_comfort`, `hotel_value`, `hotel_location`, `hotel_free_wifi`) and `label` the human one. A property strong on location and weak on facilities does not look like its opposite: the measured example scores 9.44 on Location against 7.82 on Facilities behind a single 8.1."
                    },
                    "reviews": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The review's id."
                          },
                          "score": {
                            "type": "number",
                            "description": "The guest's own score for the stay, on Booking.com's 1-10 scale."
                          },
                          "title": {
                            "type": "string",
                            "description": "The headline the guest gave the review, in their own language. Null where they wrote none — a scored review with no words at all is ordinary here."
                          },
                          "positiveText": {
                            "type": "string",
                            "description": "**What the guest liked, on its own.** Null where they wrote nothing positive. Kept apart from `negativeText` so praise and complaint are countable without parsing a paragraph."
                          },
                          "negativeText": {
                            "type": "string",
                            "description": "**What the guest disliked, on its own.** Null where they wrote nothing negative. A review can carry one, both or neither."
                          },
                          "language": {
                            "type": "string",
                            "description": "The language the text is written in, as a code. A single property's reviews are not one language — `it`, `tr` and `de` all appeared in the ten measured — so this is the field to branch on before you run anything over the text."
                          },
                          "stayCheckIn": {
                            "type": "string",
                            "description": "When the reviewed stay started, YYYY-MM-DD. **The review is anchored to a real stay**, not just a posting date."
                          },
                          "stayCheckOut": {
                            "type": "string",
                            "description": "When it ended, on the same terms."
                          },
                          "nights": {
                            "type": "number",
                            "description": "How many nights the stay ran — so you can separate a one-night opinion from a six-night one."
                          },
                          "roomType": {
                            "type": "string",
                            "description": "The room the guest actually stayed in, e.g. \"Superior Twin Room\". This is what lets a complaint be attributed to a room type rather than to the property."
                          },
                          "reviewedAt": {
                            "type": "string",
                            "description": "An ISO timestamp for when the review was posted, e.g. \"2026-09-17T10:35:58.000Z\"."
                          },
                          "reviewer": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{name, countryCode, countryName, travelerType}` — the guest's public first name, where they are from, and the trip type they declared (\"Couple\", \"Family\", \"Solo traveller\"). Public profile fields only."
                          },
                          "hostResponse": {
                            "type": "string",
                            "description": "The property's reply where it answered, null otherwise."
                          },
                          "helpfulVotes": {
                            "type": "number",
                            "description": "How many readers marked the review helpful. Can be null where the source published no count, and 0 is a real answer distinct from that."
                          }
                        },
                        "additionalProperties": false
                      }
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "booking",
                      "notes": [
                        "These are 10 of the 3854 reviews the source reports for this property.",
                        "More reviews are available. Continue with nextCursor."
                      ],
                      "pagesFetched": 1,
                      "hotelId": "fr/du-printemps-paris",
                      "totalReviewCount": 3854,
                      "reviewCount": 10,
                      "hasMore": true,
                      "nextCursor": "10",
                      "categoryScores": [
                        {
                          "category": "hotel_staff",
                          "label": "Staff",
                          "average": 8.94481086730957
                        },
                        {
                          "category": "hotel_services",
                          "label": "Facilities",
                          "average": 7.824008941650391
                        },
                        {
                          "category": "hotel_clean",
                          "label": "Cleanliness",
                          "average": 8.511276245117188
                        },
                        {
                          "category": "hotel_comfort",
                          "label": "Comfort",
                          "average": 8.569668769836426
                        },
                        {
                          "category": "hotel_value",
                          "label": "Value for money",
                          "average": 8.086325645446777
                        },
                        {
                          "category": "hotel_location",
                          "label": "Location",
                          "average": 9.435894966125488
                        },
                        {
                          "category": "hotel_free_wifi",
                          "label": "Free Wifi",
                          "average": 8.791600227355957
                        }
                      ],
                      "reviews": [
                        {
                          "id": "d2ac5babf71467e7",
                          "score": 9,
                          "title": null,
                          "positiveText": null,
                          "negativeText": null,
                          "language": "it",
                          "stayCheckIn": "2026-09-10",
                          "stayCheckOut": "2026-09-13",
                          "nights": 3,
                          "roomType": "Superior Twin Room",
                          "reviewedAt": "2026-09-17T10:35:58.000Z",
                          "reviewer": {
                            "name": "Roberto",
                            "countryCode": "it",
                            "countryName": "Italy",
                            "travelerType": "Couple"
                          },
                          "hostResponse": null,
                          "helpfulVotes": null
                        },
                        {
                          "id": "57e7cc183b361958",
                          "score": 10,
                          "title": "Wir kommen wieder! Und das sagt alles.",
                          "positiveText": "Im Vergleich mit allen anderen Hotels, die wir in Paris bewohnt haben, ist das Printemps der wahre Himmel auf Erden: Große Zimmer ohne Schnick-Schnack, großes schönes Bad mit Badewanne, alles renoviert und fein säuberlich gepflegt. Reizendes Personal!",
                          "negativeText": "Da fällt mir überhaupt nichts ein!",
                          "language": "de",
                          "stayCheckIn": "2026-09-09",
                          "stayCheckOut": "2026-09-15",
                          "nights": 6,
                          "roomType": "Superior Twin Room",
                          "reviewedAt": "2026-09-16T21:33:05.000Z",
                          "reviewer": {
                            "name": "Penny",
                            "countryCode": "ch",
                            "countryName": "Switzerland",
                            "travelerType": "Family"
                          },
                          "hostResponse": null,
                          "helpfulVotes": 0
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/resolve-url": {
      "post": {
        "operationId": "resolve-url",
        "tags": [
          "Utilities"
        ],
        "summary": "Read a Booking.com link",
        "description": "Read any Booking.com link and say what it names — a property or a search — along with the compact id it encodes and its canonical URL, ready to hand to the other routes. The right first step for a pile of pasted links: it sorts them before a single page fetch is spent, and a link it does not recognise comes back as `kind: \"unknown\"` with a note rather than as an error you have to catch. **Free.** It makes no request to Booking.com at all, answered in 0.065-0.13s across the calls measured on 17 September 2026, and reports `pagesFetched: 0`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Any Booking.com link — a property page or a search."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "url"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "url": "https://www.booking.com/hotel/fr/du-printemps-paris.html"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The source. \"booking\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. On /hotel-reviews this is where the \"10 of the 3854\" line lives; on /search-hotels it is where a default stay window is declared when you passed no dates; on /hotel-detail it is where the \"this route does not price a stay\" answer lives."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "Pages read, and what the request is billed at: 1 credit per page. A page costs the same whether it comes back full or half empty. /resolve-url reports 0 and is free."
                    },
                    "kind": {
                      "type": "string",
                      "description": "\"hotel\" when the link names a property, \"search\" for a results link, or \"unknown\" when it names neither — the last with a note saying the link does not name a property this service recognizes, rather than an error."
                    },
                    "canonicalUrl": {
                      "type": "string",
                      "description": "The property's canonical booking.com address, with tracking and date parameters stripped. Null on a search or an unknown link."
                    },
                    "hotelId": {
                      "type": "string",
                      "description": "The compact `\"countryCode/pageName\"` id the link encodes, ready to pass to /hotel-detail or /hotel-reviews as `hotel`. Null on a search or an unknown link."
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "booking",
                      "notes": [],
                      "pagesFetched": 0,
                      "kind": "hotel",
                      "canonicalUrl": "https://www.booking.com/hotel/fr/du-printemps-paris.html",
                      "hotelId": "fr/du-printemps-paris"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    }
  }
}