Scrape Booking.com Hotel Listings
Pull hotel names, prices, ratings, and locations from Booking.com search results.
- A Browserless API token from your account dashboard
Steps
Booking.com runs aggressive bot detection, including fingerprint checks, CAPTCHAs, and dynamic content that loads differently for automated requests. The examples below navigate directly to a pre-formed search URL and extract hotel listing data. All three tabs route through stealth mode and a residential proxy, which is what makes them work where plain requests don't.
Booking.com updates its markup regularly and may show a currency or location modal on first load. If selectors return empty results, check whether an overlay modal appeared and update the CSS selectors to match the current DOM structure.
- REST API
- Frameworks
- BQL
Send the BQL mutation over HTTP to the stealth endpoint. No browser library or BQL IDE required.
- cURL
- JavaScript
- Python
- Java
- C#
1. Send the request
curl -X POST \
"https://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&proxy=residential&proxyCountry=us" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation ScrapeBooking { goto(url: \"https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2\", waitUntil: networkIdle) { status } hotels: mapSelector(selector: \"[data-testid='\''property-card'\'']\") { name: mapSelector(selector: \"[data-testid='\''title'\'']\") { innerText } price: mapSelector(selector: \"[data-testid='\''price-and-discounted-price'\'']\") { innerText } rating: mapSelector(selector: \"[data-testid='\''review-score'\'']\") { innerText } location: mapSelector(selector: \"[data-testid='\''address'\'']\") { innerText } } }",
"variables": {}
}'
2. Check the output
{
"data": {
"goto": { "status": 200 },
"hotels": [
{
"name": [{ "innerText": "The Manhattan Hotel" }],
"price": [{ "innerText": "$189" }],
"rating": [{ "innerText": "8.5" }],
"location": [{ "innerText": "Midtown Manhattan, New York" }]
},
{
"name": [{ "innerText": "Brooklyn Heights Suites" }],
"price": [{ "innerText": "$142" }],
"rating": [{ "innerText": "7.9" }],
"location": [{ "innerText": "Brooklyn Heights, New York" }]
}
]
}
}
1. Send the request
const query = `mutation ScrapeBooking {
goto(
url: "https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2"
waitUntil: networkIdle
) {
status
}
hotels: mapSelector(selector: "[data-testid='property-card']") {
name: mapSelector(selector: "[data-testid='title']") {
innerText
}
price: mapSelector(selector: "[data-testid='price-and-discounted-price']") {
innerText
}
rating: mapSelector(selector: "[data-testid='review-score']") {
innerText
}
location: mapSelector(selector: "[data-testid='address']") {
innerText
}
}
}`;
const response = await fetch(
'https://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&proxy=residential&proxyCountry=us',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables: {} }),
}
);
const { data } = await response.json();
const hotels = data.hotels.map((hotel) => ({
name: hotel.name?.[0]?.innerText ?? '',
price: hotel.price?.[0]?.innerText ?? '',
rating: hotel.rating?.[0]?.innerText ?? '',
location: hotel.location?.[0]?.innerText ?? '',
}));
console.log(JSON.stringify(hotels, null, 2));
2. Check the output
{
"data": {
"goto": { "status": 200 },
"hotels": [
{
"name": [{ "innerText": "The Manhattan Hotel" }],
"price": [{ "innerText": "$189" }],
"rating": [{ "innerText": "8.5" }],
"location": [{ "innerText": "Midtown Manhattan, New York" }]
},
{
"name": [{ "innerText": "Brooklyn Heights Suites" }],
"price": [{ "innerText": "$142" }],
"rating": [{ "innerText": "7.9" }],
"location": [{ "innerText": "Brooklyn Heights, New York" }]
}
]
}
}
1. Install dependencies
pip install requests
2. Send the request
import requests
query = """
mutation ScrapeBooking {
goto(
url: "https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2"
waitUntil: networkIdle
) {
status
}
hotels: mapSelector(selector: "[data-testid='property-card']") {
name: mapSelector(selector: "[data-testid='title']") {
innerText
}
price: mapSelector(selector: "[data-testid='price-and-discounted-price']") {
innerText
}
rating: mapSelector(selector: "[data-testid='review-score']") {
innerText
}
location: mapSelector(selector: "[data-testid='address']") {
innerText
}
}
}
"""
response = requests.post(
'https://production-sfo.browserless.io/stealth/bql',
params={
'token': 'YOUR_API_TOKEN_HERE',
'proxy': 'residential',
'proxyCountry': 'us',
},
json={'query': query, 'variables': {}},
)
data = response.json()['data']
for hotel in data['hotels']:
name = hotel['name'][0]['innerText'] if hotel['name'] else ''
price = hotel['price'][0]['innerText'] if hotel['price'] else ''
rating = hotel['rating'][0]['innerText'] if hotel['rating'] else ''
location = hotel['location'][0]['innerText'] if hotel['location'] else ''
print(f'{name} — {price} — {rating} — {location}')
3. Check the output
{
"data": {
"goto": { "status": 200 },
"hotels": [
{
"name": [{ "innerText": "The Manhattan Hotel" }],
"price": [{ "innerText": "$189" }],
"rating": [{ "innerText": "8.5" }],
"location": [{ "innerText": "Midtown Manhattan, New York" }]
},
{
"name": [{ "innerText": "Brooklyn Heights Suites" }],
"price": [{ "innerText": "$142" }],
"rating": [{ "innerText": "7.9" }],
"location": [{ "innerText": "Brooklyn Heights, New York" }]
}
]
}
}
1. Send the request
import java.net.URI;
import java.net.http.*;
String token = "YOUR_API_TOKEN_HERE";
String endpoint = "https://production-sfo.browserless.io/stealth/bql?token=" + token
+ "&proxy=residential&proxyCountry=us";
String query = "mutation ScrapeBooking {"
+ " goto(url: \\\"https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2\\\","
+ " waitUntil: networkIdle) { status }"
+ " hotels: mapSelector(selector: \\\"[data-testid='property-card']\\\") {"
+ " name: mapSelector(selector: \\\"[data-testid='title']\\\") { innerText }"
+ " price: mapSelector(selector: \\\"[data-testid='price-and-discounted-price']\\\") { innerText }"
+ " rating: mapSelector(selector: \\\"[data-testid='review-score']\\\") { innerText }"
+ " location: mapSelector(selector: \\\"[data-testid='address']\\\") { innerText }"
+ " }"
+ " }";
String payload = "{\"query\": \"" + query + "\", \"variables\": {}}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
2. Check the output
{
"data": {
"goto": { "status": 200 },
"hotels": [
{
"name": [{ "innerText": "The Manhattan Hotel" }],
"price": [{ "innerText": "$189" }],
"rating": [{ "innerText": "8.5" }],
"location": [{ "innerText": "Midtown Manhattan, New York" }]
},
{
"name": [{ "innerText": "Brooklyn Heights Suites" }],
"price": [{ "innerText": "$142" }],
"rating": [{ "innerText": "7.9" }],
"location": [{ "innerText": "Brooklyn Heights, New York" }]
}
]
}
}
1. Send the request
using System.Net.Http;
using System.Text;
using System.Text.Json;
string token = "YOUR_API_TOKEN_HERE";
string endpoint = $"https://production-sfo.browserless.io/stealth/bql?token={token}&proxy=residential&proxyCountry=us";
var payload = new
{
query = @"mutation ScrapeBooking {
goto(
url: ""https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2""
waitUntil: networkIdle
) { status }
hotels: mapSelector(selector: ""[data-testid='property-card']"") {
name: mapSelector(selector: ""[data-testid='title']"") { innerText }
price: mapSelector(selector: ""[data-testid='price-and-discounted-price']"") { innerText }
rating: mapSelector(selector: ""[data-testid='review-score']"") { innerText }
location: mapSelector(selector: ""[data-testid='address']"") { innerText }
}
}",
variables = new { },
};
using (HttpClient httpClient = new HttpClient())
{
var content = new StringContent(
JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(endpoint, content);
string body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
2. Check the output
{
"data": {
"goto": { "status": 200 },
"hotels": [
{
"name": [{ "innerText": "The Manhattan Hotel" }],
"price": [{ "innerText": "$189" }],
"rating": [{ "innerText": "8.5" }],
"location": [{ "innerText": "Midtown Manhattan, New York" }]
},
{
"name": [{ "innerText": "Brooklyn Heights Suites" }],
"price": [{ "innerText": "$142" }],
"rating": [{ "innerText": "7.9" }],
"location": [{ "innerText": "Brooklyn Heights, New York" }]
}
]
}
}
Connect through stealth mode and a residential proxy so Booking.com sees a real-looking browser session, then extract hotel data from the rendered listings.
- Puppeteer
- Playwright
1. Install dependencies
npm install puppeteer-core
2. Connect and scrape
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.connect({
browserWSEndpoint:
'wss://production-sfo.browserless.io/stealth?token=YOUR_API_TOKEN_HERE&proxy=residential&proxyCountry=us',
});
try {
const page = await browser.newPage();
await page.goto(
'https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2',
{ waitUntil: 'networkidle2' }
);
// Wait for property cards to confirm the listings have rendered.
await page.waitForSelector('[data-testid="property-card"]');
const hotels = await page.evaluate(() =>
Array.from(document.querySelectorAll('[data-testid="property-card"]')).map((card) => ({
name: card.querySelector('[data-testid="title"]')?.innerText?.trim() ?? null,
price: card.querySelector('[data-testid="price-and-discounted-price"]')?.innerText?.trim() ?? null,
rating: card.querySelector('[data-testid="review-score"]')?.innerText?.trim() ?? null,
location: card.querySelector('[data-testid="address"]')?.innerText?.trim() ?? null,
}))
);
console.log(JSON.stringify(hotels, null, 2));
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
Run with node scrape-booking.mjs. null values mean the selector wasn't matched. Increase the waitForSelector timeout or check whether a modal intercepted the page load.
[
{
"name": "The Manhattan Hotel",
"price": "$189",
"rating": "8.5",
"location": "Midtown Manhattan, New York"
},
{
"name": "Brooklyn Heights Suites",
"price": "$142",
"rating": "7.9",
"location": "Brooklyn Heights, New York"
}
]
1. Install dependencies
npm install playwright-core
2. Connect and scrape
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP(
'wss://production-sfo.browserless.io?token=YOUR_API_TOKEN_HERE&stealth&proxy=residential&proxyCountry=us'
);
try {
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto(
'https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2',
{ waitUntil: 'networkidle' }
);
// Wait for property cards to confirm the listings have rendered.
await page.waitForSelector('[data-testid="property-card"]');
const hotels = await page.evaluate(() =>
Array.from(document.querySelectorAll('[data-testid="property-card"]')).map((card) => ({
name: card.querySelector('[data-testid="title"]')?.innerText?.trim() ?? null,
price: card.querySelector('[data-testid="price-and-discounted-price"]')?.innerText?.trim() ?? null,
rating: card.querySelector('[data-testid="review-score"]')?.innerText?.trim() ?? null,
location: card.querySelector('[data-testid="address"]')?.innerText?.trim() ?? null,
}))
);
console.log(JSON.stringify(hotels, null, 2));
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
Run with node scrape-booking.mjs. null values mean the selector wasn't matched. Increase the waitForSelector timeout or check whether a modal intercepted the page load.
[
{
"name": "The Manhattan Hotel",
"price": "$189",
"rating": "8.5",
"location": "Midtown Manhattan, New York"
},
{
"name": "Brooklyn Heights Suites",
"price": "$142",
"rating": "7.9",
"location": "Brooklyn Heights, New York"
}
]
1. Write the mutation
Navigate to the search results URL and use nested mapSelector calls to pull name, price, rating, and location from each property card. We send this to /stealth/bql instead of the default /bql because Booking.com's bot detection blocks plain browser sessions before listings load.
mutation ScrapeBooking {
goto(
url: "https://www.booking.com/searchresults.html?ss=New+York&checkin=2026-09-01&checkout=2026-09-02&group_adults=2"
waitUntil: networkIdle
) {
status
}
hotels: mapSelector(selector: "[data-testid='property-card']") {
name: mapSelector(selector: "[data-testid='title']") {
innerText
}
price: mapSelector(selector: "[data-testid='price-and-discounted-price']") {
innerText
}
rating: mapSelector(selector: "[data-testid='review-score']") {
innerText
}
location: mapSelector(selector: "[data-testid='address']") {
innerText
}
}
}
2. Run it
Paste into the BQL IDE and click Run.
3. Check the output
{
"data": {
"goto": { "status": 200 },
"hotels": [
{
"name": [{ "innerText": "The Manhattan Hotel" }],
"price": [{ "innerText": "$189" }],
"rating": [{ "innerText": "8.5" }],
"location": [{ "innerText": "Midtown Manhattan, New York" }]
},
{
"name": [{ "innerText": "Brooklyn Heights Suites" }],
"price": [{ "innerText": "$142" }],
"rating": [{ "innerText": "7.9" }],
"location": [{ "innerText": "Brooklyn Heights, New York" }]
}
]
}
}
Next steps
- Scrape Glassdoor Job Listings — another stealth-mode scrape against aggressive bot detection
- Scrape Walmart Product Listings — extract product data from a bot-protected retail site
- Solving Cloudflare Challenges — bypass Cloudflare's interstitial pages before scraping