# Booking.com Rate & Availability Event Monitor (`bovi/booking-rate-monitor`) Actor

**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.

- **URL**: https://apify.com/bovi/booking-rate-monitor.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.31 / 1,000 rate events

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Booking.com Rate & Availability Event Monitor

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:

| Event | Meaning |
|---|---|
| `price_drop` | Price fell by ≥ your threshold (default 5%) since last run |
| `price_spike` | Price rose by ≥ your threshold since last run |
| `sold_out` | Property was available, now shows sold-out |
| `re_opened` | Property was sold out, inventory just reappeared |
| `rooms_left_low` | Rooms remaining crossed below your threshold (default 3) |
| `new_listing` | Property seen for the first time in this search |
| `parity_gap` | Booking.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.

```json
{
  "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.

```json
{
  "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`:

```json
{
  "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`:

```json
{
  "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:

```json
{"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.

### 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](https://docs.apify.com/platform/integrations/n8n), [Make](https://docs.apify.com/platform/integrations/make), [Zapier](https://docs.apify.com/platform/integrations/zapier).
- **Webhooks** — fire your own endpoint the moment a run finishes, to push results straight into your pipeline ([docs](https://docs.apify.com/platform/integrations/webhooks)).
- **MCP server** — expose this actor as a tool to Claude, Cursor, or any [MCP client](https://mcp.apify.com) so an AI agent can pull this data mid-conversation ([guide](https://blog.apify.com/how-to-use-mcp/)).
- **API & SDKs** — fetch the dataset as JSON, CSV, or Excel through the Apify REST API or the Python / JS SDKs.

See all [Apify integrations](https://apify.com/integrations).

# Actor input Schema

## `propertyUrls` (type: `array`):

List of Booking.com hotel page URLs to monitor for price and availability changes. Example: \['https://www.booking.com/hotel/fr/paris-marriott.html']. Each URL must contain a valid Booking.com hotel ID. Monitored via AvailabilityCalendar GraphQL API — cheap (~6 requests per property per 365-day window).

## `searches` (type: `array`):

List of destination+date search configurations to monitor for inventory events. Each item must have 'destination' (city or region name), 'checkin' (YYYY-MM-DD), and 'checkout' (YYYY-MM-DD). Example: \[{"destination": "Paris", "checkin": "2026-07-01", "checkout": "2026-07-03"}]. Uses Booking.com FullSearch GraphQL — up to 1,000 results per search.

## `checkin` (type: `string`):

Default check-in date for property URL monitoring in YYYY-MM-DD format. Example: '2026-07-01'. Used as the start of the AvailabilityCalendar window. Individual searches in the 'searches' field can override this per search.

## `checkout` (type: `string`):

Default check-out date for destination search monitoring in YYYY-MM-DD format. Example: '2026-07-03'. Must be after checkin. Individual searches in the 'searches' field can override this per search.

## `amountOfDays` (type: `integer`):

Number of days to scan in the AvailabilityCalendar for each property URL. Default 61 covers ~2 months in ~1 API call. Set up to 365 for a full year (splits into ~6 API calls per property). Higher values cost more compute but give wider price monitoring coverage.

## `proxyConfiguration` (type: `object`):

Apify proxy configuration for reliable access. REQUIRED for production use — Booking.com is not reliable from datacenter IPs. Use Apify Residential proxy (useApifyProxy: true, groups: \['RESIDENTIAL']). The buyer pays proxy costs separately from PPE event charges. Without residential proxy, the actor will be rate-limited on every run.

## `dropPctThreshold` (type: `number`):

Minimum percentage price decrease to trigger a 'price\_drop' event. Default 5.0 means a 5% or larger drop emits an event. Smaller values (e.g. 2.0) are more sensitive and emit more events; larger values (e.g. 10.0) only catch significant drops. Revenue managers typically use 5–10%.

## `spikePctThreshold` (type: `number`):

Minimum percentage price increase to trigger a 'price\_spike' event. Default 5.0. Useful for monitoring competitors repricing upward, which can indicate demand increases or inventory scarcity. Setting this lower (e.g. 3.0) gives earlier warning of price trends.

## `lowRoomsThreshold` (type: `integer`):

Emit a 'rooms\_left\_low' event when a property has this many rooms or fewer remaining. Default 3. Only emits once when rooms\_left crosses below the threshold (not every run). Set to 0 to disable low-rooms alerts.

## `parityThresholdPct` (type: `number`):

Minimum Booking.com vs Kayak price gap percentage to trigger a 'parity\_gap' event. Default 5.0 matches standard rate parity contract terms. Hotel chains and OTA compliance teams typically flag gaps of 5% or more as contract violations. Set higher to reduce noise on volatile markets.

## `includeParityCheck` (type: `boolean`):

When true, fetches Kayak prices for each destination and emits 'parity\_gap' events when Booking.com and Kayak prices differ by parityThresholdPct or more. Default true. Kayak is fetched without residential proxy (no extra protection). Disable if you only need Booking-internal price/availability events.

## `maxSearchResults` (type: `integer`):

Maximum number of hotels to process per destination search. Default 100. Booking.com FullSearch returns up to 1,000 results total (25 per page); higher values increase compute cost and API calls. For narrow monitoring (a single hotel's destination), 25–50 is usually sufficient.

## `runCanary` (type: `boolean`):

When true, runs a schema-drift canary check at startup that asserts the WAF gokuProps format, b\_csrf\_token regex, and GraphQL operationNames still resolve. Emits a maintenance warning if any check fails, but does NOT abort the run. Recommended: enable in scheduled daily runs to get early warning before the actor goes red.

## Actor input object example

```json
{
  "amountOfDays": 61,
  "dropPctThreshold": 5,
  "spikePctThreshold": 5,
  "lowRoomsThreshold": 3,
  "parityThresholdPct": 5,
  "includeParityCheck": true,
  "maxSearchResults": 100,
  "runCanary": false
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset containing Booking Rate Monitor records (event\_type, property\_name, date, currency, prev\_price, curr\_price, change\_pct, rooms\_left, booking\_price, gap\_pct, direction, detected\_at).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "amountOfDays": 61,
    "dropPctThreshold": 5,
    "spikePctThreshold": 5,
    "lowRoomsThreshold": 3,
    "parityThresholdPct": 5,
    "includeParityCheck": true,
    "maxSearchResults": 100,
    "runCanary": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("bovi/booking-rate-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "amountOfDays": 61,
    "dropPctThreshold": 5,
    "spikePctThreshold": 5,
    "lowRoomsThreshold": 3,
    "parityThresholdPct": 5,
    "includeParityCheck": True,
    "maxSearchResults": 100,
    "runCanary": False,
}

# Run the Actor and wait for it to finish
run = client.actor("bovi/booking-rate-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "amountOfDays": 61,
  "dropPctThreshold": 5,
  "spikePctThreshold": 5,
  "lowRoomsThreshold": 3,
  "parityThresholdPct": 5,
  "includeParityCheck": true,
  "maxSearchResults": 100,
  "runCanary": false
}' |
apify call bovi/booking-rate-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=bovi/booking-rate-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Booking.com Rate & Availability Event Monitor",
        "description": "**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.",
        "version": "0.1",
        "x-build-id": "P5wAiPKR1fWRyyS38"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/bovi~booking-rate-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-bovi-booking-rate-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/bovi~booking-rate-monitor/runs": {
            "post": {
                "operationId": "runs-sync-bovi-booking-rate-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/bovi~booking-rate-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-bovi-booking-rate-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "propertyUrls": {
                        "title": "Property URLs to Monitor",
                        "type": "array",
                        "description": "List of Booking.com hotel page URLs to monitor for price and availability changes. Example: ['https://www.booking.com/hotel/fr/paris-marriott.html']. Each URL must contain a valid Booking.com hotel ID. Monitored via AvailabilityCalendar GraphQL API — cheap (~6 requests per property per 365-day window).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "searches": {
                        "title": "Destination Searches to Monitor",
                        "type": "array",
                        "description": "List of destination+date search configurations to monitor for inventory events. Each item must have 'destination' (city or region name), 'checkin' (YYYY-MM-DD), and 'checkout' (YYYY-MM-DD). Example: [{\"destination\": \"Paris\", \"checkin\": \"2026-07-01\", \"checkout\": \"2026-07-03\"}]. Uses Booking.com FullSearch GraphQL — up to 1,000 results per search.",
                        "items": {
                            "type": "object"
                        }
                    },
                    "checkin": {
                        "title": "Default Check-in Date",
                        "type": "string",
                        "description": "Default check-in date for property URL monitoring in YYYY-MM-DD format. Example: '2026-07-01'. Used as the start of the AvailabilityCalendar window. Individual searches in the 'searches' field can override this per search."
                    },
                    "checkout": {
                        "title": "Default Check-out Date",
                        "type": "string",
                        "description": "Default check-out date for destination search monitoring in YYYY-MM-DD format. Example: '2026-07-03'. Must be after checkin. Individual searches in the 'searches' field can override this per search."
                    },
                    "amountOfDays": {
                        "title": "Calendar Window (days)",
                        "minimum": 1,
                        "maximum": 365,
                        "type": "integer",
                        "description": "Number of days to scan in the AvailabilityCalendar for each property URL. Default 61 covers ~2 months in ~1 API call. Set up to 365 for a full year (splits into ~6 API calls per property). Higher values cost more compute but give wider price monitoring coverage.",
                        "default": 61
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Apify proxy configuration for reliable access. REQUIRED for production use — Booking.com is not reliable from datacenter IPs. Use Apify Residential proxy (useApifyProxy: true, groups: ['RESIDENTIAL']). The buyer pays proxy costs separately from PPE event charges. Without residential proxy, the actor will be rate-limited on every run."
                    },
                    "dropPctThreshold": {
                        "title": "Price Drop Threshold (%)",
                        "minimum": 0.1,
                        "maximum": 100,
                        "type": "number",
                        "description": "Minimum percentage price decrease to trigger a 'price_drop' event. Default 5.0 means a 5% or larger drop emits an event. Smaller values (e.g. 2.0) are more sensitive and emit more events; larger values (e.g. 10.0) only catch significant drops. Revenue managers typically use 5–10%.",
                        "default": 5
                    },
                    "spikePctThreshold": {
                        "title": "Price Spike Threshold (%)",
                        "minimum": 0.1,
                        "maximum": 100,
                        "type": "number",
                        "description": "Minimum percentage price increase to trigger a 'price_spike' event. Default 5.0. Useful for monitoring competitors repricing upward, which can indicate demand increases or inventory scarcity. Setting this lower (e.g. 3.0) gives earlier warning of price trends.",
                        "default": 5
                    },
                    "lowRoomsThreshold": {
                        "title": "Low Rooms Threshold",
                        "minimum": 0,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Emit a 'rooms_left_low' event when a property has this many rooms or fewer remaining. Default 3. Only emits once when rooms_left crosses below the threshold (not every run). Set to 0 to disable low-rooms alerts.",
                        "default": 3
                    },
                    "parityThresholdPct": {
                        "title": "Parity Gap Threshold (%)",
                        "minimum": 0.1,
                        "maximum": 100,
                        "type": "number",
                        "description": "Minimum Booking.com vs Kayak price gap percentage to trigger a 'parity_gap' event. Default 5.0 matches standard rate parity contract terms. Hotel chains and OTA compliance teams typically flag gaps of 5% or more as contract violations. Set higher to reduce noise on volatile markets.",
                        "default": 5
                    },
                    "includeParityCheck": {
                        "title": "Include Booking vs Kayak Parity Check",
                        "type": "boolean",
                        "description": "When true, fetches Kayak prices for each destination and emits 'parity_gap' events when Booking.com and Kayak prices differ by parityThresholdPct or more. Default true. Kayak is fetched without residential proxy (no extra protection). Disable if you only need Booking-internal price/availability events.",
                        "default": true
                    },
                    "maxSearchResults": {
                        "title": "Max Hotels Per Search",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Maximum number of hotels to process per destination search. Default 100. Booking.com FullSearch returns up to 1,000 results total (25 per page); higher values increase compute cost and API calls. For narrow monitoring (a single hotel's destination), 25–50 is usually sufficient.",
                        "default": 100
                    },
                    "runCanary": {
                        "title": "Run Schema-Drift Canary",
                        "type": "boolean",
                        "description": "When true, runs a schema-drift canary check at startup that asserts the WAF gokuProps format, b_csrf_token regex, and GraphQL operationNames still resolve. Emits a maintenance warning if any check fails, but does NOT abort the run. Recommended: enable in scheduled daily runs to get early warning before the actor goes red.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
