kestrel actors docs

kestrel actors › Guest & customer reviews

Booking.com Reviews Scraper — Hotel Guest Reviews & Ratings

A booking.com reviews scraper — a hotel review scraper for booking.com hotel reviews — that returns every guest review a property has, as structured rows: booking.com ratings on the 1–10 scale and out of five, the title, what the guest liked and what they disliked, the language it was written in, posting and stay dates, room, traveller type, reviewer name and country, guest photos and the property's public reply.

Run Booking.com Reviews Scraper on Apify →

Booking.com reviews scraper pricing: what one row costs

EventWhat it isPriceCharged for
reviewReview row$0.005One guest review: score on both the 1-10 and 5-star scales, title, what the guest liked and disliked, language, stay dates and room, traveller type, reviewer country, photos and the property's reply. Properties with no reviews are free.

You pay per delivered review row. Everything else is free: hotel rows, status rows, reviews dropped by maxRating or requireText, properties with no reviews, and lookups that fail. The number of reviews you asked for is the number you pay for, capped by maxReviewsPerHotel and by whatever spending limit you set on the run. A 50-review complaints feed across ten properties is at most 500 review rows a day and usually far fewer, because most properties have few new complaints.

Booking.com reviews scraper input reference

Every field of Booking.com Reviews, straight from the actor's input schema.

FieldTypeDefaultWhat it does
startUrlsarray[]Booking.com property pages, e.g. https://www.booking.com/hotel/pt/memmo-alfama.html — any language variant (.en-gb.html, .de.html) and query string is fine. The property is resolved through Booking's own autocomplete, and the resolved name comes back in the hotel and status rows so you can check the match.
hotelNamesarray[]Property names as you would type them into Booking's search box, e.g. "Memmo Alfama Lisbon" — add the city to disambiguate chains. The first hotel Booking's autocomplete returns is used; its full name comes back in the rows.
hotelIdsarray[]Booking's numeric property ids, e.g. 536251 — what an earlier run's hotel_id gives you. The fastest path: no lookup at all. Ids carry no name, so give a URL or a name if you want hotel_name filled.
maxReviewsPerHotelinteger2000 = every review the property has (Booking pages them 25 at a time; big hotels run into the thousands). N = the first N in the order below. The main cost control. Range 0-20000.
reviewsSortstring (enum)"most_recent"Which reviews come first. "Lowest rated" is the fastest way to find complaints; "Most recent" is the one to schedule. One of: most_recent, highest_rated, lowest_rated, most_relevant, oldest.
languagesarray[]Leave empty for every language. Otherwise the language codes to keep — en, de, fr, es, it, pt, nl, ru, ja, zh, ko, tr … — as Booking's own language filter uses them. Booking filters server-side, so the reviews you skip are never fetched or charged. The hotel row lists how many reviews each language holds.
travelerTypestring (enum)"all"Keep only reviews from one kind of guest, filtered by Booking itself (skipped reviews are never fetched or charged). One of: all, families, couples, friends, solo, business.
keywordstring""A word or phrase Booking searches the reviews for ("breakfast", "noise", "parking"). Server-side, so only matching reviews are fetched and charged. Leave empty for all.
maxRatingnumber00 = keep every review. 1-10 on Booking's scale = keep only reviews at or below it. Reviews are then read lowest score first and paging stops at the threshold, so a complaints feed reads only the pages that carry complaints. Filtered reviews are never charged. Range 0-10.
requireTextbooleanfalseDeliver and pay for only the reviews where the guest wrote something in "liked" or "disliked" — Booking allows title-only reviews, and this drops them before billing.
localestring"en-US"Booking locale for the request, e.g. en-US, en-GB, de-DE, fr-FR. It sets the language of Booking's labels (traveller type, country names); reviews always come back in the language they were written in.
includeHotelRowbooleantrueAlso emit one row per property with its id, name and location, Booking's seven category scores (staff, facilities, cleanliness, comfort, value, location, Wi-Fi), the review count and how many reviews each language and traveller type holds. Never charged.
sessionsinteger4How many proxy sessions (egress IPs) run in parallel. More is faster; each is paced separately. Range 1-16.
perIpnumber1Pace for each session. Booking answered 10 calls at 1/s from one IP without a hiccup in testing; 1/s per IP is the default. Range 0.1-3.
proxyConfigurationobject{"useApifyProxy": true}Apify Proxy is required so runs rotate IPs. The default datacenter group works for Booking's review endpoint (only Booking's HTML pages need a browser, and this actor never loads one).

