# Changelog of Airbnb Reviews · Guest Comments by Listing (`corent1robert/airbnb-reviews-scraper`) Actor

- **URL**: https://apify.com/corent1robert/airbnb-reviews-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/corent1robert/airbnb-reviews-scraper.md

## Changelog

All notable changes to the Airbnb Reviews Scraper are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### \[3.13] - 2026-09-07

#### Added

- **Actor Standby:** HTTP lookup `GET`/`POST /lookup` — **one** Airbnb `/rooms/` listing URL. Max **2** review pages (~20 reviews). City search is rejected. Console **Start** still writes the dataset as before.
- OpenAPI spec for the Standby tab (`web_server_openapi.json`).

### \[2026-09-01]

#### Changed

- Free plan cap: 20 rows per run. Anonymous Try off.

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

#### Changed

- Store suite: README and listing copy name companion Actors (Property Details, Pro Host). Dropped “Store does not cross-sell”.

### \[3.11] - 2026-08-20

#### Changed

- Store README: Airbnb suite Also available, real listing example, input table, FAQ.

### \[3.10] - 2026-08-20

#### Changed

- Store Try uses a real listing URL and **1** review page (API omit still exports the full listing).

### \[3.9] - 2026-08-20

#### Changed

- Store categories: Travel / Real estate (drop Developer Tools / Automation).

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

#### Added

- Store **Examples** (published tasks): Paris, Lyon, Barcelona, two-listing bulk.
- README **Who is this for?** and Examples table.

#### Changed

- Store listing public, Travel / Real estate categories, SEO title and description.

### \[3.7.0] - 2026-03-29

#### Changed

- **Dataset:** removed custom view **`reviewsVerbose`** (*Reviews (all fields)*) — it duplicated Apify’s built-in **All fields** tab; verbose rows are still complete in storage when **`includeVerboseReviewFields`** is on.

### \[3.6.0] - 2026-03-29

#### Fixed

- **Input UI:** **`requestListSources`** only accepts Crawlee-style **`{ "url": "…" }`** rows, so plain **`["https://…"]`** JSON triggered *“do not contain valid URLs”*. Switched **`listingUrls`** to **`stringList`** with **`items.type: string`** (**`format: uri`** is not allowed on items by Apify’s schema validator). **`coerceListingUrl`** still accepts legacy **`{ url }`** shapes.

### \[3.5.0] - 2026-03-29

#### Fixed

- **Request list / Console UI:** `listingUrls` entries sometimes arrive as objects with **`url`** plus **`"0"`,`"1"`,…** (string characters). **`coerceListingUrl`** now always prefers **`url` / `href`**, and can **reconstruct** the link from numeric keys when `url` is missing — so runs work even when the saved JSON looks noisy.

### \[3.4.0] - 2026-03-29

#### Changed

- **README** rewritten for the **Apify Store** audience: benefits first, plain-language dataset columns, short run instructions; technical detail moved to a small **Developers** section and **Power users (JSON)**.
- **`actor.json`:** shorter title and **benefit-led `description`** aligned with the Store listing.

### \[3.3.0] - 2026-03-29

#### Changed

- **Apify input UI:** **Language** field removed from the form — behaviour matches **auto** (locale from each listing URL). Set **`localizedLanguage`** in **raw JSON** only when you want a fixed language for the whole run.

### \[3.2.0] - 2026-03-29

#### Changed

- **Apify input UI:** only **Listing URLs** + **Language** — removed **`maxListings`**, **`logAllListingUrls`**, **`preferOriginalReviewText`**, **`includeVerboseReviewFields`** from the form (still supported via **raw JSON**). Dropped noisy **`sectionCaption` / `sectionDescription`** so the console stays clean.

### \[3.1.0] - 2026-03-29

> **Apify:** `actor.json` **`version`** must be **`MAJOR.MINOR`** only (each 0–99), not semver patch — e.g. **`3.1`**, not `3.1.0`.

#### Added

