Booking.com Scraper: Hotels, Prices & Reviews ($1/1k)
Pricing
from $1.00 / 1,000 hotels
Booking.com Scraper: Hotels, Prices & Reviews ($1/1k)
Scrape Booking.com hotels by destination, search URL or hotel URL: prices, ratings, address, facilities, photos and anonymous reviews. Accepts the input of voyager/booking-scraper. $1 per 1,000 hotels.
Pricing
from $1.00 / 1,000 hotels
Rating
0.0
(0)
Developer
Prague Data Lab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
0
Monthly active users
11 days ago
Last modified
Categories
Security
Limited permissionsShare
Booking.com Scraper
Get Booking.com hotels for a destination, or from search and hotel URLs: name, address, coordinates, price for your dates, rating with sub-scores, facilities, photos and anonymous guest reviews, one row per hotel. Revenue managers and travel startups use it to track prices and supply. $1 per 1,000 hotels.
Why this one
| Price per 1,000 results | |
|---|---|
| This Actor | $1.00 per hotel ($0.50 per review) |
| Popular alternatives (top 5 on the Store by users) | $1.75 – $5.00 |
The top five Actors for "booking scraper" list between $1.75 and $5 per 1,000 results. Hotel rows and review rows are priced separately.
Quick start
Run the Actor with this input. Every other field has a default.
{"search": "Prague","maxItems": 20,"currency": "EUR"}
Output example
One row, trimmed. The full field list is further down this page.
{"recordType": "hotel","name": "Hotel Residence Agnes","stars": 4,"price": 278.71,"currency": "EUR","nights": 2,"checkInDate": "2026-11-02","rating": 9.9,"ratingLabel": "Exceptional","reviews": 1277,"address": {"full": "Haštalská 943/19, Prague, 110 00, Czech Republic"}}
Use it from your code
The Actor id is praguedatalab/booking-scraper. Replace YOUR_APIFY_TOKEN with your Apify API token.
Python (pip install apify-client)
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("praguedatalab/booking-scraper").call(run_input={"search":"Prague","maxItems":20,"currency":"EUR"})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item)
JavaScript (npm install apify-client)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('praguedatalab/booking-scraper').call({"search":"Prague","maxItems":20,"currency":"EUR"});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
curl (returns the rows as JSON; the endpoint waits for the run, up to 5 minutes, so keep the limits small)
curl -X POST "https://api.apify.com/v2/acts/praguedatalab~booking-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"search":"Prague","maxItems":20,"currency":"EUR"}'
Use it with AI agents
AI assistants can call praguedatalab/booking-scraper through the Apify MCP server. One call returns a list of hotels with prices for the dates you give.
FAQ
Is it legal to scrape Booking.com?
The Actor reads public hotel and review pages without logging in. It never collects reviewer names, pictures or countries. Booking.com's terms still apply to how you use the data. This is not legal advice.
How much do 10,000 results cost?
$10 for 10,000 hotels, or $5 for 10,000 reviews. A run that finds nothing charges nothing. Set a maximum cost per run to cap the spend.
Do I need a proxy?
Keep the default proxy setting: Apify Proxy with the RESIDENTIAL group. Booking.com blocks some IP addresses. If a run stops with a "blocked this IP" message, it charges nothing.
How fresh is the data?
Each run reads Booking.com when it runs. Prices are for the check-in and check-out dates in the input and change often. price is null when nothing is available for those dates.
Can I export to Excel, CSV or Google Sheets?
Yes. Each run saves a dataset that you can download as JSON, CSV or Excel. For Google Sheets, import the CSV, or connect Apify to Google Sheets with an integration.
Can I schedule it daily?
Yes. In the Apify Console, open the Actor, go to Schedules, and add a daily schedule with the same input. Each run is billed per result, as usual.
Pricing
Pay per result, nothing else (no platform usage added on top, no start fee):
| Event | Price | You pay for |
|---|---|---|
hotel | $0.001 ($1 per 1,000) | one hotel row |
review | $0.0005 ($0.50 per 1,000) | one review row |
Set a maximum cost per run in the Apify run options and the Actor stops cleanly when it is reached. A run that is blocked or finds nothing charges nothing.
Compatible with the input of voyager/booking-scraper
Switching takes one change: the Actor ID. The input names and values are the same: search, startUrls, maxItems, propertyType, sortBy, minScore, starsCountFilter, currency, language, checkIn, checkOut (a date or 14 days), rooms, adults, children, minMaxPrice. These two are accepted so existing inputs validate, but are not offered, and the run says so in its status message: extractAdditionalHotelData (surroundings, room offerings) and flexWindow.
The output keeps the leader's field names where they exist: order, url, startUrlOrQuery, name, type, description, stars, price, currency, rating, ratingLabel, reviews, breakfast, checkIn, checkOut, checkInDate, checkOutDate, location {lat, lng}, address {full, street, postalCode, country, region}, image, images, rooms, facilities, categoryReviews, hotelId, hotelChain, timeOfScrapeISO.
Differences to know:
| Field | Here | Leader |
|---|---|---|
currency | ISO code (EUR) | symbol as Booking.com shows it (€) |
facilities | flat list of names | grouped by category |
categoryReviews | [{name, score, count}] | similar, different keys |
rooms | one entry per room offer with price, mealPlan, persons, bedType, features | similar |
| reviews | rows with recordType: "review" in the same dataset, no reviewer identity | separate Actor, with reviewer details |
| not provided | highlights, policies, roomImages, reviewSummary, licenseInfo, hostInfo, traderInfo, breadcrumbs, surroundings, roomOfferings, minimumNights | provided |
Output
Hotel rows and review rows share one dataset (recordType tells them apart, and is the charged event). Every field of both is listed here and in the dataset schema; null means Booking.com did not show the value.
| Field | Type | Row | Meaning |
|---|---|---|---|
recordType | string | both | hotel or review. Also the charged event name. |
order | integer | both | hotel: 1-based position in the search results that produced it (null for hotel URLs). review: position of the review among those saved for the hotel (newest first). |
url | string | hotel | Booking.com hotel page with the stay dates, guests and currency of the run. |
startUrlOrQuery | string | hotel | destination or start URL that led to this hotel. |
hotelId | integer | both | Booking.com property id (hotel and review rows). |
name | string | hotel | property name. |
type | string | hotel | property type (hotel, apartment, aparthotel, hostel, guest_house, bed_and_breakfast, villa…). |
description | string | hotel | property description from the hotel page (null when the hotel page was not opened). |
stars | number | hotel | star classification 1-5, null when unrated. |
price | number | hotel | lowest price for the whole stay (all nights, all rooms asked for) in currency; null when sold out for the dates. |
pricePerNight | number | hotel | average price per night in currency. |
currency | string | hotel | ISO 4217 code of price (the leader returns a symbol such as US$). |
rating | number | both | hotel: guest rating on a 0-10 scale. review: this review's score (1-10). |
ratingLabel | string | hotel | Booking.com's word for the rating (Exceptional, Superb, Fabulous, Very good, Good…); only when the hotel came from a search. |
reviews | integer | hotel | number of guest reviews. |
categoryReviews | array | hotel | review sub-scores (Staff, Facilities, Cleanliness, Comfort, Value for money, Location…) as {name, score, count}. |
breakfast | string | hotel | breakfast styles offered, comma-separated. |
checkIn | string | hotel | check-in time window, e.g. "From 15:00 to 22:00". |
checkOut | string | hotel | check-out time window, e.g. "Until 11:00". |
checkInDate | string | hotel | arrival date of the priced stay, YYYY-MM-DD. |
checkOutDate | string | hotel | departure date of the priced stay, YYYY-MM-DD. |
nights | integer | both | hotel: nights of the priced stay. review: nights the reviewer stayed. |
location | object | hotel | coordinates as {lat, lng}. |
address | object | hotel | address as {full, street, postalCode, city, region, country, countryCode}. |
distanceFromCenter | string | hotel | distance from the city centre as Booking.com words it (search results only). |
image | string | hotel | main photo URL. |
images | array | hotel | gallery photo URLs (up to 100, 1024x768). |
rooms | array | hotel | room offers for the stay as {roomId, roomType, bedType, persons, price, currency, mealPlan, available, features}; empty without availability. |
facilities | array | hotel | every facility listed on the page, as a flat list of names. |
popularFacilities | array | hotel | the "most popular facilities" shown on the hotel page. |
hotelChain | string | hotel | brand or chain. |
languagesSpoken | array | hotel | language codes spoken at the property. |
freeCancellation | boolean | hotel | the cheapest offer in the search results can be cancelled for free (search results only). |
isSponsored | boolean | hotel | the search result was marked as an ad. |
finePrint | string | hotel | the fine print of the property. |
timeOfScrapeISO | string | hotel | scrape time, ISO 8601 UTC. |
hotelName | string | review | name of the reviewed property. |
hotelUrl | string | review | hotel page URL. |
reviewId | string | review | Booking.com's opaque review id. |
date | string | review | date the review was written, YYYY-MM-DD. |
title | string | review | review headline. |
positiveText | string | review | what the guest liked. |
negativeText | string | review | what the guest disliked. |
language | string | review | language code of the text. |
travelerType | string | review | couple, family, group of friends, solo traveller or business traveller. |
stayStatus | string | review | stayed or similar status. |
stayCheckIn | string | review | check-in date of the reviewed stay. |
stayCheckOut | string | review | check-out date of the reviewed stay. |
roomType | string | review | room the guest booked. |
helpfulVotes | integer | review | helpful votes. |
hotelReply | string | review | reply posted by the property. |
Input
| Field | Meaning |
|---|---|
search | Destination, e.g. Prague. |
startUrls | Booking.com search URLs (/searchresults…, their filters are kept) or hotel URLs (/hotel/cz/name.html). Dates, guests, currency and language of the input are applied on top. |
maxItems | Hotels per destination or per search URL, at most 1,000 (higher values are capped). Default 20. |
checkIn, checkOut | YYYY-MM-DD or relative (14 days, 2 weeks). Empty: next Friday, two nights. Prices and rooms need dates. |
adults, children, rooms | Guests for the price. |
currency, language | Currency (ISO code) and site language (en-gb, de, fr…). |
propertyType, starsCountFilter, minScore, minMaxPrice, sortBy | Filters and order, as on Booking.com. minScore is applied exactly on the results (Booking.com itself only filters at 6, 7, 8 and 9). |
extractDetails | Open every hotel page for description, facilities, photos, rooms and the full address (default on). Off gives a fast list from the search results only. |
maxReviewsPerHotel | Anonymous reviews per hotel, newest first. 0 = none. |
proxyConfiguration | Optional, see below. |
How it works
Booking.com protects its pages with an AWS WAF JavaScript challenge. The Actor opens a real Chrome once, lets the challenge solve itself, keeps the aws-waf-token cookie, and then fetches everything else with a plain HTTP client that looks like Chrome: search pages, the next pages of a search (Booking.com's own GraphQL calls), hotel pages and reviews. When Booking.com asks for the challenge again, the Actor earns a new token. This is why the Actor runs with 2 GB of memory (the browser needs it for a few seconds) while the HTTP part is light.
Booking.com also answers some requests with a search page that holds no results at all (an empty "Redirect" answer, or a redirect to the search page without its query). The Actor never takes such a page for an empty destination: it asks again with the token cookie alone, then loads the search page in the browser itself, and only then gives up on that search. Every empty answer is logged in full (final URL, status, redirect chain, size, the verdict Booking.com wrote in the page, the first 500 characters) and the page is saved as SEARCH_PAGE_<n>.html in the run's key-value store, next to the SUMMARY record that lists all of them under searchDiagnostics.
Only public pages are used: no login, no captcha solving.
Proxy advice
Start without a proxy. Booking.com decides per IP address, so a run on a flagged address can be refused even though the same Actor works elsewhere. When that happens the run stops with the status message "Booking.com blocked this IP. Enable Apify Proxy with the RESIDENTIAL group…" and charges nothing. A search that comes back without results in every way the Actor can ask ends with the note "Booking.com returned no results for this search from this IP (…)", which names each attempt and what Booking.com answered; the same proxy advice applies. Enable it in the Proxy input: { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }. The browser and the HTTP client share one proxy session, so the token stays valid. Residential traffic is billed by Apify per GB; a hotel page is roughly 0.5 MB compressed, so turn off extractDetails when you only need the list.
Limits
- Booking.com shows at most 1,000 results per search. Split bigger destinations with filters (property type, stars, price ranges) or several searches.
- Reviews come 25 per request, newest first. The text is in the guest's language (
languagefield); Booking.com's translation is not fetched. priceis the lowest price for the whole stay and isnullwhen nothing is available for the dates;roomsis empty then.- Search ads (
isSponsored: true) are kept and flagged. A hotel that several searches of one run find is saved once. - Reviewer names, pictures and countries are never collected.
- The run keeps a progress record, so when Apify restarts a long run on another server it does not save or charge the same hotel twice (reviews of the hotel being processed at that moment can be incomplete).
Use cases
- Revenue managers and hotel groups: track competitor prices and ratings per night, per destination.
- Travel startups and aggregators: fill a destination database with coordinates, photos, facilities and live prices.
- Market research: map supply by property type and stars, compare rating distributions across cities.
- Reputation monitoring: pull the newest anonymous reviews of your own hotels and your competitors'.
- AI agents: one call returns a structured list of hotels with prices for given dates.
Terms and data
The Actor reads public Booking.com pages. Hotel names, addresses, prices and facilities are business data; review rows carry no reviewer identity. Check Booking.com's terms and the rules that apply to your use before running it at volume.