Booking.com API & Scraper
The Booking.com API returns hotel search, prices and availability as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary hotels/search endpoint returns hotels with id, name, description, location, URL, price and pre-discount price, and you can pull a hotels/detail, live availability, guest reviews and destination autocomplete. It is built for travel apps, price monitors and hospitality analytics that need Booking.com data without scraping a defended site. One ReefAPI key, one shared credit pool, the standard envelope.
Booking.com ids, price semantics and the two rating scales
Two things bite people here: the price is the total for the stay rather than a nightly rate, and a property carries two independent scores. Booking also has a numeric hotel_id and a URL slug that do not always agree with each other. Everything below was measured on 2026-08-27 with a Paris search for 2026-09-16 to 2026-09-18, two adults, one room, currency USD.
| Field | Measured example | What it means |
|---|---|---|
| hotel_id | 7833434 | The numeric property id. Stable, and the field to join on. |
| page_name | celine-taxim | The URL slug. hotels/detail and hotels/reviews take it together with country_code. |
| room_blocks[].room_id | 783343401 | The hotel_id with a two-digit room index appended. 237896 produced 23789607, 7833434 produced 783343401. |
| price.text | "Total" | price.amount_unformatted (374.743686913343) is the total for the whole stay, not a nightly rate. |
| price_before_discount.text | "US$176.08 x 2 nights" | This is where the per-night figure appears, when a discount exists. |
| room_blocks[].original_price | 321.7927 EUR | The property's own currency. final_price (374.74 USD) is the same room converted into the currency you asked for. |
| excluded_charges aggregate | 52.84 USD per stay | Taxes and fees not inside price. Each line carries chargeInclusion INCLUDED or EXCLUDED and a chargeMode such as PER_PERSON_PER_NIGHT or PERCENTAGE. |
| review_score.score | 8.9 | Guest score out of 10, one decimal, with review_count 573 and label "Excellent". |
| star_rating | 4.0 | Official stars, 1 to 5. A different number from the review score, and null on many apartments. |
| dest_type | city, district, landmark, airport, region, hotel | All six were returned live by destinations/autocomplete. |
| dest_id for a city | -1456928 (Paris), -755070 (Istanbul) | Frequently negative. Send the minus sign, it is part of the id. |
| coordinates.latitude / longitude | null | Null in both search rows and hotels/detail. Coordinates do come back on destinations/autocomplete suggestions. |
page_name is a legacy slug, not a name. Measured: page_name le-grand-gite-de-la-doye resolves to hotel_id 6929456, "Residence du Louvre", 1 Rue de Rohan, Paris. Booking keeps the original slug when a property is relisted or renamed, so treat page_name as the address of the page and hotel_id as the identity of the property.
Real request and response JSON
Captured from the indexed primary action, hotels/search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/booking/v1/hotels/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"dest_id": "-755070",
"dest_type": "city",
"checkin": "2026-08-01",
"checkout": "2026-08-03"
}
}{
"ok": true,
"meta": {
"api": "booking",
"endpoint": "hotels/search",
"mode": "live",
"latency_ms": 1832,
"record_count": 26,
"bytes": 402224,
"cache_hit": false,
"completeness_pct": 100,
"destination": {
"dest_id": "-755070",
"dest_type": "city",
"value": "Istanbul, Marmara Region, Turkey"
},
"completeness_basis": {
"items": 26,
"field_slots": 183,
"field_slots_filled": 183,
"pagination_total_present": true
}
},
"data": {
"items": [
{
"hotel_id": 177252,
"page_name": "sadaret-hotel-old-city-sultanahmet",
"name": "[redacted-name]",
"description": null,
"url": "https://www.booking.com/hotel/tr/sadaret-hotel-old-city-sultanahmet.html",
"location": {
"address": "Kucuk Ayasofya Cad. No:31 Sultanahmet",
"city": "Istanbul",
"country_code": "tr",
"latitude": null,
"longitude": null,
"display": "Fatih, Istanbul",
"distance": "0.3 miles from downtown",
"geo_distance_meters": null
},
"price": {
"text": "Total",
"amount": "€ 92.53",
"amount_rounded": "€ 93",
"amount_unformatted": 92.5266000000001,
"currency": "EUR"
},
"price_before_discount": {
"text": "Original price",
"amount": "€ 304.77",
"amount_rounded": "€ 305",
"amount_unformatted": 304.774774774775,
"currency": "EUR"
},
"charges_info": null,
"excluded_charges": {
"excludeChargesList": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"__typename": "ExcludeChargesIrene",
"excludeChargesAggregated": {
"__typename": "[trimmed-depth]",
"amountPerStay": "[trimmed-depth]",
"copy": "[trimmed-depth]"
}
},
"discounts": [
{
"__typename": "[trimmed-depth]",
"description": "[trimmed-depth]",
"itemType": "[trimmed-depth]",
"name": "[trimmed-depth]",
"productId": "[trimmed-depth]",
"amount": "[trimmed-depth]"
},
{
"description": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"itemType": "[trimmed-depth]",
"name": "[trimmed-depth]",
"amount": "[trimmed-depth]",
"productId": "[trimmed-depth]"
}
],
"meal_plan": null,
"breakfast_included": false,
"review_score": {
"score": 9,
"review_count": 1941,
"label": "Wonderful",
"secondary_score": 9.7
},
"star_rating": 4,
"accommodation_type_id": 219,
"photo": "/xdata/images/hotel/square600/[redacted-phone].jpg?k=c8e07d3b76bca585f2396419b689b4a23b5ed5f54cb056cffb8c7f040aa99762&o=",
"room_configurations": {
"unitConfigurations": [
"[trimmed-depth]"
],
"commonConfiguration": {
"nbBathrooms": "[trimmed-depth]",
"nbUnits": "[trimmed-depth]",
"nbBedrooms": "[trimmed-depth]",
"name": "[trimmed-depth]",
"unitId": "[trimmed-depth]",
"nbLivingrooms": "[trimmed-depth]",
"bedConfigurations": "[trimmed-depth]",
"unitTypeNames": "[trimmed-depth]",
"localizedArea": "[trimmed-depth]",
"nbAllBeds": "[trimmed-depth]",
"nbKitchens": "[trimmed-depth]",
"__typename": "[trimmed-depth]"
},
"__typename": "MatchingUnitConfigurations"
},
"room_blocks": [
{
"room_id": "[trimmed-depth]",
"occupancy": "[trimmed-depth]",
"policy_group_id": "[trimmed-depth]",
"package_id": "[trimmed-depth]",
"meal_plan_id": "[trimmed-depth]",
"final_price": "[trimmed-depth]",
"original_price": "[trimmed-depth]",
"free_cancellation_until": "[trimmed-depth]",
"cancellation_type": "[trimmed-depth]",
"only_left_message": "[trimmed-depth]",
"available_count_hint": "[trimmed-depth]",
"meal_plan": "[trimmed-depth]",
"breakfast_included": "[trimmed-depth]",
"taxes_and_charges": "[trimmed-depth]"
}
],
"policies": {
"enableJapaneseUsersSpecialCase": null,
"showNoPrepayment": false,
"showFreeCancellation": false,
"__typename": "Policies"
},
"badges": [
{
"style": "[trimmed-depth]",
"identifier": "[trimmed-depth]",
"tooltip": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"name": "[trimmed-depth]"
}
]
},
{
"hotel_id": 9179864,
"page_name": "the-peninsula-istanbul",
"name": "[redacted-name]",
"description": null,
"url": "https://www.booking.com/hotel/tr/the-peninsula-istanbul.html",
"location": {
"address": "34 Kemankeş Caddesi Kemankeş Kocamustafapaşa Mahallesi, Karaköy",
"city": "Beyoğlu",
"country_code": "tr",
"latitude": null,
"longitude": null,
"display": "Beyoglu, Istanbul",
"distance": "1 miles from downtown",
"geo_distance_meters": null
},
"price": {
"text": "Total",
"amount": "€ 1,345.77",
"amount_rounded": "€ 1,346",
"amount_unformatted": 1345.772,
"currency": "EUR"
},
"price_before_discount": {
"text": null,
"amount": "",
"amount_rounded": "",
"amount_unformatted": 0,
"currency": ""
},
"charges_info": null,
"excluded_charges": {
"excludeChargesList": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"__typename": "ExcludeChargesIrene",
"excludeChargesAggregated": {
"__typename": "[trimmed-depth]",
"amountPerStay": "[trimmed-depth]",
"copy": "[trimmed-depth]"
}
},
"discounts": [],
"meal_plan": null,
"breakfast_included": false,
"review_score": {
"score": 9.5,
"review_count": 350,
"label": "Exceptional",
"secondary_score": 9.7
},
"star_rating": 5,
"accommodation_type_id": 204,
"photo": "/xdata/images/hotel/square600/[redacted-phone].jpg?k=36fe89261a941dcc265a2c56de61c8a475cd217099e7c71aae5cc4aef22bba9e&o=",
"room_configurations": {
"__typename": "MatchingUnitConfigurations",
"commonConfiguration": {
"nbKitchens": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"localizedArea": "[trimmed-depth]",
"unitTypeNames": "[trimmed-depth]",
"nbAllBeds": "[trimmed-depth]",
"nbLivingrooms": "[trimmed-depth]",
"unitId": "[trimmed-depth]",
"bedConfigurations": "[trimmed-depth]",
"nbUnits": "[trimmed-depth]",
"nbBathrooms": "[trimmed-depth]",
"nbBedrooms": "[trimmed-depth]",
"name": "[trimmed-depth]"
},
"unitConfigurations": [
"[trimmed-depth]"
]
},
"room_blocks": [
{
"room_id": "[trimmed-depth]",
"occupancy": "[trimmed-depth]",
"policy_group_id": "[trimmed-depth]",
"package_id": "[trimmed-depth]",
"meal_plan_id": "[trimmed-depth]",
"final_price": "[trimmed-depth]",
"original_price": "[trimmed-depth]",
"free_cancellation_until": "[trimmed-depth]",
"cancellation_type": "[trimmed-depth]",
"only_left_message": "[trimmed-depth]",
"available_count_hint": "[trimmed-depth]",
"meal_plan": "[trimmed-depth]",
"breakfast_included": "[trimmed-depth]",
"taxes_and_charges": "[trimmed-depth]"
}
],
"policies": {
"showNoPrepayment": false,
"showFreeCancellation": false,
"enableJapaneseUsersSpecialCase": null,
"__typename": "Policies"
},
"badges": {
"showSkiToDoor": false,
"showBhTravelCreditBadge": false,
"__typename": "CustomBadges",
"showOnlineCheckinBadge": null
}
},
{
"hotel_id": 12433,
"page_name": "alzer-istanbul1",
"name": "[redacted-name]",
"description": null,
"url": "https://www.booking.com/hotel/tr/alzer-istanbul1.html",
"location": {
"address": "BİNBİRDİREK MAH AT MEYDANI CD DiskapiNo:20",
"city": "Istanbul",
"country_code": "tr",
"latitude": null,
"longitude": null,
"display": "Fatih, Istanbul",
"distance": "0.2 miles from downtown",
"geo_distance_meters": null
},
"price": {
"text": "Total",
"amount": "€ 103.88",
"amount_rounded": "€ 104",
"amount_unformatted": 103.8799,
"currency": "EUR"
},
"price_before_discount": {
"text": "Original price",
"amount": "€ 342.16",
"amount_rounded": "€ 342",
"amount_unformatted": 342.162162162162,
"currency": "EUR"
},
"charges_info": null,
"excluded_charges": {
"excludeChargesAggregated": {
"__typename": "[trimmed-depth]",
"amountPerStay": "[trimmed-depth]",
"copy": "[trimmed-depth]"
},
"excludeChargesList": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"__typename": "ExcludeChargesIrene"
},
"discounts": [
{
"name": "[trimmed-depth]",
"productId": "[trimmed-depth]",
"amount": "[trimmed-depth]",
"itemType": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"description": "[trimmed-depth]"
},
{
"itemType": "[trimmed-depth]",
"description": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"name": "[trimmed-depth]",
"productId": "[trimmed-depth]",
"amount": "[trimmed-depth]"
}
],
"meal_plan": null,
"breakfast_included": false,
"review_score": {
"score": 8.9,
"review_count": 737,
"label": "Excellent",
"secondary_score": 9.9
},
"star_rating": 4,
"accommodation_type_id": 204,
"photo": "/xdata/images/hotel/square600/[redacted-phone].jpg?k=a9d0319b390df53fd0d23c777c1257088be21874d62be77ba25235e86ce22f74&o=",
"room_configurations": {
"__typename": "MatchingUnitConfigurations",
"unitConfigurations": [
"[trimmed-depth]"
],
"commonConfiguration": {
"nbAllBeds": "[trimmed-depth]",
"localizedArea": "[trimmed-depth]",
"unitTypeNames": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"nbKitchens": "[trimmed-depth]",
"nbBedrooms": "[trimmed-depth]",
"name": "[trimmed-depth]",
"nbUnits": "[trimmed-depth]",
"nbBathrooms": "[trimmed-depth]",
"bedConfigurations": "[trimmed-depth]",
"nbLivingrooms": "[trimmed-depth]",
"unitId": "[trimmed-depth]"
}
},
"room_blocks": [
{
"room_id": "[trimmed-depth]",
"occupancy": "[trimmed-depth]",
"policy_group_id": "[trimmed-depth]",
"package_id": "[trimmed-depth]",
"meal_plan_id": "[trimmed-depth]",
"final_price": "[trimmed-depth]",
"original_price": "[trimmed-depth]",
"free_cancellation_until": "[trimmed-depth]",
"cancellation_type": "[trimmed-depth]",
"only_left_message": "[trimmed-depth]",
"available_count_hint": "[trimmed-depth]",
"meal_plan": "[trimmed-depth]",
"breakfast_included": "[trimmed-depth]",
"taxes_and_charges": "[trimmed-depth]"
}
],
"policies": {
"__typename": "Policies",
"enableJapaneseUsersSpecialCase": null,
"showFreeCancellation": false,
"showNoPrepayment": false
},
"badges": [
{
"style": "[trimmed-depth]",
"identifier": "[trimmed-depth]",
"tooltip": "[trimmed-depth]",
"__typename": "[trimmed-depth]",
"name": "[trimmed-depth]"
}
]
}
],
"count": 26,
"pagination": {
"per_page": 25,
"total": 3546,
"offset": 0,
"page": 1,
"has_more": true,
"next_page": 2
},
"applied": {
"sort_by": "top_picks",
"filters": []
}
}
}What the Booking.com API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| hotels/search | Search hotels in a destination for given dates (25 hotels per page, with prices, review scores, photos, room configurations). Supports sorting, price/star/rating/amenity filters and paging. Pass ?include_facets=1 to list every available filter and sorter for the destination. | Travel apps call hotels/search to search hotels in a destination for given dates (25 hotels per page, with prices, review score…. | query, dest_id, dest_type, checkin, checkout, ... |
| hotels/detail | Full hotel record: summary, rating breakdown, facilities, room types, surroundings, FAQ. | Pricing monitors call hotels/detail to get full hotel record. | page_name, country_code, hotel_url, checkin, checkout, ... |
| hotels/availability | Visible room blocks (room, occupancy, price, cancellation policy, meal plan) for a destination's top hotel on given dates. | Itinerary products call hotels/availability to get visible room blocks (room, occupancy, price, cancellation policy, meal plan) for a destinatio…. | query, dest_id, dest_type, checkin, checkout, ... |
| hotels/reviews | Paginated, deduped guest reviews for one hotel (score, title, pros/cons text, owner reply, room type, stay dates, reviewer country, photos). Fetches ALL reviews by default; supports sorting and keyword search. The response also lists the available review filters (by score / language / customer type / season / topic). | Hospitality analysts call hotels/reviews to get paginated, deduped guest reviews for one hotel (score, title, pros/cons text, owner reply, ro…. | page_name, country_code, hotel_id, hotel_url, max_reviews, ... |
| destinations/autocomplete | Resolve a free-text place ('Paris', 'Maldives', 'JFK') into the destination IDs the other actions take (dest_id + dest_type), with country, coordinates and how many hotels/homes each match has. Use this first when you only have a place name. | Travel apps call destinations/autocomplete to resolve a free-text place ('Paris', 'Maldives', 'JFK') into the destination IDs the other act…. | query, limit |
Call hotels/search from your stack
curl -X POST https://api.reefapi.com/booking/v1/hotels/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"dest_id":"-755070","dest_type":"city","checkin":"2026-08-01","checkout":"2026-08-03"}'import requests
r = requests.post(
"https://api.reefapi.com/booking/v1/hotels/search",
headers={"x-api-key": REEF_KEY},
json={
"dest_id": "-755070",
"dest_type": "city",
"checkin": "2026-08-01",
"checkout": "2026-08-03"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/booking/v1/hotels/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"dest_id": "-755070",
"dest_type": "city",
"checkin": "2026-08-01",
"checkout": "2026-08-03"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.booking.hotels/search with {"dest_id":"-755070","dest_type":"city","checkin":"2026-08-01","checkout":"2026-08-03"}.Who uses this API and why
- Travel apps call hotels/search to show live hotel prices and availability for a destination.
- Rate-parity and pricing tools use hotels/availability to monitor a property's prices across dates.
- Hospitality analytics use hotels/reviews to track guest ratings and feedback.
Questions developers ask before integrating
Is the Booking.com price per night or for the whole stay?
For the whole stay. On a two-night Paris search the price block came back with text "Total", amount "$374.74" and amount_unformatted 374.743686913343. The nightly figure only appears as free text inside price_before_discount.text, for example "US$176.08 x 2 nights". If you need a nightly rate, divide by the number of nights between your checkin and checkout dates yourself. Dates are plain YYYY-MM-DD.
Are taxes and fees included in that price?
Partly, and the response tells you which. excluded_charges.excludeChargesList holds one entry per charge, each with chargeInclusion set to INCLUDED or EXCLUDED and a chargeMode such as PER_PERSON_PER_NIGHT or PERCENTAGE. On the measured $374.74 stay, one charge of $40.73 was INCLUDED and one of $12.11 was EXCLUDED, and the aggregated amountPerStay reported $52.84 as the total not covered by the headline price. Add that aggregate if you want the walk-up cost.
What scale is the Booking.com review score, and how does it differ from the star rating?
review_score.score is a guest score out of 10 with one decimal, delivered with review_count and a label. Measured: 8.9 over 573 reviews labelled "Excellent", and 7.9 over 2,055 labelled "Good". star_rating is the official star classification from 1 to 5, returned as a float such as 4.0, and it is a separate number that is often null for apartments and guest houses. Individual reviews from hotels/reviews are also scored out of 10, one score per guest.
Why does page_name not match the hotel's name?
Because it is a legacy URL slug rather than a name. Measured on 2026-08-27: page_name le-grand-gite-de-la-doye returns hotel_id 6929456, name "Residence du Louvre", address 1 Rue de Rohan, Paris. Booking keeps the original slug when a property is relisted or renamed, so a slug can describe something that no longer exists there. Join your records on hotel_id and use page_name only to address the page.
What are dest_id and dest_type, and why is dest_id negative?
They come from destinations/autocomplete and together they name a place to search. dest_type values seen live are city, district, landmark, airport, region and hotel. City ids are frequently negative (Paris -1456928, Istanbul -755070, Tokyo -246227, Rome -126693) while districts, landmarks and airports are small positive numbers (Taksim 1053, Eiffel Tower 735, Heathrow 4). The minus sign is part of the id, pass it through unchanged. Some newer entries break the pattern, New York came back as 20088325, so read the sign off the response rather than assuming it.
Why are latitude and longitude null on hotels?
The search and detail surfaces do not publish per-property coordinates, so location.latitude, location.longitude and coordinates.latitude/longitude all come back null. What you do get is location.address, location.city, location.country_code, a display string such as "18th arr., Paris" and a distance string such as "2.2 miles from downtown". destinations/autocomplete does return real coordinates for the destination itself, for example 48.85668 and 2.3514764 for Paris.
What is room_id and how does it relate to hotel_id?
room_id is the hotel_id with a room index appended, confirmed on two properties: hotel 237896 has rooms 23789607 and 23789612, hotel 7833434 has 783343401 and 783343402. That means you can read the parent property straight off a room id. Reviews carry the same value in their stay data, so a review can be tied to the exact room type the guest booked.
Why did hotels/detail come back mostly null for one hotel and complete for another?
Because detail is assembled from whatever the property page served, and meta says which case you got. For celine-taxim, meta reported completeness_pct 100.0 and room_detail_source "captured", and url, address, star_rating and review_score were all populated. For hotel_id 237896 the same call reported completeness_pct 87.5 and room_detail_source "constructed_replay", and url, address, star_rating and review_score came back null while room_types and facilities were fine. Check meta.completeness_pct before trusting a sparse record. The two actions can also disagree on the display name after a rebrand, so key on hotel_id.
What is the Booking.com API?
Booking.com API is a ReefAPI endpoint group for search hotels with prices, scores and availability. It returns live JSON through POST requests under /booking/v1.
Is the Booking.com API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Booking.com calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Booking.com login or account?
No login to Booking.com is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Booking.com data?
The page example is captured from a live hotels/search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Booking.com API use?
Booking.com actions currently cost 1-3 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Booking.com from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call booking actions with the same key, credit pool and JSON envelope used by normal REST requests.