Skip to content

Search a destination, then price a hotel

List a destination's hotels with Google Hotels prices and filters, page through them, then price the one you pick.

Overview

You get
The hotels Google Hotels lists for a destination and stay, about 20 per page, each with its property token, lowest price for the stay, rating, class, amenities and location.
Calls
POST/v1/hotels/search per page, then POST/v1/calendar or POST/v1/offers for the hotel you pick
Cost
3 credits per page (an empty page is free), then 5 for a calendar or 3 for offers
Stay
Up to 30 nights; check-in today or later

Already on SearchAPI's google_hotels engine? POST/v1/serp/google_hotels takes the same parameters and returns the same shape, at the same price.

q is what a traveller would type into Google Hotels: hotels in Montreal, Paris, hotels near JFK. gl sets the market and currency the price currency.

curl -X POST "https://api.scrapercompany.com/v1/hotels/search" \
  -H "x-api-key: $SCRAPERCOMPANY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "q": "hotels in Montreal",
    "check_in": "2026-11-30",
    "check_out": "2026-12-02",
    "adults": 2,
    "currency": "CAD",
    "gl": "ca"
  }'

Check where Google placed the query

search_information.location says which place Google searched. A query Google can't place, which it would quietly widen to a whole country, returns 404 instead (unless the query names that country). Google can also settle an unknown query on a nearby city, which no check can catch, so read location before trusting the list.

Read the prices

Each property's rate is the lowest price Google shows for the stay, with the stay split four ways: base, taxes, fees and total (fees are never folded into taxes).

One property (shortened)
{
  "type": "hotel",
  "property_token": "ChgImtDg_rW5k8iWARoLL2cvMXRzamQ4NncQAQ",
  "name": "Radisson Hotel Montreal Airport",
  "hotel_class": "4-star hotel",
  "extracted_hotel_class": 4,
  "rating": 3.5,
  "reviews": 2535,
  "amenities": [
    "Breakfast ($)",
    "Free Wi-Fi",
    "Parking ($)",
    "Pools"
  ],
  "deal": "27% less than usual",
  "deal_description": "Great Deal",
  "rate": {
    "currency": "CAD",
    "check_in": "2026-11-30",
    "check_out": "2026-12-02",
    "nights": 2,
    "price_per_night": "$121",
    "price_per_night_before_taxes": "$102",
    "extracted_price_per_night": 121,
    "extracted_price_per_night_before_taxes": 101.68,
    "base": 203.36,
    "taxes": 38.64,
    "fees": 0,
    "total": 242,
    "before_taxes": 203.36,
    "from_display_text": false
  }
}
FieldMeaning
total
The whole stay: base + taxes + fees.
before_taxes
base + fees for the stay (SearchAPI’s before-taxes basis).
extracted_price_per_night
total / nights, so it includes taxes and fees.
extracted_price_per_night_before_taxes
Google’s exact (base + fees) / nights: the nightly price on the Google Hotels card.
price_per_night, total_price, …
The same figures as Google displays them, rounded.
from_display_text
true when only display strings came back: taxes and fees are then null and base holds the displayed before-taxes total.

rate is null for a property Google showed without a price, or one priced for other dates (the price is removed and a warning says so). type is hotel or vacation_rental: Google sometimes blends rentals into the list, and they carry their seller rows in sources.

Filter and sort

Add any of these to the body; Google applies them before paging.

FieldValues
sort_by
relevance (default), lowest_price, highest_rating, most_reviewed
price_min
Nightly price bounds in currency (with price_max)
min_rating
3.5, 4.0 or 4.5
hotel_class
Star classes, e.g. [4, 5]
amenities
Amenity ids, e.g. 6 Pool, 35 Free Wi-Fi, 9 Free breakfast, 19 Pet-friendly (full list in the reference)
property_types
Property-type ids, e.g. 13 Boutique hotels, 17 Resorts, 19 Bed and breakfasts
free_cancellation, special_offers, eco_certified
true to keep only those properties
children_ages
One age per child, e.g. [8]; the response is rejected if Google priced a different party
Four- and five-star hotels with a pool, cheapest first
{
  "q": "hotels in Montreal",
  "check_in": "2026-11-30",
  "check_out": "2026-12-02",
  "adults": 2,
  "currency": "CAD",
  "gl": "ca",
  "sort_by": "lowest_price",
  "hotel_class": [
    4,
    5
  ],
  "amenities": [
    6
  ],
  "min_rating": 4,
  "price_max": 400
}

Filters Google may ignore

When returned properties break the requested hotel_class, min_rating or price_max, the response carries a warning rather than a silently wrong list. lowest_price is Google's order, which is not always strictly ascending. Brand filters and a rentals-only list are refused with 422, because Google does not honour them for this search.

Page through results

Resend the same body with page_token set to the previous page's pagination.next_page_token. Each page is a new 3-credit call. Pages overlap by a few records (page 1 is records 1-20, page 2 starts at 19), so de-duplicate by property_token.

seen = {}
token = None
for _ in range(3):  # three pages, 9 credits at most
    r = requests.post(f"{API}/v1/hotels/search", headers=HEADERS, timeout=120,
                      json={**query, **({"page_token": token} if token else {})})
    r.raise_for_status()
    page = r.json()
    for p in page["properties"]:
        seen.setdefault(p["property_token"], p)
    token = page["pagination"]["next_page_token"]
    if not token:
        break
print(len(seen), "unique properties")

A token Google no longer accepts returns 422: restart from the first page.

Price the hotel you pick

property_token is the same Google property token every Google Hotels endpoint takes, so there is no separate lookup. Store it with the hotel.

hotel = min((p for p in seen.values() if p.get("rate") and p["rate"]["total"] is not None),
            key=lambda p: p["rate"]["total"])
r = requests.post(f"{API}/v1/calendar", headers=HEADERS, timeout=120, json={
    "token": hotel["property_token"], "days": 90, "currency": "CAD", "market": "CA",
})
r.raise_for_status()
calendar = r.json()
print(hotel["name"], len(calendar["rates"]), "nights priced")

Good to know

  • Prices follow the market Google sees the request from. When that market could not be verified the response says so in warnings; keep gl and currency constant between runs, and check search_information.currency_matches_request.
  • Google decides whether to blend vacation rentals into the list, so total_results can change between identical requests. Each result's type says what it is.
  • Display strings follow hl; hotel amenity names are always English.