Example input

{
  "startUrls": ["https://www.booking.com/hotel/pt/memmo-alfama.html", "https://www.booking.com/hotel/pt/the-lumiares.en-gb.html"],
  "maxReviewsPerHotel": 50,
  "reviewsSort": "most_recent",
  "maxRating": 6,
  "requireText": true
}
{
  "hotelNames": ["Memmo Alfama Lisbon", "The Lumiares Lisbon", "Hotel Valverde Lisboa"],
  "maxReviewsPerHotel": 0,
  "languages": ["en"],
  "requireText": true
}

Booking.com reviews scraper output fields

One row per guest review (charged), plus a free hotel context row and a free status row per property.

Dataset views in the Apify Console: Overview, Reviews, Complaints, Hotels, Status.

FieldTypeWhat it holds
typestringreview | hotel | status
hotel_idstringBooking.com's numeric property id
hotel_namestringProperty name as Booking's autocomplete resolved it (null when the input was a bare id)
urlstringThe Booking.com URL you gave, if any
localestringThe site locale the labels came in
citystring
regionstring
countrystring
country_codestring
latitudenumber
longitudenumber
score_staffnumberBooking's category score, 1-10
score_facilitiesnumber
score_cleanlinessnumber
score_comfortnumber
score_valuenumber
score_locationnumber
score_wifinumber
review_countintegerBooking's total number of reviews for the property
languagesarray[{code, count}] — how many reviews each language holds
traveler_typesarray[{type, count}] — FAMILIES, COUPLES, GROUP_OF_FRIENDS, SOLO_TRAVELLERS, BUSINESS_TRAVELLERS
seasonsarray[{season, count}] — Booking's time-of-year buckets
reviews_fetchedinteger
review_idstringBooking's review key (stable across runs)
ratingnumber1-10, Booking's scale
rating_5numberThe same score out of five
titlestring
positivesstringWhat the guest liked
negativesstringWhat the guest disliked
textstringLiked and disliked joined into one comment (null for score-only reviews)
languagestringLanguage code of the review text
review_datestringYYYY-MM-DD
check_instring
check_outstring
nightsinteger
room_typestring
room_idstring
traveler_typestringCouple, Family, Solo traveller … in the site language
traveler_type_codestringBooking's code: COUPLES, FAMILIES, GROUP_OF_FRIENDS, SOLO_TRAVELLERS, BUSINESS_TRAVELLERS
stay_statusstring
reviewer_namestring
reviewer_countrystring
reviewer_country_codestring
anonymousboolean
helpful_votesinteger
photosarrayGuest photo URLs
approvedboolean
responsestringThe property's public reply
targetstringThe input this status row is about
statusstringok | no_reviews | error
reviewsintegerReview rows delivered for this property
filteredintegerReviews fetched but dropped by maxRating / requireText (never charged)
pagesinteger
totalintegerBooking's review count for the property (after server-side filters)
duplicatesinteger1 when another input already covered this property
errorstring
fetched_atstring

Example row from a real run

Taken from the actor's README — this is the shape your dataset holds.

