---
title: "Hotel data scraping"
description: "Hotel data scraping for nightly rates, room types, availability and reviews from Booking.com, Expedia, Google Hotels and brand sites, for rate parity."
canonical: https://www.zenrows.com/use-cases/hotel-data-scraping
updated: 2026-10-18
---

# Hotel data scraping

Hotel data scraping collects nightly rates, room types, availability, taxes and review scores from OTAs, metasearch and hotel brand sites. Zenrows prices each hotel for the check-in dates and market you choose, through protected booking sites, and returns the rates as JSON for rate shopping, parity checks and revenue management.

## Sample output: one hotel, one stay date

```json
{
  "hotel": "Example Harbour Hotel",
  "channel": "ota",
  "source_url": "https://www.example-ota.com/hotel/example-harbour.html",
  "checkin": "2026-11-14",
  "checkout": "2026-11-15",
  "guests": 2,
  "point_of_sale": "us",
  "currency": "USD",
  "rooms": [
    { "room_type": "Standard Double", "rate_plan": "Non-refundable", "price": 189.0, "taxes_fees": 31.2, "rooms_left": 3 },
    { "room_type": "Standard Double", "rate_plan": "Free cancellation", "price": 214.0, "taxes_fees": 35.3, "rooms_left": 3 },
    { "room_type": "Junior Suite", "rate_plan": "Breakfast included", "price": 302.0, "taxes_fees": 49.8, "rooms_left": 1 }
  ],
  "lowest_price": 189.0,
  "review_score": 8.6,
  "review_count": 2140,
  "brand_site_price": 199.0,
  "parity_gap": -10.0,
  "scraped_at": "2026-10-17T05:00:00Z"
}
```

One hotel priced for one night, two guests, from a US visitor. The parity gap compares the OTA's lowest rate with the brand site's rate for the same room and date.

## What hotel data scraping covers

Hotel prices are a matrix: hotel, check-in date, length of stay, guests, room type and rate plan, each per channel and per market. Revenue managers, OTAs and travel startups pick different slices of that matrix.

| Job | Data you collect | Typical sources |
|---|---|---|
| Competitor rate shopping | Lowest rate per competitor hotel for the next 90 to 365 check-in dates | Booking.com, Expedia, Hotels.com, Agoda |
| Rate parity monitoring | Your own rate on each OTA versus your brand site, by room and rate plan | Booking.com, Expedia, Google Hotels, brand.com |
| Metasearch price checks | Which channel shows the lowest price and at what position | Google Hotels, Kayak, Trivago |
| Availability and demand signals | Sold-out dates, rooms left, minimum stay rules | OTA calendars and room tables |
| Hotel content and amenities | Room types, bed configuration, amenities, photos count, policies | OTA property pages, IHG, Accor, Marriott, Four Seasons |
| Guest review tracking | Score, review count, review text and date, management replies | Booking.com reviews, Tripadvisor, Google, HolidayCheck |

## Why hotel price scraping breaks

A hotel page with no dates shows no rate. Once you add dates, guests and a market, the page is assembled from several background calls and guarded by bot protection.

| What breaks | What it looks like | How Zenrows handles it |
|---|---|---|
| Rates load after the page | The room table is empty in the raw HTML or shows a spinner | Adaptive Stealth Mode escalates to browser rendering when the room table needs it, and returns the page once rates have loaded |
| Bot protection on OTAs | CAPTCHA pages and 403s once you price a few hundred hotel-dates | Zenrows' stealth gets through CAPTCHA, Akamai, PerimeterX and similar protections |
| Market-dependent rates | Prices, taxes and member discounts change by visitor country | Geotargeting prices each hotel from the guest's country, so parity checks compare like for like |
| Date matrix volume | 40 hotels times 365 check-in dates is 14,600 requests per refresh, per channel | [Batch](/products/batch) runs the whole matrix as one job and calls your webhook when it ends |
| Layout changes on OTAs | Room names and prices move to new elements and your selectors return nothing | Extract returns rooms and rates without selectors you maintain |

## How to scrape hotel prices with Zenrows

1. **Build the rate matrix.** For each hotel, list the channel URLs and the check-in dates, nights and guests you need. Most OTAs take these as query parameters.
2. **Fetch each hotel and date.** Use Adaptive Stealth Mode with geotargeting for the guest's country, so the page comes back with the room table drawn.
3. **Name the fields.** Map room type, rate plan, price and taxes with CSS selectors, or use automatic structured extraction and normalise later.
4. **Refresh and compare.** Submit the full matrix to Batch on a schedule, then compare each channel's lowest rate with your brand site to find parity gaps.

```python Python
# pip install requests
import json
import requests

# A hotel page with dates and guests in the query string, e.g.
# ...?checkin=2026-11-14&checkout=2026-11-15&group_adults=2
params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": HOTEL_URL,
    "mode": "auto",  # Adaptive Stealth Mode
    "proxy_country": "us",
    "css_extractor": json.dumps({
        "room_type": ".room-table .room-name",
        "rate_plan": ".room-table .rate-conditions",
        "price": ".room-table .price-display",
    }),
}
response = requests.get("https://api.zenrows.com/v1/", params=params)
print(response.json())
```

```javascript Node.js
// Price one hotel for the next 90 check-in dates in a single Batch job
const dates = Array.from({ length: 90 }, (_, i) => {
  const d = new Date(Date.UTC(2026, 10, 1 + i));
  const out = new Date(d.getTime() + 86400000);
  return [d.toISOString().slice(0, 10), out.toISOString().slice(0, 10)];
});

const response = await fetch("https://async.api.zenrows.com/v1/jobs", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_ZENROWS_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    type: "regular",
    status: "closed",
    zenrows_params: { mode: "auto", proxy_country: "us", extract: "auto" },
    tasks: dates.map(([checkin, checkout]) => ({
      url: `HOTEL_URL?checkin=${checkin}&checkout=${checkout}&group_adults=2`,
      external_id: `hotel-1-${checkin}`,
    })),
  }),
});
console.log(await response.json());
```

