🎉 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 what to key on instead

Every multi-OTA price comparison rests on one assumption: that the rows being compared describe the same physical property. That assumption is usually enforced by matching names — and names are the least stable thing about a hotel. When the match is wrong, every number downstream is wrong, and nothing errors.

One property, many identities

A hotel does not have one name. It has the name on its sign, the name in its OTA contracts, the name its chain's CRS uses, and the name each OTA's content team edited for their own listing. All of these drift independently:

The failure is silent. A wrong match does not throw an exception. It produces a plausible-looking price row attached to the wrong building, and the pipeline keeps running. That is what makes property identity a data-quality problem rather than a scraping problem.

What a false match costs

Rate parity and rate shopping

A parity alarm fires because "your" hotel is undercut on an OTA — except the row belongs to the similarly named property two streets away. Teams burn trust chasing phantom violations, then start ignoring the alarms that are real.

Competitive pricing models

A repricing model that ingests one mismatched competitor learns from a property with different positioning, star rating and demand curve. The error is invisible inside an average until someone audits row by row.

AI booking agents

An agent that resolves "the Taj near the airport" to the wrong listing quotes a real price for the wrong hotel. The user discovers it at the booking page — the most expensive possible place to find a data bug.

Inventory joins

Merging OTA catalogues by name either drops real properties (false negatives) or double-counts them (false positives). Both corrupt market-coverage numbers that business decisions get made on.

The fix is a deterministic key, not a better matcher

Smarter fuzzy matching lowers the error rate; it never removes the failure mode. The structural fix is to stop matching on descriptions and start joining on an identifier that names the physical place itself.

The most practical such key for hotels is the Google Place ID — Google's unique identifier for an establishment in the Places database (Google's documentation). It has three properties that matter for this job:

How to build the join in practice

1
Resolve each property to a Place ID once. For a portfolio or comp set this is a one-time mapping exercise, and worth doing with human eyes: search, confirm the address and photos match, store the ID. This is minutes per property, done once — versus a fuzzy match re-gambled on every pipeline run.
2
Treat the mapping table as an asset. Property → Place ID is slowly changing data. Keep it versioned, record who verified each row and when, and re-verify a row when a property rebrands or closes.
3
Use the metasearch view as the pivot. Query the property by Place ID on Google Hotels to get the multi-OTA price comparison already joined to the right building. Per-OTA collection (for room-level detail or member pricing) then keys back to the same Place ID, so every row in your warehouse shares one property key.
4
Flag the fallback rows. Where no Place ID exists (small guesthouses, brand-new openings), fall back to name-plus-location search — and mark those rows as probabilistic matches so they never silently pool with deterministic ones.

Where it still gets hard

How ScrapeGuys handles it

Full disclosure, this is our product — the mechanics in one paragraph.

The Google Hotels scraper takes a Place ID directly (Place ID mode, which the docs mark as recommended precisely because it is deterministic — no wrong-hotel matches), returns the multi-OTA comparison for that property, and batches up to 20 hotels per request; name-search mode exists as the fallback for properties you have not mapped yet, with the property resolved server-side in v2. The same Place ID key then lines up with per-OTA collection from Agoda, Booking.com, MakeMyTrip and Expedia, and with Rate Shop for many-hotels-many-OTAs sweeps. Coverage and limits are in the FAQ.

Where we are not the answer: if you need a static, GDS-grade mapping database across supplier codes — the problem dedicated hotel-mapping vendors sell — that is a different product category. Our key is Google's, which is the right tool when your pipeline consumes live OTA prices; it is not a replacement for supplier-code mapping inside a GDS integration.

Questions we get about this

A textual identifier for a place in Google's Places database — a string like ChIJN1t_tDeuEmsRUsoyG83frY4 that uniquely identifies one establishment, per Google's documentation. For a hotel it identifies the property itself, independent of how any OTA spells its name.

Coordinates get you close but cannot separate co-located properties — two hotels in one tower, an aparthotel on three floors, a resort and its villas on one campus — and each OTA geocodes addresses independently, so the coordinates themselves disagree slightly. A proximity join still needs a tie-breaker, and the tie-breaker ends up being the name again.

It happens with small guesthouses, new openings and some vacation rentals. Fall back to name-plus-location search and flag the match as probabilistic, so downstream consumers know the row carries risk instead of pooling it silently with deterministic ones.

No. Two OTAs can agree on the hotel and still describe its rooms differently — names, bed configurations, rate-plan bundling. Compare prices at room level only when the room attributes match, not just the property.

Sources