{
  "type": "review",
  "url": "https://www.booking.com/hotel/pt/memmo-alfama.html",
  "locale": "en-US",
  "review_id": "c90f61915f999a43",
  "rating": 10.0,
  "rating_5": 5.0,
  "title": "Best place to stay in Lisbon!",
  "positives": "Great location and amazing terrace.",
  "negatives": null,
  "text": "Great location and amazing terrace.",
  "language": "en",
  "review_date": "2026-08-28",
  "check_in": "2026-08-24",
  "check_out": "2026-08-27",
  "nights": 3,
  "room_type": "Premium Double or Twin Room",
  "room_id": "53625108",
  "traveler_type": "Couple",
  "traveler_type_code": "COUPLES",
  "stay_status": "stayed",
  "reviewer_name": "Jean",
  "reviewer_country": "Canada",
  "reviewer_country_code": "ca",
  "anonymous": false,
  "helpful_votes": 0,
  "photos": null,
  "approved": true,
  "response": null,
  "hotel_id": "536251",
  "hotel_name": "Memmo Alfama - Design Hotels",
  "fetched_at": "2026-08-29T10:12:04+00:00"
}

Booking.com reviews scraper in Python, JavaScript and curl

Python

from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("kestrel/booking-reviews-scraper").call(run_input={
    "startUrls": ["https://www.booking.com/hotel/pt/memmo-alfama.html"],
    "maxReviewsPerHotel": 100,
    "reviewsSort": "most_recent",
})
reviews = [r for r in client.dataset(run["defaultDatasetId"]).iterate_items() if r["type"] == "review"]
for r in reviews[:5]:
    print(r["rating"], r["traveler_type"], r["title"], "|", r["negatives"])

