Google Hotels API
Scrape Google Hotels listings and per-vendor pricing as structured JSON
The Google Hotels API returns structured property data from Google Hotels in two steps:
- Listing — search hotels and get property metadata (name, photos, GPS, ratings, amenities, per-night price) plus a
detail_tokenfor each property. - Detail — pass that
detail_tokento fetch the full per-vendor offer shelf (Booking.com,Hotels.com,Agoda, …) with liverate_per_nightandtotal_rate.
Credit Usage: Each successful request costs 10 credits. For bulk processing, use the Async API with plugins.
Key Features
- Two-step flow — listing call returns properties +
detail_token; detail call returns the vendor pricing shelf. - Structured JSON — each property has name, description, GPS, hotel class, rating, reviews, amenities, images, per-night price, total rate, check-in/check-out times, nearby places, and a star-by-star ratings histogram.
- Per-vendor pricing — the detail endpoint returns
booking_sources[]with sponsored ("Featured options") and organic ("All options") entries unified into one ranked list. - Currency localization — per-request
currency(USD,EUR,GBP,TRY, …). Combine withhlandglfor full regional search. - Sort & filter — lowest price / highest rating / most reviewed; price range, minimum rating, hotel class, amenities, free cancellation, eco-certified, special offers.
- Vacation rentals mode —
property_types=12switches to vacation rentals where available. - Pagination — token-based with
next_page_token. - No blocks or CAPTCHAs — handled automatically.
Listing Endpoint
GET https://api.scrape.do/plugin/google/hotelsBasic Example
curl --location --request GET 'https://api.scrape.do/plugin/google/hotels?token=<SDO-token>&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03'import requests
import json
token = "<SDO-token>"
url = f"https://api.scrape.do/plugin/google/hotels?token={token}&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03"
response = requests.request("GET", url)
print(json.dumps(response.json(), indent=2))const axios = require('axios');
const token = "<SDO-token>";
const url = `https://api.scrape.do/plugin/google/hotels?token=${token}&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03`;
axios.get(url)
.then(response => {
console.log(JSON.stringify(response.data, null, 2));
})
.catch(error => {
console.error(error);
});package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
token := "<SDO-token>"
url := fmt.Sprintf(
"https://api.scrape.do/plugin/google/hotels?token=%s&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03",
token,
)
resp, err := http.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}require 'net/http'
require 'json'
token = "<SDO-token>"
url = URI("https://api.scrape.do/plugin/google/hotels?token=#{token}&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03")
response = Net::HTTP.get(url)
puts JSON.pretty_generate(JSON.parse(response))import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class GoogleHotels {
public static void main(String[] args) throws Exception {
String token = "<SDO-token>";
String url = String.format(
"https://api.scrape.do/plugin/google/hotels?token=%s&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03",
token
);
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream())
);
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
System.out.println(response.toString());
}
}using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string token = "<SDO-token>";
string url = $"https://api.scrape.do/plugin/google/hotels?token={token}&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03";
using HttpClient client = new HttpClient();
string response = await client.GetStringAsync(url);
Console.WriteLine(response);
}
}<?php
$token = "<SDO-token>";
$url = "https://api.scrape.do/plugin/google/hotels?token={$token}&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo json_encode(json_decode($response), JSON_PRETTY_PRINT);
?>curl "https://api.scrape.do/plugin/google/hotels?token=$TOKEN&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03"Occupancy parameters are not supported. Results always reflect the default occupancy of 2 adults, 0 children.
Query Guidelines
Use simple location-based queries for best results:
| Recommended | Avoid |
|---|---|
Paris hotels | Paris luxury hotels near Eiffel Tower |
Tokyo hotels | Tokyo traditional ryokan hotels |
Edinburgh hotels | Edinburgh Royal Mile hotels |
Overly specific queries can return empty results. Simple "<City> hotels" queries have the highest success rate.
Request Parameters
Required
| Parameter | Type | Description |
|---|---|---|
token | string | Your Scrape.do API authentication token |
q | string | Hotel search query. Use simple "<City> hotels" form for best results |
check_in_date | string | Check-in date in YYYY-MM-DD |
check_out_date | string | Check-out date in YYYY-MM-DD. Must be after check_in_date |
Localization
| Parameter | Type | Default | Description |
|---|---|---|---|
hl | string | en | Language code |
gl | string | us | Country code |
currency | string | USD | Currency code (EUR, GBP, TRY, …) |
Sorting
| Parameter | Type | Default | Description |
|---|---|---|---|
sort_by | integer | 0 (relevance) | 3 = lowest price, 8 = highest rating, 13 = most reviewed |
Filters
| Parameter | Type | Description |
|---|---|---|
min_price | integer | Minimum nightly price (≥ 0) in the requested currency |
max_price | integer | Maximum nightly price (≥ 0 and ≥ min_price) in the requested currency |
rating | integer | Minimum rating: 7 (3.5+), 8 (4.0+), 9 (4.5+) |
hotel_class | string | Comma-separated star ratings (4,5 for 4★ and 5★). Each value 2–5 |
amenities | string | Comma-separated amenity codes (see Amenity Codes) |
property_types | string | Comma-separated property type codes. 12 = vacation rentals (switches mode) |
free_cancellation | boolean | Only properties with free cancellation. Accepts true or 1 |
eco_certified | boolean | Only eco-certified properties |
special_offers | boolean | Only properties with special offers |
Pagination & Page Size
| Parameter | Type | Default | Description |
|---|---|---|---|
next_page_token | string | — | From the previous response's pagination.next_page_token. Pass back unchanged |
limit | integer | 20 | Maximum properties to return on the page. Range: 1–20 |
More Examples
# Turkish locale + TRY currency
curl "https://api.scrape.do/plugin/google/hotels?q=Istanbul+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&hl=tr&gl=tr¤cy=TRY&token=$TOKEN"
# Cheapest first within £100–£300
curl "https://api.scrape.do/plugin/google/hotels?q=London+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&sort_by=3&min_price=100&max_price=300¤cy=GBP&token=$TOKEN"
# 4–5 star, free cancellation
curl "https://api.scrape.do/plugin/google/hotels?q=Dubai+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&hotel_class=4,5&free_cancellation=true&token=$TOKEN"
# Eco-certified, 4.5+ rating, with pool
curl "https://api.scrape.do/plugin/google/hotels?q=Maldives+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&rating=9&eco_certified=true&amenities=22&token=$TOKEN"
# Vacation rentals
curl "https://api.scrape.do/plugin/google/hotels?q=Santorini+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&property_types=12&token=$TOKEN"
# Page 2 (token from a previous response)
curl "https://api.scrape.do/plugin/google/hotels?q=Paris+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03&next_page_token=CgsI...&token=$TOKEN"Response
Top-Level Structure
{
"search_parameters": { ... },
"search_information": { "total_results": 18 },
"properties": [ ... ],
"ads": [ ... ],
"brands": [ ... ],
"pagination": { ... }
}properties[]
{
"type": "hotel",
"name": "The Anvaya Beach Resort Bali",
"description": "Sophisticated resort offering 8 pools, 2 restaurants & a spa, plus a private beach & a kids' club.",
"gps_coordinates": { "latitude": -8.7322535, "longitude": 115.1659877 },
"hotel_class": "5-star hotel",
"extracted_hotel_class": 5,
"overall_rating": 4.7,
"reviews": 14342,
"amenities": [6, 29, 16, 22, 2, 8, 26, 5, 4, 23, 24, 14, 1, 31, 27, 7, 11],
"images": [
{ "thumbnail": "https://lh3.googleusercontent.com/...", "original_image": "https://lh3.googleusercontent.com/..." }
],
"detail_token": "Q2hrSXZfSHlpTktIcGY2eUFSb01MMmN2TVhvemRHSm5aM0J6RUFFfDB4MTRiZTZkN2IxYjkwM2VjMTowYzZkMjFhMzRiMDgwNGNkZQ",
"check_in_time": "3:00 PM",
"check_out_time": "12:00 PM",
"ratings": [
{ "stars": 5, "percent": 91, "count": 4546 },
{ "stars": 4, "percent": 7, "count": 363 },
{ "stars": 3, "percent": 1, "count": 96 }
],
"reviews_breakdown": [
{ "name": "Cleanliness", "sentiment": 0.42, "total_mentioned": 88, "positive": 71, "negative": 8, "neutral": 9 }
],
"price": { "display": "USD 187", "amount": 187.42, "currency": "USD" },
"total_rate": { "display": "USD 374", "amount": 374, "currency": "USD" },
"nearby_places": [
{ "name": "Beach", "transportations": [{ "type": 0, "duration": "10 min" }] }
]
}| Field | Type | Always present | Description |
|---|---|---|---|
type | string | yes | "hotel" or "vacation rental" |
name | string | yes | Property name |
description | string | Short description | |
gps_coordinates | object | yes | { latitude, longitude } |
hotel_class | string | Star rating label (e.g. "5-star hotel") | |
extracted_hotel_class | int | Numeric star rating | |
overall_rating | float | Guest rating out of 5 | |
reviews | int | Number of guest reviews | |
amenities | int[] | yes | Amenity codes (see Amenity Codes). Empty array when none |
images | object[] | yes | [{ thumbnail, original_image }]. Empty array when none |
detail_token | string | yes | Opaque token. Pass to the detail endpoint to fetch per-vendor pricing |
check_in_time | string | Property check-in clock time (e.g. "3:00 PM") | |
check_out_time | string | Property check-out clock time | |
ratings | object[] | 5→1 star histogram entries: { stars, percent, count } | |
reviews_breakdown | object[] | Guest sentiment by category (Cleanliness, Service, …) | |
price | object | Per-night rate in the requested currency. See Price | |
total_rate | object | Total stay cost across the date range. See Price | |
nearby_places | object[] | Points of interest with transit-mode durations |
Price
| Field | Type | Description |
|---|---|---|
display | string | Formatted price as returned (e.g., "USD 187", "TRY 1,895") |
amount | float | Numeric value (e.g., 187.42) |
currency | string | ISO 4217 code reflecting the request's currency |
ads[]
Sponsored hotel results attached to the listing, when present. Each entry is flagged with "sponsored": true.
{
"sponsored": true,
"name": "The LINQ - A Caesars Rewards Destination",
"source": "Booking.com",
"source_icon": "//www.gstatic.com/travel-hotels/branding/icon_184.png",
"click": "/aclk?sa=l&ai=...",
"gps_coordinates": { "latitude": 36.1206, "longitude": -115.1717 },
"hotel_class": 4,
"thumbnail": "//lh3.googleusercontent.com/proxy/...",
"reviews": 42322,
"amenities": [11, 23, 4, 24, 8, 29, 2],
"price": { "display": "$291", "amount": 290.81, "currency": "USD" },
"total_rate": { "display": "$582", "amount": 582, "currency": "USD" }
}brands[]
Hotel chain / brand index attached to the listing, when present.
[
{ "id": 21, "name": "Ibis" },
{ "id": 61, "name": "Marriott Hotels & Resorts" }
]pagination
Included when more pages exist. Omitted on the last page.
{ "current_from": 1, "current_to": 20, "next_page_token": "CgsI..." }Pages 2+ may overlap by up to 2 entries — dedupe across pages by name + gps_coordinates. The detail_token is not stable across pages, so the same property may receive a different detail_token on each page.
Detail Endpoint
GET https://api.scrape.do/plugin/google/hotels/detailReturns the per-vendor offer shelf for a single property — each booking source's rate_per_night, total_rate, and quoted occupancy in the requested currency, with sponsored ("Featured options") and organic ("All options") entries unified into one ranked list.
Property metadata (name, photos, GPS, ratings, amenities) is not repeated in the detail response — it was already returned by the listing call. Merge client-side using the detail_token as the join key.
Two-Step Example
# Step 1 — listing
curl "https://api.scrape.do/plugin/google/hotels?token=$TOKEN&q=Bali+hotels&check_in_date=2026-05-01&check_out_date=2026-05-03¤cy=USD"
# → response.properties[i].detail_token
# Step 2 — detail (per-vendor pricing for one property)
curl "https://api.scrape.do/plugin/google/hotels/detail?token=$TOKEN&detail_token=Q2hrSXZfSHlpTktIcGY2eUFSb01MMmN2TVhvemRHSm5aM0J6RUFFfDB4MTRiZTZkN2IxYjkwM2VjMTowYzZkMjFhMzRiMDgwNGNkZQ&check_in_date=2026-05-01&check_out_date=2026-05-03¤cy=USD"Use the same check_in_date, check_out_date, and currency in both calls.
Detail Parameters
Required
| Parameter | Type | Description |
|---|---|---|
token | string | Your Scrape.do API token |
detail_token | string | From a listing response's properties[].detail_token. Pass it back unchanged |
check_in_date | string | YYYY-MM-DD |
check_out_date | string | YYYY-MM-DD. Must be after check_in_date |
Optional
| Parameter | Type | Default | Description |
|---|---|---|---|
currency | string | USD | ISO 4217 currency code |
gl | string | us | Country code |
hl | string | en | Language code |
Detail Response
{
"search_parameters": {
"engine": "google_hotels_detail",
"detail_token": "Q2hrSXZfSHlp...",
"check_in_date": "2026-05-01",
"check_out_date": "2026-05-03",
"currency": "USD",
"gl": "us",
"hl": "en"
},
"property": {
"detail_token": "Q2hrSXZfSHlp...",
"booking_sources": [
{
"sponsored": true,
"name": "Booking.com",
"id": 184,
"click": "/aclk?...",
"icon": "//www.gstatic.com/travel-hotels/branding/icon_184.png",
"rate_per_night": { "display": "$221", "amount": 221.04, "currency": "USD" },
"total_rate": { "display": "$442", "amount": 442.08, "currency": "USD" },
"num_guests": 2
},
{
"name": "Hotels.com",
"id": 1162912808,
"click": "/aclk?...",
"icon": "//www.gstatic.com/travel-hotels/branding/...",
"rate_per_night": { "display": "$227", "amount": 227.33, "currency": "USD" },
"total_rate": { "display": "$455", "amount": 454.67, "currency": "USD" },
"num_guests": 2
}
]
}
}booking_sources[]
Each entry is one vendor's quote for the requested dates and currency. Sponsored entries appear first and carry "sponsored": true; organic entries follow.
| Field | Type | Always present | Description |
|---|---|---|---|
sponsored | boolean | true on paid placements ("Featured options"); omitted on organic offers | |
name | string | yes | Vendor brand (e.g. "Booking.com", "Hotels.com", "Agoda") |
id | integer | Internal source identifier | |
click | string | Click-through URL that redirects to the vendor's listing | |
icon | string | Vendor brand icon URL | |
rate_per_night | object | Per-night rate. See Price. Omitted when not quoted | |
total_rate | object | Total stay cost. See Price. Omitted when not quoted | |
num_guests | integer | Occupancy the rate is quoted for — always 2 when present |
rate_per_night.currency and total_rate.currency reflect the request's currency. Numeric amount values are normalized — thousands separators are stripped before parsing.
Amenity Codes
Raw integer codes from Google's classification. Common values:
| Code | Amenity | Code | Amenity |
|---|---|---|---|
| 1 | Fitness center | 16 | Outdoor pool |
| 2 | Bar | 17 | Pet-friendly |
| 4 | Restaurant | 20 | Indoor pool |
| 5 | Room service | 22 | Pool |
| 6 | Free breakfast | 23 | Air-conditioned |
| 7 | Kids' club | 24 | Kitchen / kitchenette |
| 8 | Spa | 25 | Smoke-free |
| 9 | Business center | 26 | Full-service laundry |
| 10 | Child-friendly | 27 | Free Wi-Fi |
| 11 | Accessible | 29 | Wi-Fi |
| 14 | Beach access | 31 | Airport shuttle |
| 15 | Parking |
Codes may change without notice. Raw integer values are forwarded as-is from Google. The same codes are returned in properties[].amenities[], so they round-trip safely.
Property Types
| Value | When returned |
|---|---|
"hotel" | Traditional hotel, resort, inn, or hostel |
"vacation rental" | Vacation rental, apartment, or villa. Returned when property_types=12 is set |
Mode switching depends on inventory. Some destinations continue to return type: "hotel" even when property_types=12 is set if no vacation rentals are listed.
Error Handling
{ "error": "error_code", "message": "Human readable error message" }Common Error Codes
| Status | Error | Description |
|---|---|---|
400 | token is required | Missing token |
400 | q (hotel search query) is required | Missing q |
400 | check_in_date is required (format: YYYY-MM-DD) | Missing or malformed check_in_date |
400 | check_out_date is required (format: YYYY-MM-DD) | Missing or malformed check_out_date |
400 | check_out_date must be after check_in_date | Date range inverted |
400 | sort_by must be 3, 8, or 13 | Invalid sort_by |
400 | min_price must be a non-negative integer | Invalid min_price |
400 | max_price must be a non-negative integer | Invalid max_price |
400 | min_price must be <= max_price | Range inverted |
400 | rating must be 7, 8, or 9 | Invalid rating |
400 | hotel_class values must be 2-5 | Invalid hotel_class |
400 | detail_token is required | Detail endpoint called without detail_token |
400 | invalid detail_token: ... | detail_token malformed or not base64url |
502 | request failed | Transient. Retry |
502 | unexpected response | Non-200 upstream |
500 | decompression failed / parse failed | Transient. Retry |

