This guide outlines how to tag your website with microdata and navigation attributes so Google's price accuracy crawler can automatically navigate your booking funnel and validate your room rates.
Conceptual crawler flow
The following sequence illustrates how the crawler navigates a typical booking funnel, handling dynamic loading states and identifying critical actions.
🗺️ Quick flow overview
- 🔗 Phase 1: Google surface — Crawler accesses the Dynamic Landing Page URL (configured on Google based on your template), landing on your site just as a real user would.
- 🛬 Phase 2: Landing page — Crawler lands on the partner site
(
data-nav-stage="landing"). - 🖱️ Phase 3: Navigation — Crawler executes
mandatory interactions (clicks, selections) declared
using
data-nav-*attributes. - 🏁 Phase 4: Final page — Crawler reaches the
final accessible page
(
data-nav-stage-final="true"). - 📊 Phase 5: Price extraction — Crawler extracts the microdata-tagged price and validates total stay cost.
Step 1: landing page (hotel selection or optional upsell page)
The crawler visits the landing page URL (the page where the user lands on the partner site after clicking on Google items). For details on configuring landing page parameters to ensure the correct rate is selected, see variables and conditions.
State A: loading screen
A loading indicator mask is transiently visible. The crawler waits for this nav tag to disappear (max 15 seconds).
<body data-nav-stage="landing">
<!-- Loading indicator: Crawler waits for this to disappear (max 15s) -->
<div data-nav-loading-indicator="true">Loading Page...</div>
</body>
State B: page loaded
Once the loading indicator disappears, the page content is loaded. The crawler locates and clicks the critical path element to advance.
<body data-nav-stage="landing">
<!-- Ignored: Crawler skips non-selected rates -->
<div class="hotel-result">
<h3>Grand Hotel - Basic Room</h3>
<button>View Rates</button>
</div>
<!-- critical path: crawler clicks this to advance -->
<div class="hotel-result selected-rate">
<h3>Grand Hotel - Deluxe Suite</h3>
<button data-nav-criticalpath="true"
data-nav-interactiontype="CLICK"
data-nav-interactionorder="1">
View Rates
</button>
</div>
</body>
Step 2: optional intermediate page (room or option selection)
The crawler arrives at the rate selection page.
State A: loading screen
A spinner appears while rates are fetched.
<body data-nav-stage="room-selection">
<div data-nav-loading-indicator="true">Fetching Rates...</div>
</body>
State B: page loaded
After loading finishes, the crawler might interact with multiple elements in sequence before proceeding. Only mandatory interactions (e.g., agreeing to mandatory terms or selecting required configurations based on user selection on Google or min mandatory price path) should be tagged.
<body data-nav-stage="room-selection">
<!-- Step 1: mandatory interaction (e.g., select bed configuration) -->
<div class="rate-option">
<p>Deluxe Suite</p>
<!-- Note: Either the value selected by the user on Google should be pre-selected,
or navigation elements must be provided so the crawler can select it. -->
<div class="bed-selection">
<button data-nav-criticalpath="true"
data-nav-interactiontype="CLICK"
data-nav-interactionorder="1">
Select Queen Bed
</button>
<button>Select King Bed</button>
</div>
</div>
<!-- Step 2: mandatory interaction (e.g., mandatory insurance opt-out or agreement) -->
<div class="mandatory-notice">
<label>
<input type="checkbox"
data-nav-criticalpath="true"
data-nav-interactiontype="CLICK"
data-nav-interactionorder="2">
I agree to the non-refundable dynamic pricing waiver.
</label>
</div>
<!-- Step 3: Continue to checkout -->
<button data-nav-criticalpath="true"
data-nav-interactiontype="CLICK"
data-nav-interactionorder="3">
Continue Reserving
</button>
</body>
Step 3: final crawling page (price validation / booking summary)
The crawler reaches the final accessible page (the final page before user
personal or payment information entry). It verifies the data-nav-stage-final
flag is true. This signals the crawler to extract the microdata-tagged price
after any remaining navigations. Navigation tags are always interacted with
first before extracting the price.
Main item scopes should be a cotype of Hotel and LodgingReservation (e.g.,
itemtype="https://schema.org/Hotel https://schema.org/LodgingReservation").
For backward compatibility, we also accept a parent Hotel scope that contains
a child element cotyped as both Offer and LodgingReservation (for example,
<div itemprop="makesOffer" itemscope itemtype="https://schema.org/Offer
https://schema.org/LodgingReservation">). Only one Hotel and Offer under
that Hotel should be tagged on the page.
This example shows only the mandatory total price tagging, recommended for simplicity. {Simple Total Price}
<!-- End of Scope comments identify container boundaries --> <body data-nav-stage="checkout" data-nav-stage-final="true"> <!-- Booking Summary Cart Scope (Hotel + LodgingReservation) --> <div class="booking-summary" itemscope itemtype="https://schema.org/Hotel https://schema.org/LodgingReservation"> <!-- Hotel Identifier (Can be hidden meta tag) --> <meta itemprop="identifier" content="grand-hotel-001" /> <h2 itemprop="name">Grand Hotel</h2> <!-- Visible Stay Dates & Occupancy (checkinDate / checkoutDate are also acceptable) --> <p> Check-in: <span itemprop="checkinTime" content="2026-08-10">Aug 10, 2026</span> | Check-out: <span itemprop="checkoutTime" content="2026-08-15">Aug 15, 2026</span> | Guests: <span itemprop="numAdults">2</span> Adults, <span itemprop="numChildren">1</span> Child </p> <!-- Offer Scope --> <div class="cart-details" itemprop="makesOffer" itemscope itemtype="https://schema.org/Offer"> <!-- Compound Price Scope --> <div itemprop="priceSpecification" itemscope itemtype="https://schema.org/CompoundPriceSpecification"> <div class="total-line"> <span>Total Stay (Taxes Included):</span> <!-- The crawler extracts this final rate as total price --> <span itemprop="price">440.00</span> <meta itemprop="priceCurrency" content="USD" /> </div> </div> <!-- End of CompoundPriceSpecification Scope --> </div> <!-- End of Offer Scope --> </div> <!-- End of Hotel/LodgingReservation Scope --> </body>This example shows the total price alongside itemized breakdowns (Base Rate, Fees). {Total Price + Breakdown}
<!-- End of Scope comments identify container boundaries --> <body data-nav-stage="checkout" data-nav-stage-final="true"> <!-- Booking Summary Cart Scope (Hotel + LodgingReservation) --> <div class="booking-summary" itemscope itemtype="https://schema.org/Hotel https://schema.org/LodgingReservation"> <!-- Hotel Identifier (Can be hidden meta tag) --> <meta itemprop="identifier" content="grand-hotel-001" /> <h2 itemprop="name">Grand Hotel</h2> <!-- Visible Stay Dates & Occupancy (checkinDate / checkoutDate are also acceptable) --> <p> Check-in: <span itemprop="checkinTime" content="2026-08-10">Aug 10, 2026</span> | Check-out: <span itemprop="checkoutTime" content="2026-08-15">Aug 15, 2026</span> | Guests: <span itemprop="numAdults">2</span> Adults, <span itemprop="numChildren">1</span> Child </p> <!-- Offer Scope --> <div class="cart-details" itemprop="makesOffer" itemscope itemtype="https://schema.org/Offer"> <!-- Compound Price Scope --> <div itemprop="priceSpecification" itemscope itemtype="https://schema.org/CompoundPriceSpecification"> <div class="line-item"> <span>Base Rate (2 Nights):</span> <!-- Unit Price Scope for Base --> <span itemprop="priceSpecification" itemscope itemtype="https://schema.org/UnitPriceSpecification"> <span itemprop="price">400.00</span> <meta itemprop="priceCurrency" content="USD" /> </span> <!-- End of UnitPriceSpecification Scope --> </div> <div class="line-item"> <span>Resort Fee:</span> <!-- Unit Price Scope for Fee --> <span itemprop="priceSpecification" itemscope itemtype="https://schema.org/UnitPriceSpecification"> <span itemprop="price">40.00</span> <meta itemprop="priceCurrency" content="USD" /> </span> <!-- End of UnitPriceSpecification Scope --> </div> <div class="total-line"> <span>Total Stay:</span> <!-- The crawler extracts this final rate as total price --> <span itemprop="price">440.00</span> <meta itemprop="priceCurrency" content="USD" /> </div> </div> <!-- End of CompoundPriceSpecification Scope --> </div> <!-- End of Offer Scope --> </div> <!-- End of Hotel/LodgingReservation Scope --> </body>
1. Navigation implementation (data-nav-*)
Use custom data-nav-* HTML attributes to annotate interactive elements and
declare funnel stages so the crawler can traverse your booking flow.
1.1 Top-of-page stage declaration (data-nav-stage)
Every page in your booking funnel that displays rates should declare its stage,
attached to the <body> tag or a primary wrapper tag (such as <div>,
<section>, or <main>). This tells the crawler which stage of the booking
flow is active and assists with troubleshooting issues later on.
data-nav-stage(Recommended): Declares the active funnel stage. While not strictly required, specifying names (for example,"landing","room-selection","checkout","ready-for-payment") helps identify the active stage during validation.
1.2 Final stage indicator (data-nav-stage-final)
On the final page displaying prices prior to user personal or payment
information entry, add the final-stage flag to your top-of-page container
alongside a stage declaration (for example,
data-nav-stage="ready-for-payment").
data-nav-stage-final(Required for price validation pages): Set to"true"(data-nav-stage-final="true") on the final page of the booking funnel that crawler can access . This signals that after all required navigation steps are complete, the crawler should read the price from this page.
1.3 Critical path interaction attributes
On intermediate pages, tag the interactive elements (buttons or links) that advance the user along the booking critical path. If multiple actions are required on the same page, specify the execution order. The crawler follows the specified order before attempting to extract the price on the final page.
| Attribute | Required Value | Purpose |
|---|---|---|
data-nav-criticalpath
|
"true"
|
Marks the mandatory button/link the crawler must click to advance. |
data-nav-interactiontype
|
"CLICK"
|
Declares the
interaction action.
CLICK is for
buttons/links. |
data-nav-interactionorder
|
"1", "2", ...
|
Declares the execution sequence if multiple actions are required on the same page. |
data-nav-loading-indicator
|
"true"
|
Marks an element that indicates a loading state (e.g., spinner). The crawler waits for this element to disappear before proceeding. |
Minimal navigation example
<button data-nav-criticalpath="true"
data-nav-interactiontype="CLICK"
data-nav-interactionorder="1">
Continue
</button>
2. Price declaration (microdata)
On the final accessible page of your booking funnel (prior to personal or payment information entry), tag the price offering (one price offer) with microdata tags. Only one offer should be tagged on the page, indicating which rate the crawler extracts, corresponding to the rate selected on Google before the user lands on your website. For details on defining landing page parameters, see variables and conditions.
While Google fully supports the complete hierarchy in the hotel price structured data reference
(including itemized base rates, individual taxes, fees, and room details), Google also accepts a simplified structured data tree to make it easier for you to tag hotel prices on your webpages.
The total stay price is mandatory for available rates. If an itinerary or room
is unavailable or sold out, declare https://schema.org/SoldOut under
makesOffer without a priceSpecification:
<div itemprop="makesOffer" itemscope itemtype="https://schema.org/Offer">
<meta itemprop="availability" content="https://schema.org/SoldOut"/>
Sold Out
</div>
For more information, see the
availability property
specification.
2.1 Simplified structured data tree
To simplify tagging on your booking pages, you only need to declare the root
hotel and reservation container, stay dates (YYYY-MM-DD), occupancy, and a
single offer containing the total stay price:
Hotel / LodgingReservation (Root Container)
├── identifier [Meta/Attribute] Hotel ID
├── name [Visible text] Hotel Name
├── checkinTime / checkoutTime [Visible text with YYYY-MM-DD in content="..."] Stay Dates
├── numAdults / numChildren [Visible text] Occupancy
└── makesOffer (Offer)
└── priceSpecification (CompoundPriceSpecification)
├── priceCurrency [ISO Code, such as "USD"] Currency
└── price [Visible text] Total Stay Price (Taxes & Fees Included)
2.2 Critical visibility rules
To ensure reliable price extraction, follow these visibility rules when tagging your HTML:
- Total stay price must be visible: Tag the visible price number directly
on the page using
itemprop="price". Don't use hidden metadata (<meta>) or attribute values (value="..."orcontent="...") for the price number. - Machine-readable stay dates: Tag visible stay dates on the page as much
as possible. If needed, you can use the
content="..."attribute on<span>elements to provide a standardYYYY-MM-DDformat while displaying the visible date (for example,<span itemprop="checkinTime" content="2026-08-10">August 10, 2026</span>;checkinDateis also acceptable). - Non-visible fields: Fields that aren't displayed directly on the page
(such as the hotel ID or currency code) can use
<meta>tags orcontent="..."attributes.
3. Examples
The following end-to-end examples show how to tag intermediate funnel steps and final checkout pages with navigation attributes and microdata.
Example 1: intermediate page with clickable button
The following snippet demonstrates an intermediate room selection page
(room-selection) with a single critical path action button tagged to advance
the flow:
<body data-nav-stage="room-selection">
<!-- Main page content -->
<button class="btn-continue"
data-nav-criticalpath="true"
data-nav-interactiontype="CLICK"
data-nav-interactionorder="1">
Book This Room
</button>
<!-- Rest of the Main page content -->
</body>
Example 2: final price page with stage indicator and minimal microdata
The following snippet demonstrates the last page before payment
(ready-for-payment) where data-nav-stage-final="true" is declared on the
wrapper and the authoritative stay price is visibly exposed:
<body data-nav-stage="ready-for-payment" data-nav-stage-final="true">
<!-- Root Container: Co-Typed Hotel & LodgingReservation -->
<div itemscope itemtype="https://schema.org/Hotel https://schema.org/LodgingReservation">
<!-- Hotel Identifier (Can be hidden meta tag) -->
<meta itemprop="identifier" content="grand-view-001" />
<h1 itemprop="name">Grand View Hotel</h1>
<!-- Visible Stay Dates & Occupancy -->
<p>
Check-in: <span itemprop="checkinTime" content="2026-08-10">Aug 10, 2026</span> |
Check-out: <span itemprop="checkoutTime" content="2026-08-15">Aug 15, 2026</span> |
Guests: <span itemprop="numAdults">2</span> Adults, <span itemprop="numChildren">1</span> Child
</p>
<!-- Single Minimal Rate Offer -->
<div itemprop="makesOffer" itemscope itemtype="https://schema.org/Offer">
<div itemprop="priceSpecification" itemscope itemtype="https://schema.org/CompoundPriceSpecification">
<p class="summary-total">
Total Stay Price (Taxes & Fees Included):
<!-- 3-letter ISO Currency Code (can use content="" for hiding the 3-letter currency code while displaying a symbol) -->
<span itemprop="priceCurrency" content="EUR">€</span>
<!-- VISIBLE TOTAL STAY PRICE (Directly wrapped, no hidden attributes) -->
<span itemprop="price">650.00</span>
</p>
</div>
</div>
</div>
</body>
4. Verify your implementation
Before deploying your changes, test your markup and navigation tagging to ensure the crawler can successfully validate your pricing data.
4.1 Verify price and microdata structure
You can use the
Schema.org Validator or
Google Rich Results Test
to inspect the microdata extracted from your page URL or HTML content. Verify
the extracted fields against the specification in the
hotel price structured data reference
, and ensure that your itemprop="price" element is a visible item on the page.
4.2 Verify navigation
To verify your tags, open Browser Developer Tools in Google Chrome by
right-clicking your webpage and selecting Inspect (or pressing F12) to
examine the HTML DOM. Find elements with data-nav-* attributes.
Confirm that your implementation meets the following navigation requirements:
- Top-of-page stage is declared: Every page in the booking flow includes
data-nav-stage="..."on its<body>or root wrapper tag, and the final price page includesdata-nav-stage-final="true". - Critical path is complete: Intermediate pages have their interactive
elements (buttons or links) marked with
data-nav-criticalpath="true",data-nav-interactiontype, anddata-nav-interactionorder. If multiple actions are present, ensure order numbers are set correctly.
4.3 Verify visibility and markup quality
Confirm that your microdata satisfies all structure and visibility requirements (for details, see simplified structured data tree and critical visibility rules):
- Strict hierarchy: Ensure your markup follows the required nesting order (either the simplified tree or the full structured data reference specification). Out-of-order or improperly nested elements might be missed by the crawler.
- Visible price:
itemprop="price"directly wraps the visible price number without hidden attributes or<meta>tags. - Machine-readable stay dates:
checkinTimeandcheckoutTime(or alternativelycheckinDateandcheckoutDateattributes) contain standardYYYY-MM-DDdate format (optionally usingcontent="..."attributes if needed). If a full time (e.g.2027-08-21T23:59:59or similar) is used, the date partYYYY-MM-DDin beginning is only extracted. - Direct element tagging: All visible properties (hotel name, stay dates, occupancy) wrap visible page text.