Scrape every guest review from any Airbnb listing.
Get rating, comment, date, reviewer name, and host reply —
exported as a clean table in one run.
Paste Airbnb listing URLs, get all reviews in a structured dataset.
No scrolling, no manual copy-paste.
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.jsonversion 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 CheerioCrawleruserData 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 andlocalizedDate (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.
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.