---
title: "Google Hotels Integration: Developer Guide"
description: "Implement Google Hotels from a developer perspective with Hotel Lists, property mapping, Pull/Changed Pricing/ARI, Transaction XML, landing pages, OAuth2, request/response models, error handling and monitoring."
slug: "google-hotels-integration"
translationKey: "integration-google-hotels"
locale: "en"
type: "guide"
category: "integration"
tags: ["google-hotels","hotel","feed","ari","transaction-xml","landing-page","price-accuracy","oauth"]
vertical: ["hotel"]
platform: "Google Hotels"
domain: "developers.google.com"
featured: true
publishedAt: "2026-09-19"
updatedAt: "2026-09-26"
reviewedAt: "2026-09-26"
technicalVerifiedAt: "2026-09-26"
sourceVersion: "Travel Partner API v3 + Hotel Prices public docs reviewed 2026-09-26"
testedAgainst: "Official public documentation; not a live Hotel Center account"
codeExampleStatus: "illustrative"
changelog:
  - "2026-09-26 — Provider companion standard applied; access, limits, lifecycle and evidence boundaries clarified."
sources:
  - title: "Google Hotels — Hotel prices documentation"
    url: "https://developers.google.com/hotels/hotel-prices"
  - title: "Google Hotels — Integration overview / data feeds"
    url: "https://developers.google.com/hotels/hotel-prices/dev-guide/data-feeds"
  - title: "Google Hotels — Transaction messages XML reference"
    url: "https://developers.google.com/hotels/hotel-prices/xml-reference/transaction-messages"
  - title: "Google Hotels — Hint Response messages"
    url: "https://developers.google.com/hotels/hotel-prices/dev-guide/hint-response-messages"
  - title: "Google Hotels — Landing pages overview"
    url: "https://developers.google.com/hotels/hotel-prices/dev-guide/pos-overview"
  - title: "Google Hotels — API authorization"
    url: "https://developers.google.com/hotels/hotel-prices/dev-guide/api-auth"
  - title: "Google Hotels — Hotel price structured data"
    url: "https://developers.google.com/hotels/hotel-prices/structured-data/hotel-price-structured-data"
---

A Google Hotels integration is not a single REST call. A production connection spans at least four contracts:

1. **Property identity** — which hotels you submit and how they are matched,
2. **Pricing & availability** — which delivery mode and XML contract moves rates,
3. **Landing-page handoff** — whether Google clicks preserve the booking context,
4. **Monitoring & quality** — mapping, freshness, price accuracy and downstream conversion.

The safest developer architecture keeps Google-specific XML/API models outside your internal hotel domain.

```text
Internal Hotel Domain
      |
      +--> Google Hotel List Adapter
      |
      +--> Google Pricing Adapter
      |       +--> Pull
      |       +--> Changed Pricing
      |       +--> ARI
      |
      +--> Landing Page Adapter
      |
      +--> Travel Partner API / Diagnostics
```

This guide focuses on the implementation boundary rather than repeating Google's documentation.

For platform ownership and readiness decisions, see the [Google Hotels profile](/en/metasearch/hotels/google-hotels). For contract boundaries, publication recovery and the Hotel List/POI distinction, see [Google Hotel Feeds](/en/metasearch/feeds/google-hotel-feeds).

## 1. Access and prerequisites

Google Hotels is not a generic self-service endpoint opened with one public API key. You need the appropriate Hotel Center partner setup, pricing/feed configuration and, for some APIs, trusted-partner access.

Google currently documents OAuth 2.0 with service accounts for hotel APIs.

Travel Partner API scope:

```text
https://www.googleapis.com/auth/travelpartner
```

Price Feeds API scope:

```text
https://www.googleapis.com/auth/travel-partner-price-upload
```

Travel Partner API endpoint pattern:

```text
https://travelpartner.googleapis.com/v3/accounts/{account_id}/{path}
```

The service account also needs access to the relevant Hotel Center account.

### Internal credential boundary

Credentials belong on the server:

```ts
interface GoogleHotelsCredentials {
  serviceAccountEmail: string;
  privateKeyRef: string;
  hotelCenterAccountId: string;
  partnerKey?: string;
}
```

Store a secret-manager reference rather than embedding the private key in application configuration.

## 2. Start with a canonical hotel model

Before submitting properties to Google, your own identity model should be stable.

