# Changelog of Booking Pro Host · Hotel Emails by City (`corent1robert/booking-pro-host-scraper`) Actor

- **URL**: https://apify.com/corent1robert/booking-pro-host-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/corent1robert/booking-pro-host-scraper.md

## Changelog

All notable changes to the Booking Pro Host Scraper are documented here.

Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

***

### \[2.12] - 2026-09-06

Rebuilt on Store version **2.9** (10-version cap).

#### Fixed

- Empty / wiped Try input uses the Console example hotel instead of failing validation.
- Store Try / 5-minute check caps at 3 hotels so the run finishes.
- `Actor.exit` now times out in 5 seconds so RUN\_LOG flush cannot hang the run.

### \[2.11] - 2026-09-01

#### Changed

- Phase 1 search pagination fetches up to 5 GraphQL offsets at a time (was one page after another).
- End-of-run log: next step to Booking Availability (day-by-day rates).

### \[2.10] - 2026-08-24

#### Changed

- Store `description` / `seoDescription` name the Booking suite: **Hotel Search** (catalog) and **Availability** (calendars).
- Console **Mode** (`hotel` / `search` / `city`) so a prefilled hotel URL cannot mix with city or search input.

### \[2.9] - 2026-08-24

#### Fixed

- Country / search batch errors no longer fail the Actor — remaining batches continue; empty search still succeeds with 0 rows.

### \[2.8] - 2026-08-19

#### Changed

- README **Also available** (Booking suite) at the top.

### \[2.7] - 2026-08-19

#### Changed

- **Free plan cap:** 20 hotels per run (`APIFY_USER_IS_PAYING`). Status: `Free plan: 20 hotels. Upgrade for bulk.`
- Anonymous Try-without-account runs disabled (`isAnonymouslyRunnable: false`).

### \[2.6] - 2026-08-18

#### Changed

- Store `seoTitle`: `Booking.com Hotel Emails · Pro Host Contacts | Apify`.

### \[2.5] - 2026-08-18

#### Added

- **8 more Store Examples** (12 total) — Lisbon, Barcelona, Amsterdam, Athens, Nice, multi-city CRM, Marbella max-coverage, Lisbon search URL.

### \[2.4] - 2026-08-17

#### Changed

- **README** — Who is this for, Quick start, pricing and legality sections.
- **Store SEO** — `seoTitle` with `| Apify`.

### \[2.3] - 2026-08-17

#### Changed

- **Default RAM 4096 MB → 1024 MB** — HTTP GraphQL only; 4 GB was leftover from the browser era and made Try/API runs heavier than needed.

### \[2.2] - 2026-08-17

#### Fixed

- **One failed search URL no longer fails the whole run.** GraphQL flakes (empty body, HTML challenge, Internal Server Error) are retried up to 4 times with backoff, then skipped. The Actor finishes with whatever hotels were collected — including 0 — instead of aborting. This was the main cause of the ~68% success rate.

### \[2.1] - 2026-04-13

#### Added

- **`hotel_stars`** — star rating from Phase 1 GraphQL (`basicPropertyData.starRating.value`). Zero cost — data already present in search results.
- **`hotel_review_score`** and **`hotel_review_count`** — review score and count from Phase 1 GraphQL (`basicPropertyData.reviews.totalScore / reviewsCount`).
- **`hotel_price_per_night`** and **`hotel_price_currency`** — per-night price computed from `priceDisplayInfoIrene`. Populated only when the search URL includes dates. In multiSearch mode, dates are always present so price is always available.

#### Changed

- **multiSearch passes: 18 → 25.** New variation strategy based on observed marginal gains per pass:
  - Near-term offset sweep at 1 night (+7d, +14d, +21d, +30d, +45d) — catches date-sensitive inventory not visible at a single offset.
  - Short multi-night stays (+7d·2n, +14d·2n, +21d·3n, +21d·7n) — catches properties with minimum stay requirements.
  - Far-out date sweep (+60d to +300d) — unchanged.
  - Guest count sweep (2–12 guests) — unchanged.
  - Removed low-yield duration passes (+21d·4n, +21d·5n, +21d·6n yielded <5 new hotels each).
- **Mode auto-detection** — `mode` field removed from Console UI. Actor now infers mode from which fields are filled: `searchUrls` → Search URL mode, `cityNames` → City name mode. `mode` remains accepted as an optional override in JSON input for backward compatibility.
- **multiSearch bug fix** — multiSearch was silently ignored when `mode` was not explicitly set in JSON input. The Console UI never sets `mode`, so multiSearch never activated from the UI. Fixed by applying multiSearch logic in all `searchUrls` code paths.
- **Input form simplified** — Removed `mode` selector, `hotelUrls` field, and `maxHotels` field from Console UI. All remain usable via JSON input.
- **`hotel_city` fallback** — When Booking.com returns a malformed city string (e.g. full address in the city field, contains comma or >40 chars), the actor now falls back to the legal contact city from trader info.

