Google Hotels Entegrasyonu: Developer Rehberi
Google Hotels entegrasyonunu Hotel List, property mapping, Pull/Changed Pricing/ARI, Transaction XML, landing pages, OAuth2, request/response modelleri, error handling ve monitoring ile developer gözüyle uygulayın.
- 2026-09-26 — Provider companion standard applied; access, limits, lifecycle and evidence boundaries clarified.
Google Hotels entegrasyonu tek bir REST endpoint çağırmak değildir. Production bağlantı en az dört ayrı contract'tan oluşur:
- Property identity — Google'a hangi hotelleri gönderdiğiniz ve bunların nasıl eşleştiği,
- Pricing & availability — fiyatı hangi delivery mode ile ve hangi XML contract'ıyla taşıdığınız,
- Landing page handoff — Google'daki offer click'inin booking engine'e doğru context ile aktarılması,
- Monitoring & quality — mapping, freshness, price accuracy ve downstream conversion kalitesi.
Developer açısından en sağlıklı yaklaşım Google-specific XML/API modellerini internal domain modelinizden ayırmaktır.
Internal Hotel Domain
|
+--> Google Hotel List Adapter
|
+--> Google Pricing Adapter
| +--> Pull
| +--> Changed Pricing
| +--> ARI
|
+--> Landing Page Adapter
|
+--> Travel Partner API / DiagnosticsBu rehber Google dokümantasyonunu tekrar etmek yerine, entegrasyon boundary'sini nasıl tasarlayacağınızı gösterir.
Provider companion özeti
| Alan | Durum |
|---|---|
| Erişim | Hotel Center / partner setup gerekir; tüm yüzeyler anonymous self-service API değildir |
| Auth | Travel Partner API ve ilgili API yüzeylerinde OAuth 2.0 / service account |
| Primary contracts | Hotel List, Pricing/ARI/Transaction messages, Landing Pages, Travel Partner API |
| Data direction | Pull, Changed Pricing veya ARI modeline göre pull/push/hybrid |
| Pagination | Travel Partner API resource'larına göre endpoint-specific; pricing XML/message flow için generic pagination kavramı yok |
| Polling | Feed/message modeline göre değişir; Google'ın Query/Hint akışında partner kendi sabit polling interval'ını uydurmamalıdır |
| Universal public rate limit | Bu companion için account-independent tek bir public QPS/quota değeri doğrulanmadı; account/docs/configuration üzerinden doğrulayın |
| Booking ownership | Google Hotels generic booking-creation API değildir; kullanıcı booking partner/engine'e handoff olur |
| Evidence level | Official documentation reviewed; live Hotel Center account ile test edildiği iddia edilmiyor |
| Code examples | Illustrative architecture/mapping examples |
Capability boundary
Google Hotels'i tek bir endpoint gibi modellemeyin. Entegrasyon yüzeyleri farklı sorumluluk taşır:
Property identity -> Hotel List / matching
Price distribution -> Pull / Changed Pricing / ARI / Transaction
Consumer handoff -> Landing Pages
Account diagnostics -> Travel Partner API
Price validation -> crawler navigation / structured data / price accuracy toolingBir yüzeydeki auth, retry, pagination veya quota davranışını diğer yüzeye otomatik taşımayın.
Rate limit ve quota yaklaşımı
Bu rehberde universal bir Google Hotels QPS değeri vermiyoruz; çünkü public dokümantasyonda tüm partner/account/surface kombinasyonları için tek bir account-independent limit doğrulanmadı. Uygulama:
- aktif Hotel Center / API dokümantasyonundaki account-specific quota'yı okuyun,
- 429/quota response'larını ayrı sınıflandırın,
- exponential backoff + jitter kullanın,
- internal concurrency budget belirleyin,
- gözlenen account limitini "Google global limiti" diye yayımlamayın.
Polling ve pagination yaklaşımı
Pricing message flow klasik paginated REST collection değildir. Pull/Changed Pricing/ARI kendi message lifecycle'ına sahiptir. Travel Partner API tarafında ise collection/resource endpoint'i pagination sunuyorsa o endpoint'in resmi contract'ını uygulayın.
Bu yüzden companion standardında bu alanların değeri surface-specific olarak işaretlenmiştir.
Booking / reprice lifecycle
Google Hotels entegrasyonunda price discovery ve booking handoff ayrıdır:
Hotel/partner price state
-> Google price surface
-> user click
-> landing/booking engine
-> downstream booking confirmationGoogle'da görüntülenen bir offer, internal sisteminizde confirmed booking değildir. Click/handoff sonrası selected context, landing total ve booking confirmation ayrı event olarak tutulmalıdır.
Platform sorumlulukları ve operasyona hazırlık kararları için Google Hotels profiline bakın. Sözleşme sınırları, yayınlama hatalarından kurtarma ve Hotel List/POI ayrımı için Google Hotel Feeds referansını kullanın.
1. Ön koşullar ve erişim modeli
Google Hotels bağlantısı yalnız public bir API key ile açılan self-service bir endpoint değildir. Hotel Center partner hesabı, ilgili feed/pricing setup'ı ve bazı API'ler için trusted-partner erişimi gerekir.
Travel Partner API tarafında Google OAuth 2.0 ve service account kullanır. Güncel dokümantasyonda Travel Partner API scope'u:
https://www.googleapis.com/auth/travelpartnerPrice Feeds API için kullanılan scope:
https://www.googleapis.com/auth/travel-partner-price-uploadTravel Partner API endpoint paterni:
https://travelpartner.googleapis.com/v3/accounts/{account_id}/{path}Service account'un ayrıca ilgili Hotel Center hesabına erişimi olmalıdır.
Internal credential boundary
Credential browser'a gitmemelidir.
interface GoogleHotelsCredentials {
serviceAccountEmail: string;
privateKeyRef: string;
hotelCenterAccountId: string;
partnerKey?: string;
}Secret'ın kendisini config object içine gömmek yerine secret manager reference kullanın.
2. Canonical hotel modeli önce gelmeli
Google'a property göndermeden önce kendi canonical property modeliniz stable olmalıdır.
interface Hotel {
id: string; // internal canonical id
name: string;
countryCode: string;
city?: string;
addressLine?: string;
latitude?: number;
longitude?: number;
phone?: string;
website?: string;
active: boolean;
}Google-specific mapping ayrı tutulmalı:
interface GoogleHotelMapping {
hotelId: string; // internal canonical id
googlePartnerHotelId: string;
matchStatus: "matched" | "unmatched" | "review";
matchConfidence?: number;
lastSubmittedAt?: string;
lastMatchedAt?: string;
}Önemli invariant:
internal hotel id != Google partner hotel idHotel adı veya brand değiştiğinde internal identity değişmemelidir.
3. Hotel List entegrasyonu
Hotel List Google tarafındaki property identity layer'dır. Pricing message içindeki <Property> değeri Hotel List'teki listing ID ile eşleşmelidir.
Internal → Google Hotel List mapping
Örnek internal model:
{
"id": "HTL-84721",
"name": "Example Bosphorus Hotel",
"countryCode": "TR",
"city": "Istanbul",
"latitude": 41.0423,
"longitude": 29.0082,
"phone": "+90...",
"active": true
}Google adapter'ın ürettiği simplified listing:
<listing>
<id>HTL-84721</id>
<name>Example Bosphorus Hotel</name>
<address>
<component name="country">TR</component>
<component name="locality">Istanbul</component>
</address>
<latitude>41.0423</latitude>
<longitude>29.0082</longitude>
</listing>Bu örnek minimal ve açıklayıcıdır; production payload'ı Google'ın güncel Hotel List XML referansına göre üretilmelidir.
Hotel List pipeline
Canonical Hotel DB
|
v
Eligibility validation
|
v
Google Hotel List serializer
|
v
Feed delivery
|
v
Match report / diagnostics
|
+--> matched
+--> unmatched
+--> manual reviewKontrol edilmesi gerekenler
- stable ID,
- doğru geo,
- duplicate property,
- rebrand,
- closure/reactivation,
- invalid address,
- unsupported entity type.
Hotel List accepted olsa bile mapping sonucu ayrıca izlenmelidir.
4. Pricing delivery mode nasıl seçilir?
Google dokümantasyonunda üç ana model bulunur:
Pull
Google size <Query> gönderir; siz ilgili itinerary/property için güncel <Transaction> döndürürsünüz.
Uygundur:
- price change'i kendi tarafınızda güvenilir detect edemiyorsanız,
- query-time backend hızlıysa,
- supplier fan-out latency'niz kontrol altındaysa.
Risk:
- upstream latency,
- query burst,
- supplier timeout,
- yüksek look-to-book.
Changed Pricing
Google <HintRequest> gönderir. Siz hangi property/itinerary'lerin değiştiğini <Hint> ile belirtirsiniz. Google yalnız ilgili context için <Query> gönderir.
Google'ın akışı:
Google HintRequest
|
Partner Hint
|
Google Query
|
Partner TransactionBu model change detection'iniz güvenilir olduğunda Pull trafiğini azaltır.
ARI
Availability, Rate ve Inventory değişikliklerini siz push edersiniz.
Uygundur:
- local inventory/rate state güçlü ise,
- değişiklikleri event/delta olarak yakalayabiliyorsanız,
- yüksek hacimde predictable update akışı istiyorsanız.
Karar tablosu
| Durum | Daha uygun başlangıç |
|---|---|
| Change detection zayıf, backend hızlı | Pull |
| Change detection güçlü | Changed Pricing |
| ARI source-of-truth sizde | ARI |
| Supplier latency yüksek | Cache/Changed/ARI ağırlıklı |
| Child occupancy context kritik | Live/context-aware pricing gereksinimini ayrıca değerlendirin |
5. Pull / Changed Pricing request-response akışı
Google'dan gelebilecek simplified query:
<Query>
<Checkin>2026-10-10</Checkin>
<Nights>3</Nights>
<PropertyList>
<Property>HTL-84721</Property>
</PropertyList>
</Query>Bunu doğrudan domain modeliniz yapmayın. Önce adapter DTO'ya parse edin:
interface GooglePriceQuery {
checkIn: string;
nights: number;
propertyIds: string[];
}Sonra internal request'e çevirin:
interface HotelPriceRequest {
hotelIds: string[];
checkIn: string;
checkOut: string;
rooms: Array<{
adults: number;
childAges: number[];
}>;
currency?: string;
market?: string;
}Adapter sorumluluğu:
Google Query
-> parse
-> validate
-> provider hotel id -> internal hotel id
-> build internal pricing request
-> price engine
-> normalize
-> Google Transaction serializer6. Transaction XML response modeli
Google pricing response'un merkezinde <Transaction> bulunur.
Minimal conceptual response:
<Transaction timestamp="2026-09-20T12:00:00Z" id="txn-84721-1010">
<Result>
<Property>HTL-84721</Property>
<Checkin>2026-10-10</Checkin>
<Nights>3</Nights>
<Baserate currency="TRY">12000.00</Baserate>
<Tax currency="TRY">2400.00</Tax>
<OtherFees currency="TRY">300.00</OtherFees>
<Refundable
available="true"
refundable_until_days="2" />
<Occupancy>2</Occupancy>
</Result>
</Transaction>Google'ın Transaction mesajında:
timestampzorunlu,idzorunlu ve unique olmalı,<Property>Hotel List ID ile eşleşmeli,<Result>price/availability state taşır,- room/rate çeşitliliği için
<Rates>ve<RoomBundle>kullanılabilir.
Google Transaction mesajlarının toplam boyutu için dokümantasyonda 100 MB sınırı belirtilir; bunu hard-coded operational target gibi değil üst protocol sınırı olarak düşünün.
7. Internal normalized offer modeli
Google XML'i business logic'inizde dolaştırmayın.
interface HotelOffer {
hotelId: string;
provider: string;
checkIn: string;
nights: number;
occupancy: {
adults: number;
childAges: number[];
};
roomId?: string;
ratePlanId?: string;
baseAmount: number;
taxAmount: number;
mandatoryFeeAmount: number;
totalAmount: number;
currency: string;
refundable?: boolean;
refundableUntil?: string;
observedAt: string;
source: "live" | "cache";
}Serializer:
function toGoogleTransaction(offer: HotelOffer): GoogleTransaction {
return {
property: offer.hotelId,
checkIn: offer.checkIn,
nights: offer.nights,
baseRate: offer.baseAmount,
tax: offer.taxAmount,
otherFees: offer.mandatoryFeeAmount,
currency: offer.currency,
occupancy: offer.occupancy.adults
};
}Ama gerçek production serializer Google'ın XML schema'sını tam uygulamalıdır.
8. RoomData, PackageData ve RoomBundle ne zaman gerekli?
Simple property-level price yeterli değilse oda ve package metadata'sı önem kazanır.
Google Transaction modeli:
Transaction
├── PropertyDataSet
│ ├── Property
│ ├── RoomData
│ └── PackageData
│
└── Result
├── Rates
└── RoomBundleInternal modeliniz de physical room ile commercial rate'i ayırmalıdır:
interface Room {
id: string;
hotelId: string;
name: string;
maxOccupancy?: number;
}
interface RatePlan {
id: string;
meal?: string;
refundable?: boolean;
paymentType?: string;
conditionalRule?: string;
}RoomID ve PackageID provider adapter concern olmalı; canonical room/rate identity ile bire bir aynı olmak zorunda değildir.
9. Taxes, fees ve total price
Google pricing'de base, tax ve other fee component'leri ayrı taşınabilir. Kendi modelinizde de bunları flatten etmeyin.
interface PriceBreakdown {
base: number;
taxes: number;
mandatoryFees: number;
payAtProperty?: number;
total: number;
currency: string;
}Kontrol:
total = base + taxes + mandatory feesAncak pay-at-property ve market-specific component'ler için display semantics ayrıca tanımlanmalıdır.
En yaygın hata:
Google'a base rate göndermek ama landing page'de mandatory fee sonrası daha yüksek total göstermek.
Bu doğrudan price accuracy problemine dönüşür.
10. Occupancy ve child pricing
Occupancy cache key'in bir parçası olmalıdır.
hotel
+ check-in
+ nights
+ adults
+ child ages
+ currency
+ marketGoogle'ın güncel Transaction dokümantasyonu child occupancy rates için context-aware live pricing sınırına özellikle dikkat çeker. Child pricing'i generic double-occupancy cache'e sıkıştırmak yanlış sonuç üretir.
Örnek:
interface Occupancy {
adults: number;
childAges: number[];
}2 adults != 2 adults + child age 711. Changed Pricing Hint modeli
Change detection yapabiliyorsanız internal event üretin:
interface PriceChangeEvent {
hotelId: string;
firstAffectedDate: string;
lastAffectedDate?: string;
nights?: number;
changedAt: string;
version: number;
}Bundan Google Hint üretilebilir:
<Hint>
<Item>
<Property>HTL-84721</Property>
<Stay>
<CheckInDate>2026-10-10</CheckInDate>
<LengthOfStay>3</LengthOfStay>
</Stay>
</Item>
</Hint>Change event idempotent olmalı; aynı version tekrar işlendiğinde gereksiz inconsistent state yaratmamalıdır.
12. Landing page entegrasyonu
Google landing page dosyasının root elementi <PointsOfSale>'dır ve her landing hedefi <PointOfSale> ile tanımlanır.
Buradaki asıl developer problemi URL template değil context preservation'dır.
Booking engine URL:
https://booking.example.com/hotel/{hotelId}
?checkin={checkIn}
&checkout={checkOut}
&adults={adults}
¤cy={currency}
&click_id={trackingId}Internal handoff modeli:
interface BookingHandoff {
hotelId: string;
checkIn: string;
checkOut: string;
occupancy: Occupancy;
currency: string;
roomId?: string;
ratePlanId?: string;
clickId: string;
}Test yalnız URL'nin açılması değildir.
Doğrulayın:
- doğru hotel,
- doğru dates,
- occupancy,
- currency,
- selected room/rate,
- total price,
- mobile redirect,
- attribution ID.
13. Travel Partner API nerede kullanılır?
Travel Partner API pricing XML transport'unun yerine geçen generic booking API değildir. Hotel Center hesabı ve diagnostic/management verisi için ayrı bir API surface'tir.
Örnek authorization header:
Authorization: Bearer <oauth2-access-token>Conceptual request:
GET https://travelpartner.googleapis.com/v3/accounts/123456/priceAccuracyViews/latest
Authorization: Bearer <token>Internal API client boundary:
interface GoogleTravelPartnerClient {
getPriceAccuracyView(accountId: string, viewId: string): Promise<PriceAccuracyView>;
getAccountDiagnostics(accountId: string): Promise<unknown>;
}Google API DTO'larını doğrudan dashboard domain modeliniz yapmayın.
14. Price accuracy feedback loop
Google structured-data ve price-accuracy mekanizmaları landing tarafındaki visible price ile gönderdiğiniz pricing bilgisinin tutarlılığını doğrulamaya yardımcı olur.
Internal loop:
Price submitted
|
v
Google displays offer
|
v
Landing page validation
|
+--> match
|
+--> mismatch
|
+--> classify reason
+--> provider/hotel alert
+--> freshness policy update
+--> mapping/rate fixMismatch taxonomy:
TAX_MISMATCH
MANDATORY_FEE_MISMATCH
STALE_PRICE
WRONG_ROOM
WRONG_ITINERARY
CONDITIONAL_RATE
LANDING_CONTEXT_LOST
UNAVAILABLE
UNKNOWN15. Timeout, retry ve error handling
Tüm error'ları aynı retry policy ile ele almayın.
Retryable
- transient network failure,
- selected 5xx,
- temporary timeout,
- quota/rate-limit condition uygun backoff ile.
Non-retryable veya manual-fix
- invalid XML,
- Hotel List ID mismatch,
- schema validation,
- unsupported currency,
- malformed occupancy,
- invalid landing config.
Örnek policy:
interface RetryPolicy {
maxAttempts: number;
initialBackoffMs: number;
maxBackoffMs: number;
retryableStatusCodes: number[];
}Google-specific error'ı internal taxonomy'ye map edin:
GOOGLE_AUTH
GOOGLE_SCHEMA
GOOGLE_MAPPING
GOOGLE_RATE_LIMIT
GOOGLE_TIMEOUT
GOOGLE_UPSTREAM
GOOGLE_PRICE_REJECTED
GOOGLE_LANDING_ERROR16. Idempotency ve ordering
Transaction message'da unique ID kullanılması debugging için kritiktir.
Ayrıca Google dokümantasyonu Transaction processing'de timestamp semantics'ini önemser. Eski state'in yeni state üzerine yazmasını önlemek için internal version/order kontrolünüz olmalı.
interface PricingUpdateEnvelope {
eventId: string;
hotelId: string;
version: number;
sourceUpdatedAt: string;
generatedAt: string;
}Rule:
older version -> ignore
same version -> idempotent no-op
new version -> publish17. Persistence ve audit
Her XML payload'ı sonsuza kadar full blob olarak saklamak şart değildir; fakat replay/debug için yeterli lineage tutulmalıdır.
Örnek:
google_price_updates
- event_id
- hotel_id
- check_in
- nights
- occupancy_key
- source_version
- base_amount
- tax_amount
- fee_amount
- currency
- generated_at
- sent_at
- result_status
- retry_count
- payload_hashRaw payload için object storage reference kullanılabilir.
18. Monitoring ve SLO
Property identity
- Hotel List accepted count,
- mapping coverage,
- unmatched hotels,
- duplicate/review queue.
Pricing
- query count,
- transaction success,
- p95 response latency,
- stale age,
- changed-pricing event lag,
- rejected message count.
Quality
- price accuracy,
- tax/fee mismatch,
- unavailable-after-click,
- landing context success.
Commercial
- Google click → booking conversion,
- cancellation-adjusted conversion,
- unattributed booking,
- revenue by freshness bucket.
Örnek SLO:
mapping coverage >= 99%
p95 pricing response <= internal budget
offers under freshness threshold >= 97%
severe price mismatch < 1%
landing context success >= 99%Rakamlar kendi account/supply profile'ınıza göre belirlenmelidir.
19. Integration test matrisi
| Test | Beklenen |
|---|---|
| 1 gece / 2 adult | base happy path |
| 3 gece | stay total doğru |
| child occupancy | child context korunuyor |
| refundable | policy doğru |
| non-refundable | policy doğru |
| tax-heavy market | total doğru |
| mandatory property fee | disclosure doğru |
| currency change | correct currency |
| sold out | offer kaldırılıyor |
| price changed | fresh response |
| hotel renamed | stable ID |
| hotel closed | listing state güncel |
| mobile landing | context korunuyor |
| expired/stale cache | revalidation/fallback doğru |
| duplicate event | idempotent |
20. Go-live checklist
- Hotel Center / partner access hazır
- Service account yetkili
- Canonical hotel IDs stable
- Hotel List validation tamam
- Mapping coverage dashboard hazır
- Pricing delivery mode seçildi
- Google Query parser test edildi
- Transaction serializer schema-valid
- Room/rate semantics normalize
- Tax/fee breakdown doğru
- Occupancy/child cases test edildi
- Changed Pricing/ARI event ordering güvenli
- Landing pages context-preserving
- OAuth token rotation/refresh çalışıyor
- Retry taxonomy uygulanmış
- Idempotency uygulanmış
- Price accuracy dashboard var
- Alert'ler provider/hotel seviyesinde
- Conversion reconciliation ownership belli
Sonuç
Google Hotels entegrasyonunu “Google'a XML gönderme işi” olarak kurarsanız sistem kısa sürede provider-specific logic ile dolar. Daha sağlam model:
canonical domain
-> Google adapter
-> Google XML/API contract
-> diagnostics
-> quality feedbackşeklindedir.
Developer açısından başarı kriteri yalnız feed'in accepted olması değildir. Aynı hotel identity'sinin, aynı occupancy ve rate semantics'inin, doğru total price'ın ve doğru landing context'inin Google result'tan booking engine'e kadar korunmasıdır.
Google Hotels entegrasyonunuzu planlıyor musunuz?
Feed, connectivity, attribution ve production mimarisini birlikte değerlendirebiliriz.