```bash CLI
npx -y @zenrows/cli init
# Rooms and rates for one stay, priced from the US, as JSON
zenrows extract "HOTEL_URL?checkin=2026-11-14&checkout=2026-11-15&group_adults=2" --autoparse --proxy-country us
# jobs.jsonl: one {"url": "...", "proxy_country": "us"} per hotel-date
zenrows batch create jobs.jsonl --wait
```

```text Claude Code (MCP)
# Connect once
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp

# Then ask:
Use Zenrows to check the price of a Standard Double for 2 adults on 14 November 2026 at these three hotels on Booking.com and on each hotel's own website, from a US visitor. Return a table of hotel, channel, price and taxes, and flag any hotel where Booking.com is cheaper than the brand site.
```

## Which Zenrows product fits

| If you need to | Use | Why |
|---|---|---|
| Price one hotel for one stay | [Fetch](/products/fetch) | Returns the rendered room table from the market you pick, with named fields |
| Read rooms and rates without maintaining selectors | [Extract](/products/extract) | Automatic structured extraction returns room and rate data when OTA layouts change |
| Refresh a full hotel and date matrix every day | [Batch](/products/batch) | Thousands of hotel-date URLs as one job, results by webhook |
| Let a revenue analyst ask for rates in plain English | [MCP Server](/mcp) | Claude, Cursor and other MCP clients call Zenrows to fetch and compare rates |

## What success looks like

| Measure | What good looks like | How to check it |
|---|---|---|
| Rate accuracy | Stored rates match the price a guest sees for the same hotel, dates, guests and market | Spot-check a sample of hotel-dates by hand after each release of your pipeline |
| Matrix completeness | Every hotel, date and channel cell has a rate or an explicit sold-out flag | Count empty cells per channel per run |
| Like-for-like parity | Parity gaps compare the same room type, rate plan and tax treatment | Review gaps above your threshold and check the room mapping |
| Refresh lead time | The full matrix lands before the revenue team's daily pricing meeting | Log job completion time against the meeting time |
| Retries per run | Few hotel-dates need a second attempt, and none stay empty after it | Count failed tasks per Batch job and the cells still empty after the retry |

## Hotel data scraping FAQ

### How do I scrape hotel prices from Booking.com?

Build the hotel page URL with check-in, check-out and guest parameters, then send it to Fetch with Adaptive Stealth Mode and geotargeting for the market you are pricing. Read room type, rate plan and price with CSS selectors or automatic structured extraction. For many dates, send the URLs to Batch.

### What is hotel rate shopping?

Rate shopping is the daily collection of competitor hotels' public rates for upcoming dates, used by revenue managers to set their own prices. A typical setup prices a compset of five to fifteen hotels for every check-in date in the next few months, on one or two OTAs, and stores the lowest rate per date.

### How does rate parity monitoring work?

You price your own hotel on each OTA and on your brand site for the same room, rate plan, dates, guests and market, then compare. A gap means an OTA is undercutting your direct rate, often through a wholesaler or a member discount. Scraping from several countries with geotargeting shows gaps that only appear in some markets.

### Can I scrape Google Hotels?

Yes. Adaptive Stealth Mode renders the page so the prices load, and geotargeting sends the request from the guest's country. The page lists the channels selling each hotel and their prices for your dates, which makes it a fast first check for parity before you price each OTA directly.

### How do I scrape hotel reviews along with prices?

Review pages sit on separate URLs from the rate pages on most OTAs and on Tripadvisor. Collect the score and review count from the property page with each price run, and fetch the review pages weekly for text, date, traveller type and management replies. Key both by hotel so a rate change can be read next to a shift in review score.

### Is there a hotel price API I can use instead?

OTA partner and affiliate APIs exist, but they cover the provider's own inventory, often need a commercial agreement and may not show the price a guest sees. Zenrows works as a hotel price API across channels: you send the page URL with dates, it returns the rendered rates or JSON.

### How often should hotel rates be refreshed?

A common cadence is daily for the next 30 to 90 days and weekly for the rest of the year. Around events or when a competitor changes strategy, near-term dates may be priced several times a day. With Batch, a faster cadence only means submitting the same job more often.

## Related use cases

- [Travel data scraping](https://www.zenrows.com/use-cases/travel-data-scraping.md): Fares, room rates, car hire quotes, tour prices and reviews from airlines, OTAs and travel marketplaces.
- [Short-term rental data](https://www.zenrows.com/use-cases/short-term-rental-data.md): Nightly rates, fees, calendars and reviews from Airbnb, Vrbo and Booking.com listings, per market.
- [Review scraping](https://www.zenrows.com/use-cases/review-scraping.md): Review text, ratings, dates and replies from Amazon, Google, Yelp, Trustpilot and G2, every page.
- [Dynamic pricing data](https://www.zenrows.com/use-cases/dynamic-pricing.md): Live competitor price, stock and demand inputs for a repricing engine, on demand or on a schedule.
- [Competitor price scraping](https://www.zenrows.com/use-cases/price-scraping.md): Competitor prices, promotions, shipping and stock per SKU from retailers and marketplaces, refreshed on your schedule.
- [Flight price scraping](https://www.zenrows.com/use-cases/scraping-flight-prices.md): Fares, fare classes and award seats by route and date from airline sites and OTAs.

Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
