Booking.com Rate & Availability Event Monitor avatar

Booking.com Rate & Availability Event Monitor

Under maintenance

Pricing

from $1.31 / 1,000 rate events

Go to Apify Store
Booking.com Rate & Availability Event Monitor

Booking.com Rate & Availability Event Monitor

Under maintenance

**Monitor hotel prices and availability on Booking.com β€” emit only acted-on events.** Detects price_drop, price_spike, sold_out, re_opened, rooms_left_low, new_listing, and Booking-vs-Kayak parity violations (β‰₯5%). Stateful: remembers last-seen prices across runs via named KV store.

Pricing

from $1.31 / 1,000 rate events

Rating

0.0

(0)

Developer

Vitalii Bondarev

Vitalii Bondarev

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

2

Monthly active users

9 days ago

Last modified

Share

Monitor hotel prices and availability on Booking.com and detect changes that matter β€” price drops, spikes, sold-out inventory, re-openings, and Booking-vs-Kayak parity violations. Emits only acted-on events, not raw snapshots. Pay per event.

What this actor does

Most Booking.com scrapers return snapshots: "here are 100 hotels right now." You have to diff them yourself to find what changed. This actor does that diffing for you across runs.

It keeps a named KV-store memory across runs and emits exactly one dataset record per meaningful change:

EventMeaning
price_dropPrice fell by β‰₯ your threshold (default 5%) since last run
price_spikePrice rose by β‰₯ your threshold since last run
sold_outProperty was available, now shows sold-out
re_openedProperty was sold out, inventory just reappeared
rooms_left_lowRooms remaining crossed below your threshold (default 3)
new_listingProperty seen for the first time in this search
parity_gapBooking.com price differs from Kayak by β‰₯ 5% (contractual violation signal)

Use cases

Revenue management: Track competitor repricing in real time. Get notified when a competing property drops price or sells out β€” react before occupancy shifts.

AI agents / price bots: Feed structured events into downstream automation. Each event has all fields needed to act on it (property, date, old price, new price, delta %).

Rate parity compliance: Hotel chains and OTAs have contractual rate parity obligations. The parity_gap event flags Booking-vs-Kayak discrepancies β‰₯ 5% β€” the standard contractual threshold.

Demand signal tracking: A cluster of sold_out events across a destination on a specific date is a demand signal. A cluster of rooms_left_low events signals scarcity before the sold-out wave.

Input

Monitor specific hotel URLs (AvailabilityCalendar path)

Provide hotel page URLs directly. The actor uses Booking.com's AvailabilityCalendar GraphQL operation β€” cheap (approximately 6 requests per hotel per 365-day window) and precise.

{
"propertyUrls": [
"https://www.booking.com/hotel/fr/paris-marriott-rive-gauche.html",
"https://www.booking.com/hotel/de/four-seasons-berlin.html"
],
"checkin": "2026-07-01",
"amountOfDays": 61,
"proxyConfiguration": {"useApifyProxy": true, "groups": ["RESIDENTIAL"]}
}

Monitor a destination search (FullSearch path)

Provide destination + date pairs. The actor paginate Booking.com's FullSearch results (up to 1,000 hotels per search) and tracks all of them.

{
"searches": [
{"destination": "Paris", "checkin": "2026-07-01", "checkout": "2026-07-03"},
{"destination": "Amsterdam", "checkin": "2026-08-15", "checkout": "2026-08-17"}
],
"dropPctThreshold": 5.0,
"spikePctThreshold": 5.0,
"includeParityCheck": true,
"proxyConfiguration": {"useApifyProxy": true, "groups": ["RESIDENTIAL"]}
}

Output

Each dataset record is one event. Example price_drop:

{
"event_type": "price_drop",
"property_id": "12345",
"property_name": "Hotel de Crillon",
"date": "2026-07-04",
"currency": "USD",
"prev_price": 520.0,
"curr_price": 460.0,
"change_pct": -11.54,
"detected_at": "2026-06-04T14:32:01+00:00",
"run_id": "abc123run"
}

Example parity_gap:

{
"event_type": "parity_gap",
"property_id": "99001",
"property_name": "Hotel Parisien",
"date": "2026-07-01",
"currency": "USD",
"booking_price": 185.0,
"kayak_price": 220.0,
"gap_pct": 15.91,
"direction": "booking_cheaper",
"detected_at": "2026-06-04T14:32:05+00:00",
"run_id": "abc123run"
}

Proxy requirement

Residential proxy is mandatory. Booking.com uses AWS WAF Bot Control β€” datacenter IPs are blocked on every endpoint including the GraphQL API. This actor handles the WAF challenge automatically, but it requires a real residential IP to get through.

Configure via proxyConfiguration input:

{"useApifyProxy": true, "groups": ["RESIDENTIAL"]}

The buyer pays proxy costs (Apify Residential) separately from PPE event charges.

Access architecture

This actor uses an HTTP/session rung where useful and escalates to its own rendering browser when the page requires it:

  1. HTTP/session rung β€” The actor uses direct HTTP requests and session state for the parts of the flow that can be handled reliably without rendering, including the GraphQL and CSRF bootstrap where applicable.

  2. Rendering rung β€” When Booking.com requires browser execution, the actor launches its own Camoufox rendering browser on the caller's Apify RESIDENTIAL proxy. Real JavaScript execution clears the mp_verify challenge. This does not depend on a managed or publisher-paid browser and carries $0 external COGS for the browser itself.

Success is determined from the real rendered page β€” including the expected selectors and page content β€” rather than from an HTTP 202 response or an awswaf substring. Those indicators may remain present even after the real page has rendered successfully, so they are not sufficient success or failure signals.