- **`listingConcurrency`** (raw JSON, **1**–**8**, default **3**): in **API mode**, process up to **N** listings **in parallel** (pool + existing per-request **`retryWithBackoff`**). Set **`1`** for the previous strictly sequential behaviour. Listing-page (HTML) mode unchanged.
- **Chunked dataset pushes** (`src/pushChunks.js`): split large batches so each **`Actor.pushData`** stays under a **~4MB** byte budget and **120** rows max per call (under Apify’s per-item limit). Unit test **`tests/unit/pushChunks.test.js`**.

#### Changed

- Completion lines and batch ETA use a small **async mutex** so logs stay coherent when several listings finish around the same time.
- **`htmlFallbackJobs`** are sorted by **`listingIndex`** before the listing-pages phase so order matches input even when API work finished out of order.

### \[3.0.12] - 2026-03-29

#### Changed

- **`pushReviews`:** one **`Actor.pushData`** per page batch (pass an array when several rows) instead of one await per review — fewer dataset round-trips.
- **Locale:** store **`acceptLanguage`** on each listing job; reuse for the reviews feed and **CheerioCrawler** `userData` so **`preNavigationHooks`** no longer calls **`getReviewsLocaleConfig`** on every HTML request (fallback only if missing).

#### Removed

- Dead tree copies: **`src/main.js.backup`**, **`main.js.phase1`**, **`*.tmp`**, and the stray **`airbnb-reviews.html`** snapshot. **`.gitignore`** entries to avoid re-adding similar scratch files.

### \[3.0.11] - 2026-03-29

#### Changed

- **Default `maxReviewPages`:** was **15** (easy to hit e.g. 150 rows at 10/page). Omitted input now defaults to the **hard cap (200 pages)** so long listings get **all** reviews until Airbnb stops paginating. Set **`maxReviewPages`** in raw JSON to a lower number for faster tests.

### \[3.0.10] - 2026-03-29

#### Changed

- **User-facing copy:** removed **GraphQL** and other implementation jargon from **run logs**, **README**, **actor description**, **input schema** text, and **tests README**. Logs use neutral phrases (e.g. “Reviews fetch failed”, “Listing pages · N listing(s)”).
- **Raw JSON aliases (optional):** **`reviewsFeedDisabled`** / **`useListingPagesOnly`** (truthy) = same as legacy **`useGraphQLReviews`:** **`false`**. **`reviewsQueryHash`** = same as legacy **`graphqlPersistedQueryHash`**. Legacy names still work.

### \[3.0.9] - 2026-03-29

#### Changed

- **Run log:** shorter progress lines (`[bar] %`, optional `Li/N` only when multiple listings, `p3/15 +10 30/149`, compact ETA). Removed the extra **per-listing** bar line that duplicated the last page at 100%. One **summary** line per listing (`id · N rows · duration`, plus batch ETA when several listings). Banner and listing-page logs without decorative emoji; **RUN\_LOG** flushes slightly less often (every 24 lines) to cut KV writes.

### \[3.0.8] - 2026-03-29

#### Changed

- **Single Apify input: Language** (`localizedLanguage`) — one dropdown controls **global** behaviour: request locale, `Accept-Language`, Airbnb-translated review copy **and** **`localizedDate`** (`Intl` month names). Removed separate UI fields **`locale`** and **`localizedDateLanguage`**.
- **`resolveListingLanguageKey`** centralises resolution; **`getIntlLocaleForLocalizedDate`** removed (use `getReviewsLocaleConfig(…).intlLocale` — always aligned with the same key).

#### Deprecated (still supported in raw JSON)

- `locale`, `localizedDateLanguage` — applied only if **`localizedLanguage`** is **auto** / omitted, or use legacy keys when `localizedLanguage` is unset; **`localizedLanguage`** takes priority when set to a fixed language.

### \[3.0.7] - 2026-03-29

#### Added

