Booking.com Scraper – Hotels, Prices & Reviews
Pricing
from $1.50 / 1,000 hotel returneds
Booking.com Scraper – Hotels, Prices & Reviews
Scrape Booking.com hotels for any destination and dates: name, total price, rating, review count, stars, address, coordinates, photo and link, plus each hotel's reviews with score, text, guest nationality and traveller type. Every property Booking lists, no 1,000 cap. No key, no browser.
Pricing
from $1.50 / 1,000 hotel returneds
Rating
0.0
(0)
Developer
Simple Actor
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Scrape Booking.com as structured data. Give it a destination and your dates and every property Booking lists comes back as a row: name, total price for your stay, guest score and review count, star rating, address, coordinates, property type, photo and a link — in Booking's own order. Turn reviews on and each property's reviews arrive as rows of their own: score, title, what the guest liked and disliked, their nationality and traveller type, stay dates, room, and the hotel's reply.
Built for price monitoring, market research, revenue management and hospitality lead generation — and it reaches every property Booking lists, with no thousand-result ceiling.
No API key, no login, no browser, no account of yours involved. A hundred properties arrive per request, so a typical search finishes in seconds.
$1.50 per 1,000 hotels, $1.00 per 1,000 reviews — $0.15 for a typical 100-hotel search. Apify platform usage included, nothing else to pay. A search that cannot be read is never charged.
Unofficial. Not affiliated with, endorsed by, or sponsored by Booking.com.
Features
- The price for your exact stay — total for the dates, guests and rooms
you searched, in the currency you chose, with a per-night average and a
priceBasison every row that says exactly what the number covers. No ambiguity about nightly versus total, or whose currency. - Everything Booking lists — a hundred properties per request and no cap: a city's full 1,500–7,000 properties are reachable. Booking's own count of results is on every row, so you know how many there are.
- A catalogue without dates — leave the dates empty and every property in the destination is listed without prices, cheaply.
- Booking's own filters and sorts — star ratings, minimum review score, free cancellation, and any filter string copied from a Booking URL; sorted by Booking's recommendation, price, reviews or stars.
- Reviews worth having — score, title, liked and disliked text, guest
nationality (name and code), traveller type, stay dates, room type, the
hotel's reply and helpful votes. Joined to their hotel by
hotelId. - Stable IDs — Booking's numeric
hotelIdon every row, for deduplicating across runs and joining reviews to hotels. - Paste a Booking link — a search copied from Booking keeps its destination, dates, guests, currency and filters.
- Honest rows for every outcome. A destination Booking does not know is
a
not_foundrow, a bad line abad_inputrow, a read that fails aread_failedrow — never a silent empty result. The run stays green.
Input
{"queries": ["Lisbon", "Algarve"], // one destination per line, or Booking search links"checkIn": "2026-10-10", // leave both dates empty to list without prices"checkOut": "2026-10-12","adults": 2, "children": 0, "rooms": 1,"currency": "USD","maxHotels": 100, // per destination, up to 5,000"sortBy": "recommended", // recommended | price | review_score_and_price | class"stars": [4, 5], // optional"minReviewScore": 8, // optional: 6, 7, 8 or 9"freeCancellation": false,"filters": "ht_id=204", // optional: any Booking nflt= string"includeReviews": false,"maxReviewsPerHotel": 25,"reviewSort": "most_relevant" // most_relevant | newest_first}
| Field | Type | Default | What it does |
|---|---|---|---|
queries | array | — | Destinations as you would type them into Booking — a city, region, district, landmark or country — or search-results links copied from Booking. |
checkIn, checkOut | string | — | YYYY-MM-DD. Both, or neither. With dates: prices and availability. Without: every property, no prices. |
adults, children, rooms | integer | 2, 0, 1 | The booking the price is for. childrenAges (array) makes family pricing exact. |
currency | string | USD | Three-letter code. Booking converts at its own rate. |
maxHotels | integer | 100 | Properties per destination, up to 5,000. A hundred per request. |
sortBy | string | recommended | Booking's sort menu. |
stars, minReviewScore, freeCancellation | — | — | Booking's own filters. |
filters | string | — | Any other Booking filter, as it appears after nflt= in a filtered URL (ht_id=204;mealplan=1). |
includeReviews | boolean | false | Collect each property's reviews as separate rows. Charged per review. |
maxReviewsPerHotel | integer | 25 | Reviews per property, up to 2,000. Twenty-five per request. |
reviewSort | string | most_relevant | Booking's most relevant, or newest first. |
What the price is
price is the total for the whole stay — every night, every guest,
every room you asked for — as Booking quotes it for those exact dates, in
the currency you chose. pricePerNight is that total divided by the nights,
and originalPrice is the struck-through price before a discount, when
Booking shows one.
priceBasis spells it out on every row ("total for 2 nights, 2 adults, 1
room, as Booking quotes it") so a spreadsheet never has to guess. Without
dates there is no price, and the row says so with nulls rather than a
guessed number.
How many properties
A hundred arrive per request and there is no ceiling: maxHotels decides
how far to read, and Booking's own count is in totalResults on every row
— so a first run at the default 100 tells you a city has 1,819 properties,
and a second run at 2,000 collects them all.
Output
One item per property, in the run's dataset — a real row, from a search for
a hotel by name ("The Plaza Hotel New York", 16–18 October 2026):
{"type": "hotel","position": 1,"name": "The Plaza, A Fairmont Hotel","hotelId": 58958,"url": "https://www.booking.com/hotel/us/the-plaza.html?checkin=2026-10-16&checkout=2026-10-18&group_adults=2&no_rooms=1&group_children=0&selected_currency=USD","address": "768 5th Avenue","city": "New York (New York)","countryCode": "us","latitude": 40.7644335377821,"longitude": -73.9742463827133,"stars": 5,"reviewScore": 8.6,"reviewCount": 509,"accommodationTypeId": 204,"propertyType": "Hotel","price": 3402.5,"originalPrice": null,"currency": "USD","pricePerNight": 1701.25,"priceBasis": "total for 2 nights, 2 adults, 1 room, as Booking quotes it","checkIn": "2026-10-16","checkOut": "2026-10-18","nights": 2,"adults": 2,"children": 0,"rooms": 1,"imageUrl": "https://cf.bstatic.com/xdata/images/hotel/square600/914954799.webp?…","destination": "The Plaza, A Fairmont Hotel, New York (The City of New York), New York, United States","destinationId": "hotel:58958","totalResults": 456,"query": "The Plaza Hotel New York","page": 1,"retrievedAt": "2026-09-12T04:59:49.890Z"}
Every hotel field
| Field | Type | What it is |
|---|---|---|
type | string | "hotel" on a property row. |
position | integer | The property's rank in Booking's order for this search, 1 first, counted across pages. |
name | string | The property's name as Booking shows it. |
hotelId | integer | Booking's numeric ID for the property — stable across runs. Join review rows to hotels on it; deduplicate across runs with it. |
url | string | The property's page on Booking.com, with the dates, guests, rooms and currency of your search already in the link. |
address | string / null | Street address as Booking states it. |
city | string / null | City. |
countryCode | string / null | Two-letter country code, lower case as Booking uses it (pt, lk, us). |
latitude, longitude | number | Coordinates of the property. |
stars | number / null | Star rating 1–5 as Booking states it. Null for properties Booking does not class (many guest houses and homestays). |
reviewScore | number / null | Booking's guest review score out of 10. Null for a property with no reviews yet — never 0. |
reviewCount | integer / null | How many guest reviews the property has. |
accommodationTypeId | integer / null | Booking's numeric property-type code. |
propertyType | string / null | The type in words for the codes Booking documents: 204 Hotel, 201 Apartment, 203 Hostel, 205 Motel, 206 Resort, 208 Bed and breakfast, 213 Villa, 216 Guest house, 219 Aparthotel, 220 Holiday home, 222 Homestay, 225 Capsule hotel. Null for other codes. |
price | number / null | The total for the whole stay — every night, guest and room you searched — as Booking quotes it, in currency. Null when no dates were given, or when Booking quoted no rate for those dates (sold out, minimum stay). |
originalPrice | number / null | The stay's price before Booking's discount, when the property shows one struck through. Null when there is no discount. |
currency | string / null | Currency of price — the one you asked for. |
pricePerNight | number / null | price divided by nights, an average. |
priceBasis | string / null | What the price covers, in words — e.g. "total for 2 nights, 2 adults, 1 room, as Booking quotes it" — so a spreadsheet never has to guess. |
checkIn, checkOut | string / null | The dates the price is for, YYYY-MM-DD. Null when no dates were searched. |
nights | integer / null | Nights in the stay. |
adults, children, rooms | integer / null | The booking the price is for. |
imageUrl | string / null | The property's main photo on Booking's image host. |
destination | string | The destination as Booking resolved your search — e.g. "Lisbon, Lisbon Region, Portugal", or the property itself when you searched a hotel name. |
destinationId | string | Booking's ID for that destination with its type: "city:-2167973", "region:…", "hotel:58958". |
totalResults | integer | Booking's own count of properties for this search — how many there are to collect. |
query | string | The line of input this row answered. |
page | integer | Which request of the search the row arrived on, 1 first. A hundred properties per request. |
retrievedAt | string | When this run read the search, ISO 8601. |
And, with includeReviews on, one item per review — a real one, of the same property, with the hotel's reply:
{"type": "review","position": 5,"hotelId": 58958,"hotelName": "The Plaza, A Fairmont Hotel","url": "https://www.booking.com/hotel/us/the-plaza.html?checkin=2026-10-16&checkout=2026-10-18&group_adults=2&no_rooms=1&group_children=0&selected_currency=USD","reviewScore": 10,"title": "Most amazing stay","positiveText": "The location, the service, the luxury","negativeText": "Nothing","language": "xu","reviewedAt": "2026-09-03T10:14:12.000Z","guestName": "Haya","guestCountryCode": "kw","guestCountry": "Kuwait","travellerType": "Solo traveler","customerType": "SOLO_TRAVELLERS","stayCheckIn": "2026-08-04","stayCheckOut": "2026-08-07","nights": 3,"roomType": "Grand Luxe Family Queen Room with Sofa Bed","hotelReply": "Dear Guest, \n\nWe are delighted to hear that you had an exceptional experience. Thank you for your kind words and we look forward to having you with us again soon. \n\nWarm regards, \n\nSam Ioannidis\nManaging Director","helpfulVotes": 0,"query": "The Plaza Hotel New York","retrievedAt": "2026-09-12T04:59:49.890Z"}
Every review field
| Field | Type | What it is |
|---|---|---|
type | string | "review" on a review row. |
position | integer | The review's position in the hotel's list, in the order you chose. |
hotelId | integer | The hotel the review is of — joins to the hotel row's hotelId. |
hotelName | string | The hotel's name, repeated for convenience. |
url | string | The hotel's Booking page. |
reviewScore | number | The guest's score out of 10. |
title | string / null | The review's title, when the guest gave one. |
positiveText | string / null | What the guest liked. |
negativeText | string / null | What the guest disliked. |
language | string / null | The language the review is written in (en, de, fr…). Booking's code xu means the text was machine-translated. |
reviewedAt | string | When the review was posted, ISO 8601. |
guestName | string / null | The guest's first name as shown. |
guestCountryCode | string / null | The guest's country, two-letter code — nationality, for segmenting. |
guestCountry | string / null | The guest's country in words. |
travellerType | string / null | Who travelled, as Booking labels it: Family, Couple, Solo traveler, Group… |
customerType | string / null | Booking's code for the same: FAMILIES, COUPLES, SOLO_TRAVELLERS, GROUP_OF_FRIENDS… |
stayCheckIn, stayCheckOut | string / null | The guest's own stay dates. |
nights | integer / null | How many nights the guest stayed. |
roomType | string / null | The room the guest stayed in. |
hotelReply | string / null | The property's public reply to the review, when there is one. |
helpfulVotes | integer / null | How many people marked the review helpful. |
query | string | The line of input the hotel came from. |
retrievedAt | string | When this run read the reviews, ISO 8601. |
Error rows
A search that could not be read is its own row rather than a failed run:
| Field | What it is |
|---|---|
type | "error" |
query | The line of input that could not be searched. |
error | bad_input (the line cannot be searched — a hotel-page link, a bad date), not_found (Booking knows no such destination) or read_failed (Booking's answer could not be read after retries). |
errorDescription | The reason in words. |
retrievedAt | When it was tried. |
Error rows are never charged. The run itself stays green either way, and its
status message says how many searches could not be read — so if you run on a
schedule, check for type: "error" rows rather than trusting the run
status.
An empty dataset means every search was read and Booking lists nothing for
it. A destination Booking does not know comes back as a not_found row,
never as silence.
How to use
From Apify Console
- Open the Actor and click Try for free / Start.
- Put one destination per line into Destinations, or paste a Booking search link.
- Set Check-in and Check-out for prices, or leave them empty to list every property without prices.
- Set Hotels per destination — 100 is one request;
totalResultson the rows tells you how many there are. - Turn on Also collect each hotel's reviews if you want them, and cap Reviews per hotel.
- Click Start, then open the Dataset tab — the Hotels view shows a photo of each, the Reviews view the reviews — and export as JSON, CSV or Excel.
From the API
curl -s "https://api.apify.com/v2/acts/simple.actor~booking-hotels/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"queries": ["Lisbon"], "checkIn": "2026-10-10", "checkOut": "2026-10-12", "maxHotels": 200}'
Price monitoring with the JavaScript client:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('simple.actor/booking-hotels').call({queries: ['Lisbon'],checkIn: '2026-10-10',checkOut: '2026-10-12',currency: 'EUR',stars: [4, 5],maxHotels: 500,});const { items } = await client.dataset(run.defaultDatasetId).listItems();// Track price by hotelId over runs; join reviews to hotels on the same key.
Use cases
- Price monitoring — the same destination and dates every day, keyed by
hotelId, to see how rates move. - Market research — every property in a city with score, stars, type and price band, and how many there are.
- Revenue management — where a property sits against its competitors for the same dates and guests.
- Hospitality lead generation — every property of a type in an area, with its Booking page and coordinates.
- Review analysis — thousands of reviews with nationality, traveller type and the hotel's reply, for sentiment or service benchmarking.
Pricing
Simple pay-per-result pricing — no subscription and no start fee.
| Event | Price |
|---|---|
| Hotel returned | $0.0015 |
| Review returned | $0.0010 |
What that comes to:
- A search at the default depth, 100 hotels: $0.15.
- Every property in a big city, around 1,800: about $2.70.
- 100 hotels with 25 reviews each: $2.65 — $0.15 for the hotels, $2.50 for the 2,500 reviews.
- 1,000 reviews, however many hotels they come from: $1.00.
Error rows are never charged, and a search Booking has nothing for costs nothing.
Limits worth knowing
- Hotel-page details are not included — amenities, the named rooms with
their individual prices, the description and the photo gallery live on
pages that need a browser. Each row's
urlopens them. - Prices are Booking's quote for one booking as you defined it. Booking shows different prices for different guest counts and dates; change the input, not the arithmetic.
- Reviews cost per review. A property with a thousand reviews is a
thousand rows;
maxReviewsPerHotelcaps it. - A destination is resolved to Booking's best match. Every row carries
the resolved
destination, so an ambiguous name ("Paris") is visible as what Booking took it to be.
FAQ
Is scraping Booking.com legal? Results come from Booking.com and are subject to Booking's terms — check them, and your own use, before relying on the data. This Actor reads the same listing data Booking shows anyone searching, without logging in.
Do I need a Booking.com API key or partner account? No. There is no key, no login, no browser and no quota of your own to manage.
Why is price null on some rows?
Either no dates were given (then no row has a price), or Booking quoted no
rate for that property for those dates — sold out, or minimum-stay rules.
It is left null rather than guessed.
Can I get a specific hotel?
Yes — type its name as a destination. Booking resolves it to the property
itself, which arrives as the first row (with destinationId hotel:…),
followed by its neighbours. Turn reviews on to get that hotel's reviews.
Hotel-page links cannot be read directly, but the hotel's name can.
How do I know a search failed if the run stays green?
Check for rows with type: "error" — that is the failure channel by
design, so one bad search never costs the rest of the job. The run's status
message also counts how many searches could not be read.
Results come from Booking.com and are subject to Booking's terms. Unofficial, and not affiliated with Booking.com.