Booking.com Scraper: Guest Review Sentiment Analysis
Pricing
Pay per usage
Booking.com Scraper: Guest Review Sentiment Analysis
๐จ BookingScraper extracts hotel & apartment data from Booking.com โ listings, prices, real-time availability, amenities, reviews & ratings โ at scale. โก Export to CSV/JSON/API for market research, price monitoring, competitor analysis & travel apps. ๐ Fast, reliable, proxy-ready.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Scrapier
Maintained by CommunityActor stats
0
Bookmarked
7
Total users
4
Monthly active users
2 days ago
Last modified
Categories
Share
Booking.com Scraper โ Hotels, Guest Reviews and Sentiment Analysis
Booking.com Scraper: Guest Review Sentiment Analysis takes a destination search or a list of Booking.com URLs and returns three linked result types in one dataset: the full hotel record, each property's guest reviews as child rows, and a keyless reviewSentiment rollup โ positive/neutral/negative counts, recommend rate and a ranked aspect breakdown โ computed per hotel with no API key and no login. Every response is structured JSON, ready to pass straight to an LLM, load into a spreadsheet, or feed a monitoring pipeline. Point it at a city, a landmark, or a hotel URL and get hotel data plus scored guest sentiment back from the same run.
What is Booking.com Scraper: Guest Review Sentiment Analysis?
Booking.com Scraper: Guest Review Sentiment Analysis is an Apify Actor that queries Booking.com by destination (or a supplied list of hotel/search URLs) and returns hotel property records, each hotel's embedded guest reviews as linked child rows, and a computed sentiment/aspect summary per hotel โ all as structured JSON or CSV. No Booking.com account, login, or API key is required for the core hotel, review and sentiment data; an API key is only needed if you turn on the optional LLM review-classification add-on. What sets it apart from a plain review scraper is that the reviewSentiment object is calculated directly from the scraped reviews' real 1โ10 scores and liked/disliked text โ not copied from a UI label โ so every hotel row carries a ready-to-use reputation score without a separate analysis step.
- Hotel property data โ name, stars, price, currency, guest rating, rooms, facilities, address, coordinates, house-rule policies, trader/host contact info and breadcrumbs.
- Guest review child rows โ liked/disliked text, per-review score, review title, reviewer name and country, traveller type, trip purpose, room type and review date, linked to the parent hotel.
- Keyless sentiment and aspect rollup โ positive/neutral/negative counts, recommend rate, average review score and a ranked list of aspects (staff, cleanliness, location, comfort, facilities, value, breakfast, food, room, Wi-Fi, noise) mentioned across the reviews.
- Optional AI review classification โ a multi-provider LLM pass that adds
aiSentimentandaiTopicsper review when you supply your own API key. - Query controls โ destination text or start URLs, property type, star rating, minimum guest rating, sort order, currency, interface language, stay dates with flexible-window search, room/guest counts and a price range filter.
- Two input modes โ a free-text destination search, or a pasted list of Booking.com hotel and search-results URLs, both supported in the same run.
๐ง What data can you get with Booking.com Scraper: Guest Review Sentiment Analysis?
The Actor pushes two row types to one dataset โ hotel rows and review rows โ plus a computed sentiment object attached to every hotel row.
| Result Type | Extracted Fields | Primary Use Case |
|---|---|---|
Hotel (parent row, type: "hotel") | name, stars, price, currency, rating, ratingLabel, reviews, address, location, rooms, facilities, policies, images, hotelChain, licenseInfo, hostInfo, traderInfo, breadcrumbs, hotelId, url | Property comparison, pricing and availability research |
Guest review (child row, type: "review") | reviewerName, reviewerCountry, travellerType, tripPurpose, reviewScore, sentiment, title, positiveText, negativeText, roomType, reviewedAt, reviewUrl, aiSentiment, aiTopics | Guest feedback analysis, reputation tracking |
| Sentiment & aspect rollup (on every hotel row) | reviewSentiment.reviewsAnalyzed, .positiveCount, .neutralCount, .negativeCount, .recommendRate, .averageReviewScore, .topAspects[] | Aggregate reputation scoring without a separate analysis tool |
๐ง Keyless sentiment and aspect rollup
reviewSentiment is attached to every hotel row when analyzeSentiment is on (default). It is built entirely from the reviews collected for that hotel โ no third-party sentiment API, no keyword dictionary lookup service, no LLM call unless you separately enable aiEnhancement. Per-review polarity comes from review_sentiment(): a score โฅ 8 is positive, a score โฅ 6 is neutral (nudged to negative if the review has disliked text but no liked text), anything below 6 is negative; when a review has no numeric score at all, polarity falls back to whichever of positiveText / negativeText is present. The hotel-level rollup then counts polarities, computes a recommend rate (positiveCount / judgedReviews), averages the numeric scores, and scans every liked/disliked span against a fixed keyword vocabulary (cleanliness, staff, location, comfort, facilities, value, breakfast, food, room, Wi-Fi, noise) to rank the top 10 aspects by mention volume, each carrying a net polarity:
"reviewSentiment": {"reviewsAnalyzed": 10,"positiveCount": 8,"neutralCount": 1,"negativeCount": 1,"recommendRate": 0.8,"averageReviewScore": 8.6,"topAspects": [{ "aspect": "staff", "positiveMentions": 7, "negativeMentions": 1, "netSentiment": "positive" },{ "aspect": "location", "positiveMentions": 6, "negativeMentions": 0, "netSentiment": "positive" },{ "aspect": "room", "positiveMentions": 4, "negativeMentions": 2, "netSentiment": "positive" }]}
Guest review child rows
Each hotel's featured reviews โ read straight out of the same page's embedded Apollo state used to build the hotel record, no second request โ become their own dataset rows with type: "review", isChild: true and parentId set to the hotel's hotelId. A review row carries the liked and disliked text separately, the reviewer's numeric score, traveller type ("Family with young children", "Business traveller", "Solo traveller"โฆ), trip purpose, room type, and the review date normalized to ISO 8601. Reviews are also mirrored, uncharged, into a per-run reviews-<runId> named dataset for downstream jobs that only need the review rows.
Why not build a Booking.com scraper yourself?
Booking.com does not expose a self-serve public API for hotel search or guest reviews โ its Demand/Connectivity APIs are partner-gated and unsuitable for one-off destination research. Building this yourself means reverse-engineering the hotel page's embedded Apollo GraphQL cache (which changes shape between deploys), then getting past Booking.com's AWS WAF challenge, which blocks plain HTTP clients outright and requires a real browser to solve a JavaScript challenge and hold the resulting aws-waf-token cookie across requests. On top of that you need residential proxy capacity, retry/rotation logic for when the challenge doesn't clear, and your own sentiment scoring layer if you want anything beyond raw review text. Booking.com Scraper: Guest Review Sentiment Analysis already does all of that โ Playwright-driven WAF solving, Apollo-state parsing across four fallback layers, and the keyless sentiment rollup โ so you configure a destination or URL list and get typed rows back instead of maintaining a scraping pipeline.
What's the difference between a Booking.com review scraper and a guest sentiment analysis tool?
A review scraper returns raw text: the reviewer's score, their liked and disliked comments, and metadata like traveller type โ one row per review, with no judgment attached. A sentiment analysis tool takes that same review text and turns it into a per-property verdict: how many guests were net positive, what a "recommend rate" looks like, and which specific aspects (staff, cleanliness, location) are driving satisfaction or complaints. The distinction matters because reading fifty raw reviews to decide whether a hotel's cleanliness complaints are trending up is not something most workflows can do at scale, while a single aggregated number or aspect list can be diffed, sorted, and alerted on automatically. Booking.com Scraper: Guest Review Sentiment Analysis returns both in the same run โ the raw review child rows for anyone who wants the source text, and the reviewSentiment object on the parent hotel row for anyone who wants the aggregate. You don't have to choose one architecture over the other or run a separate NLP step; both arrive in the same dataset, from the same guest reviews, computed the same way every run.
How to scrape Booking.com hotels and guest reviews with Booking.com Scraper: Guest Review Sentiment Analysis?
- Open Booking.com Scraper: Guest Review Sentiment Analysis on its Apify Store listing and click Try for free (or Run, if you already have it saved).
- Enter a destination in
location(e.g."Paris"), or paste one or more Booking.com hotel/search-results links intostartUrlsโlocationis ignored wheneverstartUrlsis non-empty. - Set the query controls that matter for your search:
propertyType,starsCountFilter,minimumRating,sortBy,currency,language, and optionallycheckIn/checkOut/flexWindowfor live-priced results. - Leave
scrapeReviewsandanalyzeSentimenton (both defaulttrue) and setmaxHotels/maxReviewsPerHotelto your target size, then start the run. - Open the Output tab to inspect hotel and review rows in the table view, or export the full dataset as JSON, CSV, Excel or XML from the same tab.
{"location": "Paris","maxHotels": 10,"scrapeReviews": true,"maxReviewsPerHotel": 10,"analyzeSentiment": true,"currency": "EUR","starsCountFilter": "4"}
How to run multiple destinations or hotels in one job
There is no separate "batch" input โ startUrls itself accepts as many Booking.com hotel or search-results links as you want to paste in, and each is scraped in the same run. Note that maxHotels is a global ceiling across the whole run, not a per-URL limit: if you paste 50 hotel URLs with maxHotels left at its default of 10, only the first 10 hotels (in list order) are scraped and the rest are skipped. For search-results URLs mixed into the same startUrls list, each one is expanded into individual hotel links before the same global cap is applied. To cover more than maxHotels (20,000 maximum), split the destinations across separate runs โ the Actor exposes no built-in concurrency setting to parallelize a single run beyond what one browser context processes sequentially.
โฌ๏ธ Input
All 25 input fields are optional โ there is no required parameter, and an empty run (no location, no startUrls) exits immediately without scraping anything.
| Parameter | Type | Default | Constraints |
|---|---|---|---|
location | string | "" | Destination text (city, region, landmark). Ignored when startUrls is non-empty. |
startUrls | array of strings | [] | Booking.com hotel detail or search-results page URLs. |
maxHotels | integer | 10 | minimum: 1, maximum: 20000. Global cap on hotel (parent) rows for the run. |
scrapeReviews | boolean | true | Emit per-review child rows. |
maxReviewsPerHotel | integer | 10 | minimum: 0, maximum: 100. 0 = all reviews Booking embeds on the page. |
reviewSortBy | string (enum) | "most_relevant" | most_relevant | newest | highest_score | lowest_score. |
analyzeSentiment | boolean | true | Adds the keyless reviewSentiment object to each hotel row. Free, no key needed. |
aiEnhancement | boolean | false | Turns on optional LLM sentiment/topic classification per review. Requires aiApiKey (or a matching provider env var) โ without one, AI fields stay null. |
aiModel | string (enum) | "claude-haiku-4-5" | One of 14 models across Anthropic, OpenAI, Google, xAI, DeepSeek, Perplexity, Mistral. Provider is auto-detected from the model name prefix. |
aiApiKey | string, secret | โ | Provider key matching aiModel. Marked isSecret โ masked in the Apify Console and encrypted at rest. Only read when aiEnhancement is true. |
includeHotelDetails | boolean | true | On: rooms, facilities, images, trader/host info, policies, breadcrumbs. Off: core identity fields only (name, price, rating, address, location, stars). |
propertyType | string (enum) | "none" | none, hotels, apartments, hostels, guest houses, homestays, bed and breakfasts, holiday homes, boats, villas, motels, resorts, holiday parks, campsites, luxury tents. |
sortBy | string (enum) | "distance_from_search" | distance_from_search | price | review_score_and_price | review_score | class. |
minimumRating | string | "" | Minimum guest rating (e.g. "7", "8", "9"). Empty = no filter. |
starsCountFilter | string (enum) | "any" | any, 1, 2, 3, 4, 5. |
currency | string (enum) | "USD" | USD, EUR, GBP, CAD, AUD, CHF, JPY, CNY, INR, BRL, MXN. |
language | string (enum) | "en-gb" | en-gb, en-us, de, fr, es, it, pt-br, nl, pl, ru, ja, zh. |
checkIn | string | "" | Absolute YYYY-MM-DD or relative ("2 weeks", "1 month"). Empty = flexible search. |
checkOut | string | "" | Same format as checkIn. |
flexWindow | string (enum) | "0" | 0, 1, 2, 3, 7 โ number of days the stay dates may shift. |
rooms | integer | 1 | minimum: 1, maximum: 9. |
adults | integer | 2 | minimum: 1, maximum: 30. |
children | integer | 0 | minimum: 0, maximum: 30. |
minMaxPrice | string | "0-999999" | Free-text range, e.g. "50-200" or "100+" (minimum only). |
proxyConfiguration | object | {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]} | Apify Proxy configuration. Residential is required to clear Booking.com's bot protection. |
{"location": "New York","maxHotels": 5,"scrapeReviews": true,"maxReviewsPerHotel": 10,"reviewSortBy": "newest","analyzeSentiment": true,"aiEnhancement": false,"includeHotelDetails": true,"propertyType": "hotels","sortBy": "review_score","starsCountFilter": "4","currency": "USD","language": "en-gb","checkIn": "","checkOut": "","flexWindow": "0","rooms": 1,"adults": 2,"children": 0,"minMaxPrice": "0-999999","proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
Common pitfall: checkIn / checkOut only accept an absolute YYYY-MM-DD date or a relative phrase matching "<number> day(s)/week(s)/month(s)/year(s)" (e.g. "2 weeks"). A free-text value that doesn't match either pattern โ "next week", "tomorrow" โ is silently normalized to an empty string and the search runs without stay dates, so price fields that depend on live pricing come back null with no error raised. Similarly, minMaxPrice values that don't parse as "min-max" or "min+" are silently ignored rather than rejected.
โฌ๏ธ Output
Results are pushed to the Actor's default dataset as typed, normalized JSON โ one item per hotel and one item per guest review, interleaved (each hotel's reviews immediately follow it). Export from the Apify Console as JSON, CSV, Excel, XML, RSS or HTML from the same Export results menu the platform provides for every dataset.
Scraped results
[{"order": 1,"url": "https://www.booking.com/hotel/us/trump-international-new-york.en-gb.html","startUrlOrQuery": "New York","name": "Trump International Hotel & Tower New York","type": "hotel","description": "Overlooking Central Park, this luxury hotel offers spacious suites with kitchens...","stars": 5,"price": 780.0,"currency": "USD","rating": 9.0,"ratingLabel": "Superb","reviews": 794,"breakfast": "Available","checkIn": "From 15:00","checkOut": "Until 12:00","checkInDate": null,"checkOutDate": null,"location": { "lat": 40.7691, "lng": -73.9821 },"address": { "full": "One Central Park West", "country": "US", "city": "New York" },"image": "https://cf.bstatic.com/xdata/images/hotel/example.jpg","rooms": [{ "id": "31023", "name": "Deluxe Suite", "description": "City view, kitchenette", "size": "60", "occupancy": 3, "price": 780.0 }],"highlights": ["Central Park view", "Spa", "Fitness centre"],"finePrint": "Guests are required to show a photo ID and credit card at check-in.","policies": ["Guests are required to show a photo identification and credit card upon check-in.", "The minimum age for check-in is 21."],"images": ["https://cf.bstatic.com/xdata/images/hotel/example1.jpg", "https://cf.bstatic.com/xdata/images/hotel/example2.jpg"],"roomImages": [{ "roomId": "31023", "images": ["https://cf.bstatic.com/xdata/images/room/example.jpg"] }],"categoryReviews": [{ "name": "Staff", "score": 9.4 }, { "name": "Location", "score": 9.8 }],"hotelChain": null,"licenseInfo": null,"hostInfo": null,"traderInfo": {"isBusiness": true, "email": null, "phone": null, "companyName": "Trump International Hotel LLC","firstName": null, "middleName": null, "lastName": null, "registrationNumber": null, "tradeRegisterName": null,"address": { "street": "1 Central Park West", "street2": null, "postalCode": "10023", "city": "New York", "countryCode": "US", "state": "NY" }},"breadcrumbs": [{ "name": "United States", "url": "https://www.booking.com/country/us.html" }, { "name": "New York", "url": "https://www.booking.com/city/us/new-york.html" }],"hotelId": "291324","facilities": [{ "name": "Free WiFi", "id": "107" }, { "name": "Fitness centre", "id": "46" }],"timeOfScrapeISO": "2026-07-25T14:02:11.408Z","source_url": "https://www.booking.com/hotel/us/trump-international-new-york.en-gb.html","isChild": false,"parentId": null,"reviewSentiment": {"reviewsAnalyzed": 3, "positiveCount": 2, "neutralCount": 1, "negativeCount": 0,"recommendRate": 0.667, "averageReviewScore": 9.0,"topAspects": [{ "aspect": "location", "positiveMentions": 2, "negativeMentions": 0, "netSentiment": "positive" }]},"reviewsCollected": 3},{"type": "review", "isChild": true, "parentId": "291324", "parentHotelName": "Trump International Hotel & Tower New York","hotelUrl": "https://www.booking.com/hotel/us/trump-international-new-york.en-gb.html","reviewId": "5294536437", "reviewerName": "Netta", "reviewerCountry": "GB","travellerType": "Family with young children", "tripPurpose": "Leisure","title": "Perfect stay with kids in New York!","positiveText": "Amazing location right by Central Park, extremely comfortable beds, staff went out of their way to help.","negativeText": null, "reviewScore": 10.0, "sentiment": "positive", "language": "en","roomType": "Deluxe Suite", "reviewedAt": "2026-06-01T16:07:48Z","reviewUrl": "https://www.booking.com/reviewlist.html?r=abc123def456","aiSentiment": null, "aiTopics": null},{"type": "review", "isChild": true, "parentId": "291324", "parentHotelName": "Trump International Hotel & Tower New York","hotelUrl": "https://www.booking.com/hotel/us/trump-international-new-york.en-gb.html","reviewId": "5288120044", "reviewerName": "Marco", "reviewerCountry": "IT","travellerType": "Couple", "tripPurpose": "Leisure","title": "Great location, dated bathroom","positiveText": "Walking distance to everything, very quiet at night.","negativeText": "Bathroom fittings felt a bit dated for the price point.","reviewScore": 7.5, "sentiment": "neutral", "language": "en","roomType": "Executive Room", "reviewedAt": "2026-05-14T09:22:03Z","reviewUrl": "https://www.booking.com/reviewlist.html?r=ghi789jkl012","aiSentiment": null, "aiTopics": null},{"type": "review", "isChild": true, "parentId": "291324", "parentHotelName": "Trump International Hotel & Tower New York","hotelUrl": "https://www.booking.com/hotel/us/trump-international-new-york.en-gb.html","reviewId": "5271044981", "reviewerName": "Chen", "reviewerCountry": "SG","travellerType": "Business traveller", "tripPurpose": "Business","title": "Reliable business stay","positiveText": "Fast WiFi, quiet workspace in the suite, front desk handled a late check-in without any fuss.","negativeText": null, "reviewScore": 9.2, "sentiment": "positive", "language": "en","roomType": "Deluxe Suite", "reviewedAt": "2026-04-29T20:11:57Z","reviewUrl": "https://www.booking.com/reviewlist.html?r=mno345pqr678","aiSentiment": null, "aiTopics": null}]
Hotel row fields (type: "hotel") โ every key extract_hotel_data() and the run loop write to the parent row:
| Field | Description |
|---|---|
order | 1-based position of this hotel within the run. |
url | Canonical Booking.com hotel URL, from the page's schema.org JSON-LD. |
startUrlOrQuery | The location search text, or the input URL if one was used. |
name | Hotel name. |
type | Always "hotel" on parent rows. |
description | Property description text. |
stars | Official star rating, when present on the page. |
price | Cheapest room price found on the page (see the โ ๏ธ note below), or null. |
currency | Currency code for price, e.g. "USD". |
rating | Guest review score (Booking's 1โ10 scale). |
ratingLabel | Booking's text label for the score (e.g. "Superb", "Very good"). |
reviews | Total review count reported by Booking (not the number of review rows scraped โ see reviewsCollected). |
breakfast | "Available" when breakfast is detected in facilities/highlights or page text, else null. |
checkIn / checkOut | The hotel's stated check-in/check-out time window (e.g. "From 15:00"), not the search dates. |
checkInDate / checkOutDate | The stay dates you searched with (from input checkIn/checkOut), or null. |
location | { lat, lng } coordinates. |
address | { full, country, city }. |
image | First property image URL. |
rooms | Array of { id, name, description, size, occupancy, price }, empty when includeHotelDetails is off. |
highlights | Array of highlighted amenity strings, empty when includeHotelDetails is off. |
finePrint | Concatenated fine-print disclosures, null when includeHotelDetails is off. |
policies | Array of real house-rule phrases (ID/credit-card requirement, age limits, quiet hours, party/curfew rules), empty array when none are present or when includeHotelDetails is off. |
images | Up to 50 image URLs, empty when includeHotelDetails is off. |
roomImages | Array of { roomId, images[] }, empty when includeHotelDetails is off. |
categoryReviews | Array of { name, score } per-category guest scores (e.g. Staff, Location), empty when includeHotelDetails is off. |
hotelChain | Chain name, or null. |
licenseInfo | First registered license number, or null. |
hostInfo | Host display name for non-corporate listings, or null. |
traderInfo | { isBusiness, email, phone, companyName, firstName, middleName, lastName, registrationNumber, tradeRegisterName, address }, null when includeHotelDetails is off. |
breadcrumbs | Array of { name, url } site breadcrumb links, empty when includeHotelDetails is off. |
hotelId | Booking.com's internal property ID โ also used as parentId on this hotel's review rows. |
facilities | Array of { name, id }, empty when includeHotelDetails is off. |
timeOfScrapeISO | UTC timestamp of the scrape. |
source_url | The exact URL fetched for this hotel. |
isChild | Always false on hotel rows. |
parentId | Always null on hotel rows. |
reviewSentiment | The keyless rollup object described above, or null when analyzeSentiment is off. |
reviewsCollected | Number of review rows actually collected and pushed for this hotel (after maxReviewsPerHotel is applied). |
Review row fields (type: "review") โ every key _normalize_featured() plus the push loop write to each child row:
| Field | Description |
|---|---|
type | Always "review" on child rows. |
isChild | Always true on review rows. |
parentId | The parent hotel's hotelId. |
parentHotelName | The parent hotel's name, copied for convenience. |
hotelUrl | The parent hotel's url. |
reviewId | Booking.com's internal review ID. |
reviewerName | Guest display name (falls back to Booking's own anonymized label when the reviewer opted out). |
reviewerCountry | Guest's country code, upper-cased, or null. |
travellerType | Human-readable label ("Solo traveller", "Couple", "Family with young children", "Family with older children", "Group", "Group of friends", "Business traveller", "With friends"), or the raw Booking code title-cased if unmapped. |
tripPurpose | "Leisure" or "Business", or null. |
title | Review headline text, or null. |
positiveText | What the guest liked. Booking's placeholder values ("Nothing", "N/A", "-", "None") are normalized to null. |
negativeText | What the guest disliked, same placeholder normalization. |
reviewScore | The reviewer's numeric score on Booking's 1โ10 scale, or null. |
sentiment | Keyless polarity for this single review โ positive / neutral / negative / null โ computed by review_sentiment(). |
language | Review language code as tagged by Booking. |
roomType | The room type the reviewer booked, or null. |
reviewedAt | Review completion timestamp, ISO 8601 UTC. |
reviewUrl | Deep link to the review on Booking.com, or null. |
aiSentiment | LLM-classified sentiment (positive/neutral/negative), populated only when aiEnhancement is on with a valid key โ otherwise null, never faked. |
aiTopics | Up to 4 LLM-generated topic keywords per review, same condition as aiSentiment. |
โ ๏ธ Price note: price is the lowest per-room price the page exposes, taken across the room list โ not a nightly-rate-for-your-dates figure unless you supplied checkIn/checkOut. Without stay dates, Booking often shows no live price at all and price comes back null.
How can I use the data extracted with Booking.com Scraper: Guest Review Sentiment Analysis?
- ๐จ Revenue managers and hospitality teams: pull
reviewSentiment.recommendRateandtopAspectsfor your own property and named competitors to see which specific aspect (staff, cleanliness, breakfast) is dragging the score down before it shows up in the overall rating. - ๐ค AI engineers and LLM developers: an agent issues a destination or hotel query, receives the hotel + review +
reviewSentimentJSON back, and passes it to the model as grounded context โ no separate NLP pipeline needed since the sentiment rollup already ships pre-computed. - ๐ Market researchers: aggregate
rating,priceandreviewSentiment.averageReviewScoreacross every hotel in a destination search to build share-of-voice and price-versus-satisfaction views for a market. - ๐งณ Travel product and OTA teams: compare
topAspectsnet polarity across a shortlist of properties to identify which listings are strong on the aspects your users search for (location, breakfast, Wi-Fi) before they're featured or promoted.
๐ How do you monitor guest review sentiment over time?
Guest sentiment is not static โ a hotel's recommendRate and aspect mix shift as new reviews post, renovations happen, or service quality changes, and Booking.com's own UI gives you no way to see that trend. The pattern is a scheduled run repeated against the same hotel URLs or destination search, with each run's output diffed against the last. The fields worth tracking between runs are reviewSentiment.recommendRate, reviewSentiment.positiveCount / .negativeCount, reviewSentiment.averageReviewScore, and the netSentiment of specific entries inside reviewSentiment.topAspects โ a "staff" aspect flipping from positive to mixed between two runs is a concrete, actionable signal that raw star ratings won't surface for weeks. Because reviews are read from what Booking currently embeds on the page rather than a fixed archive, later runs will naturally include newer reviews and drop older ones out of the ~10-review window, so the rollup itself is already a rolling snapshot rather than a lifetime average โ comparing consecutive runs is comparing consecutive rolling windows, which is exactly what you want for catching a recent quality shift. A typical workflow: schedule a weekly run across your tracked hotel URLs โ pull reviewSentiment from each run's dataset โ compare recommendRate and topAspects against the previous week's export โ alert when recommendRate drops by a meaningful margin or a previously positive aspect turns negative. Set the recurring run up with Apify's built-in Schedules (Console โ Schedules โ New schedule, pointed at this Actor with a saved input), and either read each run's dataset via the API afterward or wire an Apify webhook on the run-succeeded event to notify your own system the moment new data lands.
Integrate Booking.com Scraper: Guest Review Sentiment Analysis and automate your workflow
Booking.com Scraper: Guest Review Sentiment Analysis works with any language or tool that can call the Apify API โ there is no separate signup or authentication scheme beyond your Apify account token.
REST API with Python
from apify_client import ApifyClientclient = ApifyClient("<APIFY_API_TOKEN>")run = client.actor("<YOUR_USERNAME>/booking-scraper-guest-review-sentiment-analysis").call(run_input={"location": "Lisbon","maxHotels": 10,"scrapeReviews": True,"maxReviewsPerHotel": 10,"analyzeSentiment": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item.get("type") == "hotel":print(item["name"], item.get("reviewSentiment", {}).get("recommendRate"))
Swap <YOUR_USERNAME> for your Apify handle if you've made a copy of the Actor under your own account, or call the public Actor ID directly from the Store listing. <APIFY_API_TOKEN> is your personal Apify API token from Console โ Settings โ Integrations.
Scheduled monitoring and delivery
There is no bespoke webhook system inside the Actor itself โ you use the Apify platform's own Schedules (recurring runs on a cron expression) and Webhooks (HTTP callbacks on run events like ACTOR.RUN.SUCCEEDED) to turn a one-off run into an ongoing feed. Both are configured per-Actor from the Apify Console or via the API, and neither requires code changes to this Actor's input.
Is it legal to scrape Booking.com hotel and review data?
Yes โ scraping publicly accessible web pages is generally lawful in most jurisdictions, and Booking.com Scraper: Guest Review Sentiment Analysis only returns what any visitor can already see on a Booking.com hotel or search-results page, without logging in. The bulk of what this Actor returns โ hotel names, prices, room descriptions, facilities, addresses โ is business/product data, which is governed primarily by Booking.com's own Terms of Service and, in some jurisdictions, database-rights law rather than data-protection law. Guest reviews carry a thinner layer of personal data (a reviewer's display name and country), so if you store or process review data involving EU or California residents at scale, GDPR and CCPA considerations can apply to those specific fields even though the hotel data itself does not implicate them. Scraping for internal monitoring or research carries a different risk profile than scraping to train or fine-tune a model for redistribution โ treat the two differently. Consult your legal team for commercial use cases involving bulk data storage or redistribution.
โ Frequently asked questions
Does Booking.com Scraper: Guest Review Sentiment Analysis require a Booking.com account or login?
No. Both the hotel scrape and the review scrape read data embedded in the public hotel page's HTML/Apollo state โ there is no login step and no session cookie beyond what's needed to clear Booking.com's bot-protection challenge.
What languages and currencies does it support?
language covers 12 Booking.com locales (en-gb, en-us, de, fr, es, it, pt-br, nl, pl, ru, ja, zh), which changes both the site domain requested and the review-text language returned. currency covers 11 currency codes (USD, EUR, GBP, CAD, AUD, CHF, JPY, CNY, INR, BRL, MXN) for displayed prices.
How do I control hotel and review order?
sortBy controls the order hotels come back in a destination search (distance, price, review score, review score + price, or star class). reviewSortBy independently controls each hotel's review order โ most relevant (Booking's own order), newest, highest score, or lowest score โ before maxReviewsPerHotel trims the set.
How does Booking.com Scraper: Guest Review Sentiment Analysis handle Booking.com's anti-bot measures?
Booking.com sits behind AWS WAF, which 302-redirects plain HTTP clients before they ever see page content. The Actor drives a headless Playwright Chromium browser through Apify's Residential proxy, waits for the aws-waf-token cookie to be issued, and reloads the page until the real content (identified by page size and the presence of the Apollo data block or a property card) is served โ retrying with a fresh browser context up to 3 times if the challenge doesn't clear.
Does Booking.com Scraper: Guest Review Sentiment Analysis extract a computed sentiment score, not just raw reviews?
Yes. The reviewSentiment object on every hotel row is computed deterministically from that hotel's own scraped reviews โ polarity from each review's real 1โ10 score and liked/disliked text, then aggregated into counts, a recommend rate, an average score and a ranked aspect list. It requires no API key and is on by default (analyzeSentiment: true); it is absent (null) only when you explicitly turn it off, or trivially empty when scrapeReviews is off and there are no reviews to score.
How many hotels and reviews does it return per run?
Up to maxHotels hotels (1โ20,000, default 10) and up to maxReviewsPerHotel reviews per hotel (0โ100, default 10, where 0 means take everything available). In practice, Booking.com's hotel page embeds only its most recent featured reviews โ typically up to around 10 โ and this Actor reads that embedded set with no separate pagination request, so setting maxReviewsPerHotel above what a given hotel page actually embeds will not produce more rows than the page provides.
How do I use it to monitor guest sentiment over time?
Schedule a recurring run over the same hotel URLs or destination with Apify Schedules, then compare reviewSentiment.recommendRate, .positiveCount/.negativeCount and .topAspects between consecutive runs' dataset exports, alerting when the recommend rate drops or an aspect's netSentiment flips negative.
Does Booking.com Scraper: Guest Review Sentiment Analysis work with Claude, ChatGPT and AI agent frameworks?
Yes, as an HTTP endpoint any agent framework can call through the Apify API โ there is no dedicated MCP server documented for this Actor. An agent triggers a run with apify_client (or a raw REST call), reads the resulting dataset, and grounds its answer in the returned hotel and reviewSentiment data.
How does Booking.com Scraper: Guest Review Sentiment Analysis compare to other Booking.com review scrapers?
plowdata/booking-com-review-scraper also targets Booking.com and returns raw review rows plus Booking's own internal per-category score dataset (location, cleanliness, etc. with confidence intervals) โ its listing does not document a computed sentiment or aspect rollup, and it advertises a review-scrape speed of roughly 30โ50 reviews/second on its Apify Store page, checked 2026-07-25 โ that figure is theirs, not independently measured here. memo23/hotels-scraper targets Hotels.com rather than Booking.com and surfaces sentiment tags ("Liked: cleanliness, staff & service") that come from Hotels.com's own GraphQL API rather than being computed by the Actor. Booking.com Scraper: Guest Review Sentiment Analysis is the only one of the three, as checked on the Apify Store 2026-07-25, that returns the full hotel property record, the raw review rows, and a self-computed keyless reviewSentiment/topAspects rollup together in one dataset.
Can I use it without managing proxies or Booking.com credentials?
Yes for proxies โ Apify Residential proxy is pre-wired into the default proxyConfiguration and is what clears Booking.com's WAF challenge, so no proxy account or IP management is required. No Booking.com credentials are needed at all. The only credential you would ever supply is your own AI provider API key (aiApiKey), and only if you turn on the optional aiEnhancement classification pass.
๐ฌ Your feedback
Found a bug, or a hotel/review field that doesn't match what you see on the live Booking.com page? Open an issue directly from this Actor's page in the Apify Console (the Issues tab) with the run ID and the URL that produced the problem โ the Scrapier team reviews reports there and ships fixes as new Actor builds.