🎉 Developer Free Trial — 1 week of full API access, no credit card required. Claim your trial →
Guide

Matching the same hotel across OTAs: why name matching fails, and how to key on Google Place ID

Before you can compare a hotel's price on two OTAs, you have to be sure both rows are the same hotel. Name-and-city matching gets this wrong often enough to matter, and it fails silently: the wrong hotel still returns a perfectly plausible price.

Published 18 September 2026

Why matching on names fails

Every OTA stores its own copy of a property's name, address and coordinates, typed in by a different person under different rules. The strings drift apart in predictable ways:

A wrong match is worse than a missing one. A missing row shows up as a gap you can see. A wrong row shows up as a real-looking price for a different building. Parity alerts fire on hotels that are in parity, and competitor sets quietly include the wrong competitor.

The options, and what each one costs you

ApproachHow it worksWhere it breaks
Fuzzy name + cityString similarity on name, sometimes addressBranches of one chain, transliteration, rebrands. Needs a threshold nobody can tune well for every market.
Name + coordinatesSimilarity plus a distance cutoffBetter, but OTAs disagree on pins, and dense city centres put different hotels within the same radius.
Supplier mapping dataBuy a pre-built cross-reference between supplier IDsGood if you already buy it. It maps supplier codes, not what a scraper sees on a public page.
A shared external keyResolve every property once to one identifier, then key everything on itYou have to resolve and maintain the key. That is the whole job, and it is a small one.

Google Place ID as the shared key

A Place ID is Google's unique identifier for a place in its Places database and on Google Maps. It is a practical key for hotels because Google Hotels already groups the OTA offers for a property under that property, so resolving a hotel to its Place ID does the cross-OTA matching step for you.

Three details from Google's own documentation decide how you store it:

A matching pipeline that holds up

1
Resolve once, by hand where it matters. For each property in your set, find its Place ID and have a person confirm the ambiguous ones (chains, same-name neighbours). This is a one-time cost per property, not per price.
2
Store the ID and the query that found it. Keep place_id, the original search string, and the date you resolved it. The query is your recovery path if the ID ever goes stale.
3
Key every price row on the ID, not the name. Names become display fields. Joins, deduplication and parity checks all run on place_id.
4
Refresh on a schedule. Re-check IDs older than 12 months. Treat NOT_FOUND as a review task, never as a silent re-match.
5
Label anything that was not resolved. If you must fall back to name search for a new property, flag those rows as lower-confidence until a person confirms them.

How ScrapeGuys handles it

Full disclosure: this is our product. The Google Hotels API accepts a Place ID directly. In that mode the lookup is deterministic, so a request cannot return a different hotel than the one you asked for. Each hotel comes back with 12 to 25 OTAs, including Booking.com, Agoda, MakeMyTrip, Expedia, Hotels.com and Goibibo. Place ID jobs typically finish in 3–5 seconds, name-search jobs in 4–8, and you can batch up to 20 hotels that share the same dates in one request. Details are in the FAQ and the API docs.

Where we are not the answer: if you already license supplier mapping data and only work with supplier codes, keep it. A Place ID key earns its place when your prices come from public pages, where supplier codes are not available.

Questions we get about this

No. Google states that Place IDs may change as its Maps database is updated, and recommends refreshing stored IDs that are more than 12 months old. The refresh request is free.

Yes. Google's documentation says Place IDs are exempt from the caching restrictions in the Google Maps Platform Terms of Service, so they can be stored for later use.

Treat it as a review task. Re-run the original search that produced the ID, confirm the result is the same property, and only then store the new ID. Never let the pipeline re-match silently.

Mostly. You still need a one-time resolution step for each new property, and ambiguous cases such as two branches of one chain deserve a human check. After that, every join runs on the ID.

Sources