- **Startup queue log:** numbered list of URLs actually queued (same order as input). By default only the **first 12** lines are printed, then `+N more` — enable **`logAllListingUrls`** to print every URL in **RUN\_LOG** (watch size on huge lists).
- **`maxListings`:** optional cap — process only the first **N** URLs from input (warns if input was longer; hard max 10000).
- **Dataset (slim):** **`listingIndex`** (1-based) and **`listingsTotal`** on every review row for multi-listing exports / Sheets.
- **Progress lines:** each listing starts with `▶ [i/N] <url>`; GraphQL page lines include `[i/N]`.

#### Changed

- HTML fallback logs the URL sub-queue only when it is a **strict subset** of listings (avoids duplicating the full list when GraphQL is off or all listings fall back).

### \[3.0.6] - 2026-03-29

#### Changed

- **`listingUrl`** in each dataset row is now the **URL as provided in input** (`listingUrls` / `url`), not the internal canonical `https://www.{host}/rooms/{id}` used for fetching. Requests still use the canonical URL where needed.

### \[3.0.5] - 2026-03-29

#### Added

- **Input: Date label language** (`localizedDateLanguage`) — `auto` or `fr` / `en` / `de` / … Formats **`localizedDate`** from `publishedAt` with **Intl** in that language **without** changing **Review text language**. Fixes “dates still in French” when URLs are `.fr` but you want e.g. **October 2025**.

#### Changed

- **Default (slim) export** again includes **`reviewTextOriginal`** (alongside **`reviewText`**) so the guest’s original wording is always available without turning on verbose mode.

### \[3.0.4] - 2026-03-29

#### Added

- **`intlLocale`** on each supported language in `localeConfig` (BCP 47 for `Intl`).
- **`reviewDateLabel.js`:** `formatReviewMonthYearUtc` + `localizedDateForReviewRow` — **`localizedDate`** is derived from **`publishedAt`** with **`Intl.DateTimeFormat`** in the **same language as the actor export** (overrides Airbnb’s raw string when a valid ISO date exists). HTML fallback rows get **`localizedDate`** too when `intlLocale` is set.
- **Default (slim) dataset** now includes **`localizedDate`** (month + year in export language).

#### Changed

- **README / input copy:** clarifies that **`auto` + `airbnb.fr` URL ⇒ French everywhere** (API + UI strings). For a **global English export**, set **Review text language** to **`en`** — GraphQL then returns English for `reviewText`, highlights, reviewer location strings, etc.; `localizedDate` stays consistent via Intl.

#### Tests

- **`tests/unit/reviewDateLabel.test.js`** — locales (en, fr, de, es, it, pt), fallbacks, invalid inputs.
- Extended **locale**, **stays API**, **HTML extraction** tests for `intlLocale` / `localizedDate`.

### \[3.0.3] - 2026-03-29

#### Added

- **Apify input (UI):** **Review text language** (`locale`: auto / fr / en / de / es / it / pt) — drives GraphQL `locale` + `Accept-Language` so **`reviewText`** follows Airbnb’s translation for that locale (same idea as switching site language in the browser).
- **Original text only** (`preferOriginalReviewText`) — fills **`reviewText`** from **`reviewTextOriginal`** when you do not want Airbnb’s translation.
- **Include all extra fields** (`includeVerboseReviewFields`) — when off (default), dataset rows are **compact** (ids, rating, `reviewText`, dates, reviewer name, host reply, `scrapedAt`). When on, restores previous verbose columns (`source`, `reviewTextOriginal`, highlights, language flags, etc.).

#### Changed

- **Dataset view `reviewsFlat`** now matches the compact default; new view **`reviewsVerbose`** lists the full column set for CSV exports when verbose input is enabled.

### \[3.0.2] - 2026-03-29

#### Fixed

- **GraphQL never ran when `maxRequestRetries` was omitted from input:** `Number(undefined) ?? 2` is **`NaN`** (nullish coalescing does not replace `NaN`), so `maxAttempts` became `NaN`, **`retryWithBackoff`’s loop never executed**, and the function **threw `undefined`** — logged as `GraphQL failed …: undefined`, then HTML fallback returned **0 reviews**. Default retries are now applied when the field is missing or non-finite.

### \[3.0.1] - 2026-03-29

#### Changed