```ts
interface Hotel {
  id: string;
  name: string;
  countryCode: string;
  city?: string;
  addressLine?: string;
  latitude?: number;
  longitude?: number;
  phone?: string;
  website?: string;
  active: boolean;
}
```

Keep Google mapping separate:

```ts
interface GoogleHotelMapping {
  hotelId: string;
  googlePartnerHotelId: string;
  matchStatus: "matched" | "unmatched" | "review";
  matchConfidence?: number;
  lastSubmittedAt?: string;
  lastMatchedAt?: string;
}
```

Invariant:

```text
internal hotel id != Google partner hotel id
```

A rename or rebrand should not create a new internal identity.

## 3. Hotel List integration

The Hotel List is the property-identity layer used by Google. The `<Property>` value in pricing messages must match the listing ID defined in your Hotel List.

Example internal record:

```json
{
  "id": "HTL-84721",
  "name": "Example Bosphorus Hotel",
  "countryCode": "TR",
  "city": "Istanbul",
  "latitude": 41.0423,
  "longitude": 29.0082,
  "active": true
}
```

Simplified adapter output:

```xml
<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>
```

Use Google's current Hotel List XML reference for the production schema.

### Pipeline

```text
Canonical Hotel DB
      |
      v
Eligibility validation
      |
      v
Google Hotel List serializer
      |
      v
Feed delivery
      |
      v
Match report / diagnostics
      |
      +--> matched
      +--> unmatched
      +--> manual review
```

Monitor stable IDs, geocoding, duplicates, rebrands, closures and unsupported entities.

## 4. Choosing a pricing delivery mode

Google documents three primary models.

### Pull

Google sends a `<Query>`; your system responds with current pricing in a `<Transaction>`.

Useful when:

- you cannot reliably detect every price change,
- your query-time backend is fast,
- upstream latency is controlled.

Main risks are supplier latency, query bursts and high look-to-book load.

### Changed Pricing

Google sends `<HintRequest>`. Your system identifies changed hotels or itineraries with `<Hint>`. Google then requests only those contexts with `<Query>`.

```text
Google HintRequest
      |
Partner Hint
      |
Google Query
      |
Partner Transaction
```

Use it when change detection is reliable.

### ARI

Your system pushes Availability, Rate and Inventory changes.

Use it when local ARI state is authoritative and you can generate reliable deltas/events.

### Decision table

| Situation | Likely starting point |
|---|---|
| Weak change detection, fast backend | Pull |
| Reliable change detection | Changed Pricing |
| Strong ARI source of truth | ARI |
| High supplier latency | More cache/Changed/ARI |
| Child occupancy critical | Evaluate context-aware live pricing carefully |

## 5. Pull / Changed Pricing request-response flow

Simplified Google query:

```xml
<Query>
  <Checkin>2026-10-10</Checkin>
  <Nights>3</Nights>
  <PropertyList>
    <Property>HTL-84721</Property>
  </PropertyList>
</Query>
```

Parse into an adapter DTO first:

```ts
interface GooglePriceQuery {
  checkIn: string;
  nights: number;
  propertyIds: string[];
}
```

Then map into the internal request model:

```ts
interface HotelPriceRequest {
  hotelIds: string[];
  checkIn: string;
  checkOut: string;
  rooms: Array<{
    adults: number;
    childAges: number[];
  }>;
  currency?: string;
  market?: string;
}
```

Flow:

```text
Google Query
   -> parse
   -> validate
   -> provider hotel id -> internal hotel id
   -> internal price request
   -> price engine
   -> normalize
   -> Google Transaction serializer
```

## 6. Transaction XML response

Pricing responses are centered on `<Transaction>`.

Simplified example:

```xml
<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's current Transaction reference requires a timestamp and transaction ID. The `<Property>` must match the Hotel List listing ID. `<Rates>` and `<RoomBundle>` support richer room/rate scenarios.

The documented upper message-size limit is 100 MB; treat that as a protocol ceiling, not an operational batching target.

## 7. Normalize before serializing

Do not let Google XML become the domain model.

```ts
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 boundary:

```ts
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
  };
}
```

The production serializer must implement Google's current XML schema.

## 8. RoomData, PackageData and RoomBundle

For richer room-level offers, Google separates room/package metadata from itinerary-level pricing.

