Booking.com Review Scraper
Pricing
from $1.50 / 1,000 results
Booking.com Review Scraper
Which room type gets the complaints? What do guests really say about your breakfast? Scrape Booking.com reviews at room level — score, liked/disliked texts, stay details, traveler type, management replies — with score-band filters and topic search. Deduplicated, LLM-ready.
Pricing
from $1.50 / 1,000 results
Rating
0.0
(0)
Developer
NoraView Intelligence
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
5 days ago
Last modified
Share
💬 Booking.com Review Scraper
Room-level guest review corpus for any Booking.com hotel — up to a few hundred of the newest reviews per hotel, one clean record per review. Built as a data foundation for revenue managers and LLM pipelines: sampling, summarization and sentiment analysis happen downstream; this actor's job is to return a complete, deduplicated, structured corpus — fast and cheap.
Instead of rendering heavy hotel pages, this actor queries the same internal review endpoint Booking's own review panel uses. One page load per hotel, then light JSON calls (25 reviews per call) — a fraction of the proxy cost of page-scraping actors.
Why this scraper?
- 🛏️ Room-level granularity — every review carries the room stayed in, plus
Booking's
booking_room_id, so you can join reviews directly against room-level price data (e.g. the sibling Booking.com Room-Level Price Tracker) - 🎯 Server-side score filter — pull only the bands you need (Superb 9+ / Good 7-9 / Passable 5-7 / Poor 3-5 / Very poor 1-3). Booking does the filtering, you don't pay for pages you'll throw away
- 🔍 Topic search — same as Booking's "Search guest reviews by topic" bar:
breakfast,noise,wifi,pool... Combines with the score filter - 🧳 Full stay context — nights, stay month, traveler type (solo/couple/family/group/business), reviewer country, review language
- 💬 Management responses — property replies included
- 🧹 Corpus-ready — stable Booking review IDs, deduplicated, newest-first,
score-only reviews kept and flagged (
has_text) so they count toward evidence totals without polluting your LLM prompts
📥 Input
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
hotelUrls | array | ✅ | — | Booking.com hotel URLs to scrape reviews for |
maxReviewsPerHotel | integer | ❌ | 200 | Newest reviews to collect per hotel (~8 pages at 200) |
cutoffMonths | integer | ❌ | 24 | Stop paginating once reviews are older than this — beyond that, a review no longer describes the hotel as it is today |
sort | string | ❌ | Newest first | Newest first, Oldest first, Highest/Lowest score first, Most relevant |
reviewScoreFilter | array | ❌ | All | Score bands to include (server-side). Multiple bands = one pass per band, merged and deduplicated |
searchText | string | ❌ | — | Topic search (Booking semantic search, like the site's own search bar) |
useApifyProxy | boolean | ❌ | true | Apify residential proxy — Booking blocks datacenter IPs |
proxyCountries | array | ❌ | ["FR"] | Proxy source market. Reviews are geo-independent — one country is normal |
proxyUrl | string | ❌ | — | External proxy (only if Apify Proxy is off) |
delayBetweenRequests | integer | ❌ | 2 | Seconds between review page fetches |
Example input
{"hotelUrls": ["https://www.booking.com/hotel/th/karon-homes.html"],"maxReviewsPerHotel": 200,"cutoffMonths": 24,"sort": "f_recent_desc","reviewScoreFilter": ["all"],"searchText": "","useApifyProxy": true,"proxyCountries": ["FR"],"delayBetweenRequests": 2}
Example: pain-point mining
Only negative reviews mentioning the air conditioning, last 12 months:
{"hotelUrls": ["https://www.booking.com/hotel/ma/savoy-le-grand.html"],"maxReviewsPerHotel": 300,"cutoffMonths": 12,"reviewScoreFilter": ["passable", "poor", "very_poor"],"searchText": "air conditioning"}
📤 Output
Review records (record_type: "review") — one per review
| Field | Type | Description |
|---|---|---|
review_id | string | Booking's own stable review ID — reliable dedup across runs |
booking_hotel_id | string | Booking's internal hotel ID |
hotel_url / hotel_name | string | Source hotel |
review_date | string | YYYY-MM-DD (UTC) |
review_score | number | 1–10 |
title | string | Review title (often null — Booking rarely returns titles anymore) |
positive_text | string | "Liked" text, in the reviewer's language |
negative_text | string | "Disliked" text |
has_text | boolean | false = score-only review: kept for evidence totals, skip it in LLM prompts |
language | string | Review language code from Booking (en, ru, ar...; xu = undetermined) |
booking_room_id | integer | Booking's room ID — join key against room-level price data |
room_name | string | Raw room name ("Stayed in: …") |
room_name_clean | string | Bed-type suffix stripped |
nights | integer | Length of stay |
stay_month | string | YYYY-MM — month of the stay (not the review date) |
traveler_type | string | solo, couple, family, group or business |
traveler_type_raw | string | Booking's raw enum (SOLO_TRAVELLERS, COUPLES...) |
reviewer_country | string | ISO-2 country code (BR, DE...) |
mgmt_response_text | string | Property's reply, if any |
mgmt_response_date | — | Always null — Booking no longer exposes the reply date via this endpoint |
helpful_votes_count | integer | "Helpful" votes (null on score-only reviews) |
scrape_timestamp | string | UTC timestamp of the run |
Hotel summary records (record_type: "hotel_summary") — one per hotel
Run metadata: reviews_count_on_booking, reviews_scraped, pages_fetched,
duplicates_dropped, sort, cutoff_months, score_filter, search_text,
per_band breakdown, and stop_reason (max_reviews, cutoff, last_page…).
🔍 How it works
- Bootstrap — loads the hotel page once to resolve Booking's internal hotel ID and destination ID
- Fetch — calls Booking's internal review endpoint (the one powering the on-site review panel): 25 reviews per call, newest first
- Stop conditions —
maxReviewsPerHotelreached, no more reviews, or the oldest review on the page passescutoffMonths(reviews arrive newest-first, so everything beyond that point is out of range too)
Filters are applied server-side, exactly like Booking's own UI: the score
filter maps to Booking's score bands, and searchText uses Booking's semantic
topic search — not a literal substring match, so it catches related wording
(breakfast also matches "morning meal"). Match highlights are stripped from
the text automatically.
Anti-block infrastructure
Rotating user-agents/viewports/timezones, a fresh browser context and proxy IP per hotel, resource blocking (images/fonts/tracking), cookie-consent and WAF challenge handling, exponential backoff on retry. Failed hotels produce an error record — one bad IP never sinks the run.
🚀 Use cases
- LLM sentiment pipelines — feed
has_text: truereviews into summarization; keep score-only reviews in the evidence counts - Room-level complaint analysis — join on
booking_room_id/room_name_cleanagainst the price tracker to find which room types get complaints, and whether they're priced accordingly - Competitor benchmarking — track review velocity, score trends and topic mix across a comp set
- Pain-point mining — score filter + topic search to isolate exactly the reviews that cost you stars
⚠️ Notes & limitations
mgmt_response_dateis alwaysnull— Booking exposes the reply text only- Booking's topic search is semantic (same behavior as booking.com's own search bar); match counts are approximate by design
- Review
titleis oftennull— Booking rarely returns titles anymore
📝 Changelog
v1.1.0
- Server-side score-band filter (Superb/Good/Passable/Poor/Very poor, multi-band merge + dedup)
- Server-side topic search (
searchText), combinable with the score filter <mark>highlight stripping,search_textin summary records
v1.0.0
- Initial release — review corpus, pagination, 24-month cutoff, dedup by Booking review ID, proxy + retry infrastructure