The sibling booking-scraper proved this HTTP/session-plus-own-browser pattern. This actor's page and selector flow still requires its launching live-cloud gate before that flow can be treated as validated in production.

The CSRF layer remains independently bootstrapped when needed: after the usable Booking page is available, the actor extracts b_csrf_token and X-Booking-Context-Aid, then uses those headers for subsequent GraphQL POSTs. Challenge or authentication failures during a run trigger the appropriate retry or re-bootstrap, while rendered selectors and content remain the success criteria.

Schema-drift canary: enable runCanary: true in your daily scheduled run to get early warning if Booking rotates either layer. The canary asserts gokuProps format, CSRF regex, and operationNames β€” logs a MAINTENANCE ALERT before the health test goes red.

Scheduling

This actor is designed for scheduled runs. Set it to run daily (or hourly for high-demand dates) on a schedule. Between runs, state is preserved in a named KV store (booking-rate-monitor-state) on your Apify account.

Run status

Each run persists a RUN_STATUS key in the default key-value store. It contains: searchesTotal, searchesEmpty, challengePages, csrfMissing, propertyCardMissing, transportErrors, deliveredRecords, coverageComplete, and lastFailureClass.

lastFailureClass distinguishes transport errors, challenge pages for a protected target, missing CSRF state, missing property cards, and parsed-empty responses.

Pricing

Pay per event emitted β€” not per hotel, not per snapshot. A run that detects no changes costs only the actor-start compute fee. Runs with 50 price events are charged for 50 events.

Price: see actor listing (per-event rate, PPE model).

Limitations

  • Maximum 1,000 hotels per destination search (Booking.com FullSearch cap). Use bounding boxes or multiple searches for full-city coverage.
  • Availability calendar does not return rooms_left β€” only the FullSearch path surfaces that field.
  • Kayak parity check is best-effort: name-matching between Booking and Kayak properties is approximate. Exact property ID matching requires a Kayak API that is not publicly available.
  • Expedia parity is NOT supported (advanced protection, requires a separate approach).
  • This actor runs on the buyer's Apify account. Booking.com's Terms of Service require compliance with their robots.txt and fair-use policies. Do not use to violate rate parity agreements or resell raw Booking.com data.

Integrations

Built for revenue managers and travel-tech teams detecting price drops, sell-outs, and OTA parity violations β€” the JSON/dataset output drops into the tools you already run, no glue code:

  • n8n / Make / Zapier β€” trigger a run or pipe every new dataset item into 500+ apps (Google Sheets, Airtable, Slack, HubSpot, your database) with no code: n8n, Make, Zapier.
  • Webhooks β€” fire your own endpoint the moment a run finishes, to push results straight into your pipeline (docs).
  • MCP server β€” expose this actor as a tool to Claude, Cursor, or any MCP client so an AI agent can pull this data mid-conversation (guide).
  • API & SDKs β€” fetch the dataset as JSON, CSV, or Excel through the Apify REST API or the Python / JS SDKs.

See all Apify integrations.

Usage statistics

This Actor creates a small, content-free summary at the end of each run. It is used only to monitor reliability and improve this Actor. A copy is saved as USAGE_STATS in your own Apify key-value store, so you can see the exact record created for your run.

Set disableUsageStats to true in the input to opt out. Nothing is sent then; your USAGE_STATS record only says that statistics were disabled.

Only these fields are recorded:

  • schema version, Actor name and build number;
  • UTC start and finish hour (not a precise timestamp);
  • run duration, number of results and time to the first result, each as a coarse range;
  • whether the result was empty, the end status, and an error type from a fixed list;
  • memory setting and counts of charged events;
  • names of the input fields you set, never their values;
  • the selected option for input fields that offer a fixed list of choices (for example a sort order).

We do not collect input text, search terms, URLs, domains, usernames, email addresses, names, proxy credentials, tokens, scraped records, output items, raw error messages, stack traces, or hashes of any of those values. Records are kept for no longer than 13 months, used only as aggregated operational statistics, and never sold or shared.

Additional fields (Phase 2)

This Actor also records your Apify user ID, whether Apify marks the account as paying, the size range of list inputs, the selected country when the input offers a fixed list of countries, and one category from a fixed Actor taxonomy. We use these fields only for aggregate reliability, repeat-use and cross-Actor analysis; reports suppress any cell with fewer than five distinct users.

The same disableUsageStats: true input flag turns these fields off too. The user ID is removed after 13 months; we do not export, sell, share, or attempt to re-identify this data.

Run-outcome signals (v2)

To learn whether a run did what it was asked to do, the record also holds a few more coarse ranges and yes/no flags. None of them contains content:

  • the result limit you asked for (a range, when the input has one) and what share of it was delivered;
  • results delivered per input item you listed (a range);
  • output quality as ranges: how fully the result fields were filled, the share of rows that look like errors, the share of duplicate rows, and how many different fields appeared. These are counted in memory while results are saved; no result content is kept;
  • how the run was started (console, API, schedule, webhook, another Actor);
  • how it ended: stopped by you, timed out, reached the requested limit, stopped by the charge limit, and how many times the platform moved the run;
  • if this Actor reports it: how many items to process worked or failed (ranges) and one failure reason from a fixed list;
  • a short code made from the names of the input fields you set, never their values.

Repeat-run fingerprint (v2)

When your Apify user ID is recorded (see above), the record also holds an 8-character one-way code made from your input (proxy settings left out) and this Actor's name. It only lets us see that the same account ran the same input again soon after an unsatisfying run; we never see the input itself. It is stored only in the database, never published, and reports use it in aggregate with the same five-user minimum. It is the one exception to the statement above that no hashes are collected, and disableUsageStats: true turns it off.