```text
Transaction
  ├── PropertyDataSet
  │     ├── Property
  │     ├── RoomData
  │     └── PackageData
  │
  └── Result
        ├── Rates
        └── RoomBundle
```

Keep physical room and commercial rate semantics separate internally:

```ts
interface Room {
  id: string;
  hotelId: string;
  name: string;
  maxOccupancy?: number;
}

interface RatePlan {
  id: string;
  meal?: string;
  refundable?: boolean;
  paymentType?: string;
  conditionalRule?: string;
}
```

Google `RoomID` and `PackageID` belong in the adapter boundary and do not need to equal your canonical IDs.

## 9. Taxes, fees and total price

Do not flatten components too early.

```ts
interface PriceBreakdown {
  base: number;
  taxes: number;
  mandatoryFees: number;
  payAtProperty?: number;
  total: number;
  currency: string;
}
```

At the simplest level:

```text
total = base + taxes + mandatory fees
```

But pay-at-property and market-specific display rules still need explicit semantics.

A classic failure is submitting a low base rate while the landing page displays a materially higher mandatory total.

## 10. Occupancy and child pricing

Occupancy belongs in the cache key:

```text
hotel
+ check-in
+ nights
+ adults
+ child ages
+ currency
+ market
```

Google's current Transaction documentation specifically calls out child occupancy pricing in the context of live/context-aware pricing. Do not collapse child cases into generic double-occupancy cache entries.

```ts
interface Occupancy {
  adults: number;
  childAges: number[];
}
```

```text
2 adults != 2 adults + child age 7
```

## 11. Changed Pricing hint model

Represent change detection internally first:

```ts
interface PriceChangeEvent {
  hotelId: string;
  firstAffectedDate: string;
  lastAffectedDate?: string;
  nights?: number;
  changedAt: string;
  version: number;
}
```

Then serialize a Google Hint:

```xml
<Hint>
  <Item>
    <Property>HTL-84721</Property>
    <Stay>
      <CheckInDate>2026-10-10</CheckInDate>
      <LengthOfStay>3</LengthOfStay>
    </Stay>
  </Item>
</Hint>
```

Change events should be idempotent.

## 12. Landing-page integration

Google landing-page files use `<PointsOfSale>` with one or more `<PointOfSale>` definitions.

The real implementation concern is context preservation.

```text
https://booking.example.com/hotel/{hotelId}
  ?checkin={checkIn}
  &checkout={checkOut}
  &adults={adults}
  &currency={currency}
  &click_id={trackingId}
```

Internal model:

```ts
interface BookingHandoff {
  hotelId: string;
  checkIn: string;
  checkOut: string;
  occupancy: Occupancy;
  currency: string;
  roomId?: string;
  ratePlanId?: string;
  clickId: string;
}
```

Validate the correct property, dates, occupancy, currency, selected product, total price, mobile redirects and attribution IDs.

## 13. Where the Travel Partner API fits

Travel Partner API is not a generic booking API replacing the pricing XML flow. It is a separate Hotel Center management/diagnostic API surface.

Authorization:

```http
Authorization: Bearer <oauth2-access-token>
```

Conceptual call:

```http
GET https://travelpartner.googleapis.com/v3/accounts/123456/priceAccuracyViews/latest
Authorization: Bearer <token>
```

Client boundary:

```ts
interface GoogleTravelPartnerClient {
  getPriceAccuracyView(accountId: string, viewId: string): Promise<PriceAccuracyView>;
  getAccountDiagnostics(accountId: string): Promise<unknown>;
}
```

Keep Google API DTOs out of your dashboard/domain model.

## 14. Price-accuracy feedback loop

Google's structured-data and price-accuracy tooling can help validate submitted pricing against the visible landing experience.

```text
Price submitted
     |
     v
Google displays offer
     |
     v
Landing-page validation
     |
     +--> match
     |
     +--> mismatch
             |
             +--> classify reason
             +--> provider/hotel alert
             +--> freshness policy update
             +--> mapping/rate fix
```

Useful internal mismatch taxonomy:

```text
TAX_MISMATCH
MANDATORY_FEE_MISMATCH
STALE_PRICE
WRONG_ROOM
WRONG_ITINERARY
CONDITIONAL_RATE
LANDING_CONTEXT_LOST
UNAVAILABLE
UNKNOWN
```

## 15. Timeouts, retries and errors

