Booking.com Hotel Listings & Reviews Scraper
Pricing
Pay per event
Booking.com Hotel Listings & Reviews Scraper
Scrape Booking.com hotel search results and detail pages by destination or direct URL: name, address, coordinates, star rating, review score, price, room type, and optional guest reviews. Camoufox-powered to clear the AWS WAF challenge. Pay only per hotel scraped.
Pricing
Pay per event
Rating
0.0
(0)
Developer
DevilScrapes
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
🎯 What this scrapes
For each run, this Actor works in one of two modes:
- Search mode — pass a
destination(plus check-in/check-out dates), and it opens Booking.com's search results for that window, capped at yourmaxHotelsPerSearch. - Direct-URL mode — pass a list of
hotelUrls, and it visits each hotel's own detail page directly, independent of a destination search.
For every hotel it visits, the Actor emits one dataset row with name, address, city, country, coordinates, star rating, review score, review count, price, currency, room type, and thumbnail. When scrapeReviews is enabled, it also attaches up to maxReviewsPerHotel best-effort guest reviews per hotel.
🔥 Features
- We clear the JS-execution gate Booking.com puts in front of raw HTTP requests — a hardened browser layer renders the page like a real visitor instead of bouncing off an automated bot-control challenge screen.
- We rotate proxy sessions on every block — a fresh exit IP and session id whenever a challenge or mismatch is detected.
- We verify the currency and locale actually match what you asked for before trusting a price — a wrong-geo page never gets emitted as if it were correct.
- We back off when the target pushes back. Partial successes surface with a clear status message — we never silently return an empty dataset.
- Per-hotel fault isolation — one dead page, timeout, or parse error skips that hotel with a logged warning; it never fails the whole run.
- Pydantic v2 input + output validation — every row is a typed, schema-enforced record; nullable fields are always
null, never silently dropped. - Pay-Per-Event pricing — you're charged per hotel row written to your dataset, plus a small per-review charge only when you opt into reviews.
💡 Use cases
- OTA price intelligence — track how a set of hotels prices a given date range across markets.
- Revenue-management benchmarking — hotel revenue managers pull comparable listings to sanity-check their own pricing and positioning.
- Review-signal analysis — pull guest review scores and text at scale for sentiment or competitive analysis.
- Travel-agtech and itinerary tools — bulk-source structured hotel data (name, coordinates, star rating, price) to power a booking or comparison product.
⚙️ How to use it
- Click Try for free at the top of the page.
- Enter a
destinationwithcheckin/checkoutdates (search mode), or paste one or morehotelUrls(direct-URL mode) — or both. - Optionally enable
scrapeReviewsto pull guest reviews alongside each hotel. - Click Start. Output streams into the run's dataset.
- Export from Storage → Dataset as JSON, CSV, or Excel — or fetch via the API.
📥 Input
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
destination | string | no* | — | Free-text destination for search mode, e.g. "Paris, France". |
checkin | string | required if destination set | — | Check-in date (YYYY-MM-DD). |
checkout | string | required if destination set | — | Check-out date (YYYY-MM-DD), must be after checkin. |
adults | integer | no | 2 | 1-30 adults per room. |
rooms | integer | no | 1 | 1-10 rooms. |
maxHotelsPerSearch | integer | no | 20 | 1-200 hotels returned per search. |
hotelUrls | array | no* | [] | Direct hotel-detail URLs; alongside or instead of destination. |
scrapeReviews | boolean | no | false | Also fetch guest reviews per hotel. |
maxReviewsPerHotel | integer | no | 20 | 1-200 reviews per hotel when scrapeReviews is on. |
countryCode | string | no | "US" | 2-letter ISO country code — proxy geo pin. |
currency | string | no | "USD" | 3-letter ISO 4217 currency code. |
sortOrder | string | no | — | Optional search sort: popularity, price, review_score, class. |
proxyConfiguration | object | no | unset | Apify Proxy editor; not required to be RESIDENTIAL. |
* At least one of destination or hotelUrls must be set.
Example input
{"destination": "Paris, France","checkin": "2026-09-10","checkout": "2026-09-12","adults": 2,"rooms": 1,"maxHotelsPerSearch": 20,"scrapeReviews": false,"countryCode": "US","currency": "USD"}
📤 Output
Every row is one dataset item — one Booking.com hotel.
| Field | Type | Notes |
|---|---|---|
hotel_name | string | Hotel name. |
booking_url | string | Canonical hotel detail URL. |
address | string | null | Street address. |
city | string | null | City. |
country | string | null | Country. |
latitude | float | null | Latitude, when available. |
longitude | float | null | Longitude, when available. |
star_rating | float | null | Official star rating. |
review_score | float | null | Booking.com's own review score (0-10 scale). |
review_count | integer | null | Number of reviews behind the score. |
price | float | null | Price in the requested currency, numeric. |
currency | string | Echoes the requested currency. |
room_type | string | null | Room type. |
thumbnail_url | string | null | Thumbnail image URL. |
checkin / checkout | string | null | Echoes the requested dates. |
reviews | array | null | Guest reviews, only when scrapeReviews=true; best-effort. |
Example output
{"hotel_name": "Hôtel Eiffel Trocadéro","booking_url": "https://www.booking.com/hotel/fr/eiffel-trocadero.html","address": "35 Rue Benjamin Franklin, 75116 Paris","city": "Paris","country": "France","latitude": 48.8635,"longitude": 2.287,"star_rating": 4.0,"review_score": 8.6,"review_count": 1204,"price": 189.0,"currency": "USD","room_type": "Superior Double Room","thumbnail_url": "https://cf.bstatic.com/xdata/images/hotel/square200/....jpg","checkin": "2026-09-10","checkout": "2026-09-12","reviews": null}
💰 Pricing
Pay-Per-Event — you pay only when these events fire:
| Event | USD | What it is |
|---|---|---|
actor-start | $0.02 | One-off warm-up charge per run. |
hotel-scraped | $0.0020 | Per hotel row written to your dataset. |
reviews-scraped | $0.0005 | Per review item emitted, only when scrapeReviews=true. |
Example: 1 000 hotel rows at the rates above ≈ $2.00. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.
🚧 Limitations
Scaffold-stage build — hotels-only (no Booking.com Attractions, car rentals, or flights), one-shot scrape per run (no price-trend history), and read-only extraction (no booking/reservation actions). Review pagination shape may evolve as recon continues; reviews stay best-effort and never block a hotel's core row.
❓ FAQ
Is this legal?
We only fetch content Booking.com makes publicly available on its search and hotel pages. Respect Booking.com's terms of service before using output commercially.
Why is a field null?
Detail-page fields are null when Booking.com doesn't expose that field publicly for a given hotel, or when a currency/locale mismatch caused the Actor to skip trusting that page's price.
Do I need my own proxy?
No — a proxy is wired in by default; proxyConfiguration is optional if you want to bring your own.
Why did I get fewer rows than maxHotelsPerSearch?
Any hotel that hits a dead page, a challenge that doesn't clear, or a currency/locale mismatch is skipped rather than failing the run — check the run's status message for a breakdown.
💬 Your feedback
Spotted a bug, hit a weird edge case, or need a new field? Open an issue on the Actor's Issues tab on Apify Console — we ship fixes weekly and we read every report.