#### Removed

- **`hotel_postal_code`** — removed from output. Rarely populated; added noise to exports.
- **`hotel_contact_country`** — removed from output. Two-letter country code has no practical use in outreach workflows.

#### Fixed

- **Warning during multiSearch passes** — "X of Y properties collected. Enable multiSearch…" warning was incorrectly firing during multiSearch passes (where partial collection per pass is expected by design). Now suppressed when `multiSearch: true`.
- **Warning with small `maxHotels`** — Same warning incorrectly fired when `maxHotels` intentionally capped results. Now suppressed when the cap is the reason for partial collection.
- **`hotel_price_per_night` always 0** — Booking.com's `averagePricePerNight` field returns `0` (not null) even when a valid stay price is available. Fix: treat `0` as absent and compute per-night from `amountPerStay / nights` instead.

***

### \[2.0] - 2026-04-13

Complete rewrite. The Actor now runs 100% via HTTP GraphQL — no browser, no Chrome, no residential proxy required.

#### Added

- **HTTP GraphQL Phase 1** — Hotel URLs collected via Booking.com's internal `/dml/graphql` search endpoint. No browser navigation needed; no `destId` required (searchString is sufficient).
- **HTTP GraphQL Phase 2** — Trader contact info (email, phone, company, address, SIRET) fetched via the `hotelPageByPageName` GraphQL query with inline fragment `... on HotelPageType`. No page rendering, no Apollo cache scraping.
- `**src/phase1-http-extractor.js`\*\* — Phase 1 rewrite: pure HTTP, concurrent city searches.
- `**src/phase2-http-crawler.js**` — Phase 2 rewrite: bounded concurrent `Promise.all` workers (default 20, cap 50).
- `**src/hotel-graphql-client.js**` — Thin HTTP client for `hotelPageByPageName` with `normalizePhone` + `sanitizeWebsite` applied at extraction time.
- `**httpGraphqlFetch()**` exported from `src/graphql-client.js` — reusable HTTP fetch without a browser page.
- `**normalizePhone()**` and `**sanitizeWebsite()**` — migrated to `src/utils.js` and unit-tested.
- **Proxy optional** — The `/dml/graphql` endpoint is not behind the AWS WAF that blocks HTML page rendering. Proxy can still be added for large-scale runs or geo-routing.

#### Changed

- **Docker image** — `apify/actor-node-puppeteer-chrome:22` → `apify/actor-node:22`. Image drops from ~1.5 GB to ~200 MB.
- **Default memory** — 4096 MB → **512 MB** (no Chrome process).
- **Dependencies** — Removed `puppeteer`, `crawlee`, `cheerio`. Added `undici` for proxy-aware fetch.
- **Phase 2 duration** — Was 30–60 s/5 hotels (blocked by AWS WAF without residential proxy). Now **~0.3 s/5 hotels** (~100× faster).
- `**proxyConfiguration`\*\* input — Changed from required to optional; description updated accordingly.
- `**src/constants.js**` — Removed `CHROME_ARGS`, `BLOCKED_URL_PATTERNS`, and browser-specific timeouts.

#### Removed

- `**src/graphql-extractor.js**` — Replaced by `phase1-http-extractor.js`.
- `**src/phase2-crawler.js**` — Replaced by `phase2-http-crawler.js`.
- `**src/apollo-extractor.js**` — Apollo cache parsing (page.evaluate) no longer needed. Pure utility functions migrated to `utils.js`.
- `**src/contact-extractor.js**` — Puppeteer page scraping layer removed.
- `**src/dom-fallback.js**` — DOM extraction fallback removed (GraphQL is reliable without browser).
- `**scripts/**` and `**docs/**` — Investigation artifacts removed from the repository.
- Dead tests (`03-apollo-extractor`, `06-graphql-extractor`, `07-phase2-fetch-live`, `08-email-filter`, `10-apollo-keys`, `16-contact-extractor`) replaced with tests for current modules.

***

### \[1.0] - 2026-03-03

Initial release. Puppeteer-based scraper.

#### Added

- GraphQL extraction via `page.evaluate` (browser context) + DOM fallback.
- Pro hosts filter, host deduplication (`onlyUniqueProHost`).
- Modes: Country, City name, Search URL, Hotel URL(s).
- multiSearch (18 date/guest variations per city).
- CSV and JSON export.
- Progressive dataset output (stream during run).