Do not apply one retry policy to every failure.

### Usually retryable

- transient network failure,
- selected 5xx,
- temporary timeout,
- quota/rate-limit conditions with appropriate backoff.

### Usually not blindly retryable

- invalid XML,
- Hotel List ID mismatch,
- schema validation error,
- unsupported currency,
- malformed occupancy,
- invalid landing configuration.

```ts
interface RetryPolicy {
  maxAttempts: number;
  initialBackoffMs: number;
  maxBackoffMs: number;
  retryableStatusCodes: number[];
}
```

Normalize Google failures:

```text
GOOGLE_AUTH
GOOGLE_SCHEMA
GOOGLE_MAPPING
GOOGLE_RATE_LIMIT
GOOGLE_TIMEOUT
GOOGLE_UPSTREAM
GOOGLE_PRICE_REJECTED
GOOGLE_LANDING_ERROR
```

## 16. Idempotency and ordering

Use unique transaction identities and preserve update ordering.

```ts
interface PricingUpdateEnvelope {
  eventId: string;
  hotelId: string;
  version: number;
  sourceUpdatedAt: string;
  generatedAt: string;
}
```

Rule:

```text
older version -> ignore
same version  -> idempotent no-op
new version   -> publish
```

Google's Transaction documentation also gives timestamp ordering semantics, so stale updates should not be allowed to overwrite newer source state in your own pipeline.

## 17. Persistence and audit trail

You do not need to keep every raw XML payload forever, but retain enough lineage for replay and debugging.

```text
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_hash
```

Raw payloads can live behind object-storage references.

## 18. Monitoring and SLOs

### 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 messages.

### Quality

- price accuracy,
- tax/fee mismatch,
- unavailable-after-click,
- landing-context success.

### Commercial

- Google click-to-booking conversion,
- cancellation-adjusted conversion,
- unattributed bookings,
- revenue by freshness bucket.

Example internal SLOs:

```text
mapping coverage                    >= 99%
p95 pricing response               <= internal budget
offers under freshness threshold   >= 97%
severe price mismatch              < 1%
landing context success            >= 99%
```

Calibrate targets to your own supply/account profile.

## 19. Integration test matrix

| Test | Expected behavior |
|---|---|
| 1 night / 2 adults | base happy path |
| 3 nights | correct stay total |
| child occupancy | child context preserved |
| refundable | correct policy |
| non-refundable | correct policy |
| tax-heavy market | correct total |
| mandatory property fee | correct disclosure |
| currency change | correct currency |
| sold out | offer removed |
| price changed | fresh response |
| hotel renamed | stable ID |
| hotel closed | listing state updated |
| mobile landing | context preserved |
| stale cache | correct revalidation/fallback |
| duplicate event | idempotent |

## 20. Go-live checklist

- Hotel Center / partner access ready
- Service account authorized
- Canonical hotel IDs stable
- Hotel List validation complete
- Mapping coverage dashboard available
- Pricing delivery mode selected
- Google Query parser tested
- Transaction serializer schema-valid
- Room/rate semantics normalized
- Tax/fee breakdown correct
- Occupancy/child cases tested
- Changed Pricing/ARI ordering safe
- Landing pages preserve context
- OAuth token refresh works
- Retry taxonomy implemented
- Idempotency implemented
- Price-accuracy dashboard exists
- Alerts available at provider/property level
- Conversion reconciliation ownership clear

## Summary

If you model Google Hotels as “an XML export,” provider-specific behavior quickly leaks across the system.

A stronger design is:

```text
canonical domain
   -> Google adapter
   -> Google XML/API contract
   -> diagnostics
   -> quality feedback
```

Developer success is not just a feed being accepted. Property identity, occupancy, rate semantics, total price and booking context should remain consistent from Google results through the booking engine.


## Companion contract checklist

### Capability boundary

Google Hotels spans distinct property, pricing, landing-page and diagnostic surfaces. Treat each surface as a separate contract boundary rather than assuming one API owns the entire integration.

### Polling and pagination

Pricing XML flows are message-driven rather than generic paginated REST collections. Travel Partner API pagination is endpoint-specific. Do not invent a universal polling interval or generic page loop.

### Evidence boundary

The implementation examples in this guide are **illustrative**. They are based on official public documentation reviewed on 2026-09-26 and are not claimed to have been tested against a live Hotel Center account.