- **README**, **input schema**, **tests/README**, and **CHANGELOG** are **English-only** (aligned with Airbnb Professional Host Scraper style).
- **Runtime logs** are **English**: structured banner (`LOG_SEP`), GraphQL progress **bars**, **ETA** (smoothed per page / per listing, same idea as professional-host), HTML fallback section, final **Done** line with total duration.
- **RUN\_LOG** key-value mirroring matches professional-host pattern: `console.log` / `info` / `warn` / `error` all append, flush interval **15**, max lines **2000**.

### \[3.0.0] / \[2.3.0] - 2026-03-29

#### Removed

- **Proxy:** no `Actor.createProxyConfiguration`, no `proxyUrl` / `ProxyAgent` on GraphQL, no `proxyConfiguration` on **CheerioCrawler**. **Direct HTTP** only.
- **`undici`** dependency (Node global `fetch` is enough).

### \[2.2.5] - 2026-03-29

#### Fixed

- **Local proxy mis-config:** avoid implicit empty proxy object (SDK could enable Apify proxy without password → GraphQL fetch failed, log showed `undefined`). Use **`{ useApifyProxy: false }`** when no non-empty proxy config is provided.
- GraphQL error logging: show **`cause`** or **`String(e)`** when `message` is missing.

### \[2.2.4] - 2026-03-29

#### Added

- **`input.json.example`**; **`input.json`** in **`.gitignore`** (like professional-host-scraper).

#### Fixed

- **`validation.test.js`** and **`single-listing-reviews.test.js`** no longer delete **`input.json`**: backup / restore via **`tests/integration/preserveInputJson.mjs`**.

### \[2.2.3] - 2026-03-29

#### Changed

- **`tests/`** layout aligned with **professional-host-scraper**: `tests/unit/`, `tests/integration/`, `tests/fixtures/`, `tests/README.md` (pipeline, npm scripts).
- Top-level **`tests/reviews-html-extraction.test.js`**.

#### Added

- **`tests/integration/single-listing-reviews.test.js`**, **`validation.test.js`**, **`tests/unit/localeConfig.test.js`**, fixtures.

### \[2.2.2] - 2026-03-29

#### Fixed

- **`ProxyConfiguration.newUrl(sessionId)`** only allows `[\w._~]+` — use underscores in session id (`gql_{listingId}_{offset}`).

### \[2.2.1] - 2026-03-29

#### Changed

- Apify input UI: **Listing URLs** only; proxy block removed from schema (was still available via raw JSON until 3.0).

### \[2.2.0] - 2026-03-29

#### Changed

- Simplified Apify input (listings + proxy block); advanced options via raw JSON / API.

### \[2.1.0] - 2026-03-29

#### Added

- **`StaysPdpReviewsQuery`** (persisted GraphQL GET) as **default** (`/api/v3/StaysPdpReviewsQuery/{hash}`), **offset/limit**, **`metadata.reviewsCount`**, richer fields.
- **`undici`** + **`ProxyAgent`** for GraphQL behind Apify proxy (removed in 3.0).
- Input: **`useGraphQLReviews`**, **`reviewsPerPage`**, **`graphqlPersistedQueryHash`**, **`reviewsSortingPreference`**, **`currency`**, optional stay params.
- **`staysPdpReviewsApi.test.js`**.

#### Changed

- **`maxReviewPages`** cap **200** (max requests per listing).

#### Fixed

- Listings with **0 reviews** no longer trigger unnecessary HTML fallback when GraphQL returns a valid empty block.

### \[2.0.0] - 2026-03-29

#### Added

- Reviews-only actor: one dataset row per review.
- **`utils.js`**, **`localeConfig.js`**, **`reviewExtraction.js`** — HTML / embedded **`REVIEWS_DEFAULT`**.
- **CheerioCrawler**, **`maxReviewPages`**, **`maxReviewsPerListing`**, Apify schemas, **`RUN_LOG`**.

#### Removed

- Previous city search + pro-host contact flow (wrong product for this actor).

#### Changed

- **`actor.json`** 2.0, storage wiring consistent with other Airbnb actors in the repo.