JavaScript (Node)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('kestrel/booking-reviews-scraper').call({
    hotelNames: ['Memmo Alfama Lisbon'], maxReviewsPerHotel: 0, maxRating: 6, requireText: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const complaints = items.filter(r => r.type === 'review');
console.log(complaints.length, 'complaints;', complaints.filter(r => r.response).length, 'answered by the hotel');

curl

curl -X POST "https://api.apify.com/v2/acts/kestrel~booking-reviews-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"hotelIds": ["536251"], "maxReviewsPerHotel": 25, "languages": ["en"]}'

The same three calls are runnable files in the examples repository: examples/booking-reviews-scraper.py|.js|.sh.

Limits and honest notes

  • Score-only reviews are real. Booking lets guests rate without writing; in a recent sample 11 of 25 reviews had no words. requireText drops them before billing.
  • Ids carry no name. An id resolves to reviews and scores but not to a name — Booking's lookup runs from name to id, not back. Give a URL or a name if you need hotel_name filled.
  • Unknown ids look like empty properties. Booking answers a wrong id with zero reviews, which is reported as no_reviews (free). A URL or a name that Booking cannot find is reported as error (also free).
  • Filters are Booking's. Language codes must be the ones Booking uses (the hotel row lists them with counts); a code Booking rejects fails that property with a clear message rather than silently harvesting everything.
  • No room-level or topic filter. Booking's endpoint accepts language, traveller type and keyword; score and topic filters are applied by this actor after fetching (maxRating reads ascending so it rarely over-fetches).
  • Pace. Booking answered ten calls a second from one IP in testing; the default of one call per second per session is conservative. Each call returns 25 reviews.

FAQ

Does it need a Booking.com API key or login?

No. There is no public Booking.com reviews API for reading reviews; the actor uses the request Booking's own review list makes, which needs no key, no cookies and no browser.

Can I download Booking.com reviews as CSV or Excel?

Yes. Every run writes an Apify dataset; open the Reviews or Complaints view and export booking.com reviews csv, Excel, JSON or XML, or fetch the same through the API with ?format=csv.

How do I get only Booking.com negative reviews?

Set maxRating to 6 (Booking's own wording: 5 is "Poor", 7 is "Good"). The property is read lowest score first and paging stops at the threshold, so you pay only for the complaints. Add requireText: true to skip score-only ratings.

Do I need the hotel id, or can I use the URL?

Either — or the name. URLs and names are resolved through Booking's autocomplete and the matched name comes back in every row; ids skip the lookup. Once you have a hotel_id from a run, use it for the fastest repeat runs.

How many reviews can one hotel return?

All of them. Booking pages reviews 25 at a time and the actor keeps going until the property is exhausted or maxReviewsPerHotel is reached. A large city hotel with 15,000 reviews is 600 calls.

Are reviews translated, and which languages come back?

Reviews come back as written, with language set — multi language hotel reviews from one property, or booking.com reviews by language when you set languages. The hotel row lists every language the property's reviews come in, with counts, so you can pick what to fetch with languages. Booking's labels (traveller type, country names) follow locale.

Does it include the hotel's replies?

Yes — response carries the property's public reply when there is one.

What is traveler_type_code?

Booking's own guest category on the booking behind the review: COUPLES, FAMILIES, GROUP_OF_FRIENDS, SOLO_TRAVELLERS or BUSINESS_TRAVELLERS. traveler_type is the same thing in the site language.

Can I scrape Booking.com reviews without an API?

That is what this is — booking.com reviews without api keys: scrape booking.com reviews by URL, name or id, no key, no browser, pay per review.

Can I use it for hotel sentiment analysis?

Yes. positives and negatives are already split, text joins them for a single column, every row is tagged with language, rating and traveler_type, and requireText keeps the corpus clean.

What does bulk work cost?

Reviews only. A bulk hotel reviews pull of one hundred properties at 200 reviews each is at most 20,000 review rows; with requireText on it is typically 12,000, because score-only reviews are free.

How to use Booking.com Reviews Scraper

Ready-made runs you can open and start

Each one is a saved input on the Apify Store — open it, press Start, and it runs with the fields already filled in. All 163 across the suite.

Ready-made runWhat it does
Booking.com Negative Reviews for Any HotelBooking.com reviews scored six or below with a written complaint, lowest first: what goes wrong at one Singapore property and how often.
Download Booking.com Reviews to CSVThe hundred newest Booking.com reviews of one Berlin hotel with score, title, liked and disliked text, room type and stay dates, exportable as CSV or JSON.
Booking.com Reviews by Hotel IDFour properties by Booking.com hotel id in one run, twenty-five newest reviews each - no lookup step; the ids come from any earlier run's hotel_id column.
Booking.com Reviews by Language: German OnlyOnly German-language reviews of two Vienna and Munich grand hotels, filtered by Booking.com itself so reviews in other languages are never billed.
Booking.com Competitor Reviews: Las Vegas StripThree Las Vegas Strip resorts compared on Booking.com: seven category scores and review counts per property in one table, thirty newest reviews each.
Hotel Guest Reviews from Business TravellersOnly business travellers' Booking.com reviews of two Frankfurt airport hotels, filtered by Booking itself, forty per property, leisure stays never billed.
Hotel Review Data by Keyword: Noise ComplaintsOnly Booking.com reviews that mention noise at one Barcelona hotel, searched server-side so nothing else is fetched or billed - swap in any keyword.
Booking.com Guest Reviews from FamiliesEighty newest Booking.com reviews written by families at one Dubai resort, filtered by traveller type on Booking's side, with room type and stay dates.
Hotel Reputation Monitoring on Booking.comTwenty newest Booking.com reviews for each of four Berlin properties by hotel id - small enough to schedule daily and alert on when a score drops.
Booking.com Ratings: Highest-Scored Reviews FirstThe highest-scored Booking.com reviews with a written comment for one Tokyo hotel - quotable testimonials with reviewer country and stay dates attached.
Booking.com Reviews Without API AccessBooking.com has no public review API. This is the smallest replacement: one property URL, forty newest reviews, no key, no partner contract, no browser.
Hotel Sentiment Analysis Dataset from Booking.comNinety Booking.com reviews with the liked and disliked text already separated by the guest - a pre-labelled set for hotel sentiment analysis work.

Run Booking.com Reviews Scraper

The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/booking-reviews-scraper