# Changelog of Airbnb Scraper 🏠 Real Nightly Prices, Whole-City Coverage (`tagadanar/airbnb-scraper`) Actor

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

## Changelog

### 2026-10-01

- **A slow connection no longer costs a listing its calendar, or the run a
  browser start.** A calendar request that gets no answer is now cut after 15
  seconds and tried once more over a different proxy exit; if that one answers,
  the rest of the page's calendars use it. Before, the request could wait far
  longer than its 30-second limit, and it was never retried.
- **The calendar key is only renewed when Airbnb says it is out of date.** The
  actor renews that key by opening a real browser, which is the slowest and most
  expensive thing it does. Three failed calendar requests in a row used to
  trigger it whatever the reason, and so did a single failed check at the start
  of the run. Now only answers from Airbnb that reject the request count; a
  request that timed out or was refused at the proxy does not.
- The run log ends with a `CALENDAR ROUTE` line: calendars read, requests that
  got no answer, retries, and how many the retry recovered.
- Nothing changes in what is charged: a calendar is billed only when it was
  read, exactly as before.

### 2026-08-29 (0.1.23)

- **The `locale` input added earlier today (0.1.22) is removed.** It was
  measured on Apify the same day and it does not work: a `locale=` on the
  search URL changes the shape of the server-rendered blob this actor reads
  (or redirects away from it), so
  `…/s/Paris/homes?currency=EUR&locale=fr` came back **HTTP 200 with 0
  listings on every proxy rung** (run `zZAYMpNi102p1l1M6`, three "No results
  in SSR" retries) while the identical input without it returned 5 listings
  (run `kdkowBScMnehu9W6c`). An input that guarantees an empty run is worse
  than no input at all, so it is gone from the schema, the URL builder and the
  README, and a test now asserts no URL this actor builds ever carries
  `locale=`. Calendar requests go back to the `fr`/`EUR` pair they used before
  0.1.22 — the day records are language-independent, so no output changes.
  Nothing else about a run changes: `currency` behaves exactly as it did.
- **A run that collects nothing now says so.** The 0-listing run above finished
  SUCCEEDED with the status message "Starting the crawler." — Crawlee's own
  line, because the actor never set one. Every path now ends with an explicit
  status message built from counted evidence (pages parsed, pages served with
  no listing data, requests that failed, rows delivered): a run where Airbnb
  answered but served no listing data says *"Airbnb served N page(s) without
  listing data; nothing was charged — retry later or paste a different search
  URL."*, and "no listings matched your filters" is only ever said after a page
  this run actually read listings out of.

### 2026-08-29

- New `locale` input: pick the language Airbnb renders the listing text in
  (English, French, German, Spanish, Italian, Portuguese or Dutch). It is
  written onto every search and calendar request the run makes, the same way
  `currency` already was, so a run never comes back with two languages mixed
  in it. Defaults to English, which is what runs got before.
- README: the pricing section said Apify platform usage was billed to you
  separately. It is not, and has not been since 2026-08-26: $1.00 per 1,000
  listings is the whole bill, compute and proxies included. The buyer
  comparison table is rebuilt on that basis and the actor start fee now reads
  its real $0.001 instead of a long-stale $0.00005.
- README: all 25 inputs are now documented in a table. Fifteen of them, every
  filter and the monitor trio included, had never been named on the page.

### 2026-08-09 (pre-push review fixes)

- **BREAKING-ish, read this if you consume `price`/`pricePerNight`: prices are
  now always denominated in the `currency` input, which defaults to `USD`.**
  Until today the currency parameter was written on the search URL only when the
  caller asked for one, so the denomination silently followed the proxy exit IP
  (a pinned US residential exit, hence `USD`). The proxy ladder can land on an
  unpinned datacenter exit, which would have let the denomination drift page to
  page inside one run. The default reproduces the old behaviour exactly; set
  `currency` to `EUR`, `GBP`, … to change it. Every output row now carries the
  run's currency in its `currency` field rather than whatever symbol happened to
  be in the price string. Same mechanism as the companion actor
  airbnb-str-yield, which defaults to `EUR` because its exit was pinned FR.
- **The calendar-hash KV store was renamed to
  `airbnb-scraper-calendar-hash-v2`.** The old store had been created by hand
  from a dev box, so the platform recorded it with `actId: null` — it belonged to
  no actor, and a run under LIMITED_PERMISSIONS may only open named storages its
  own actor created. Every platform run's open was therefore denied and swallowed
  into a single warning, and the actor re-minted the hash with Chromium on every
  run: 30 days, `readCount` still 0. The v2 name has never been created manually,
  so the first platform run creates and owns it and every run after that is WARM.
- **The proxy ladder no longer burns a full search page per rung.** `selectRung`
  now returns the winning probe's parsed body, and the run ingests it as its own
  first page instead of re-fetching the identical URL. The probe went from pure
  overhead (up to 3 extra search pages a run, some on residential at $8/GB) to
  the run's page-1 fetch. Per-rung availability and per-attempt status logging is
  unchanged, and so is the fall-back-to-the-last-rung behaviour; when no rung
  passes there is no validated body, so nothing is reused.
- Tests: 35 → 43.
- Not pushed, not applied.

### 2026-08-09 (later)

- **Proxy ladder (new `src/proxy.js`)**: the actor now tries a datacenter proxy
  first and only steps down to residential when Airbnb refuses the cheap class.
  Every rung logs whether the platform granted it, and every rung logs the HTTP
  status and the number of listings our own parser could read from its probe —
  a proxy-config failure is never swallowed. Datacenter is obtained by omitting
  `apifyProxyGroups`; there is no Apify proxy group named `DATACENTER`.
  Evidence (2026-08-09, OVH AS16276 datacenter vantage): Airbnb search SSR
  returned HTTP 200 on 25/25 pages across 5 cities (450 listings parsed), and
  `PdpAvailabilityCalendar` answered 6/6.
- **Cold-vs-warm accounting (new `src/hashcache.js`)**: the cached calendar hash
  now carries when it was minted and when it was last confirmed working, and
  every run ends with a single `COST SUMMARY` line stating `mode=WARM|COLD`, the
  proxy rung, the whole ladder result, the cache age and the number of browser
  mints. Previously a finished run gave no way to tell a cheap run from an
  expensive one, which is how a one-off cold-mint run became this actor's
  headline cost figure. Legacy bare-string cache records still load.
- **Mid-run hash rotation is now handled**: three consecutive empty calendars
  trigger exactly one re-mint instead of silently returning listings with no
  calendars for the rest of the run.
- **Guardrails**: `maxMemoryMbytes: 1024` added (the actor had no cap at all, so
  a run could be started at 32 GB) and the default run memory dropped 2048 → 1024.
  The 1024 bucket is set by Chromium during the rare hash mint, not by the JS
  accumulators — `pushed[]` holds a six-field summary per listing, not full
  records, so it is not a growth risk.
- **Currency is now always written on the search URL** (defaulting to `USD`,
  which is what the previously-pinned US exit produced). Without this an
  unpinned datacenter exit could vary the currency page to page.
- Tests: 20 → 35.
- Not pushed, not applied. Console-side changes staged in
  `actors/tools/cfg-airbnb-scraper.json`; `maxMemoryMbytes` needs `apify push`.

### 2026-08-09

- Copy only (not pushed): title/description reworded around the two job phrases
  we already rank well on unprompted ("airbnb price monitoring" #3, "airbnb
  market research" #5 in the public Store search, measured 2026-08-09).
  README opens with a measured free-tier all-in cost table against the market
  leader (tri_angle/airbnb-scraper: $6.00/1,000 on their FREE tier before any
  platform cost, vs our $1.00/1,000 sticker and ≈$2.75–$5.78/1,000 all-in
  worst case). No pricing, input or code change. Proposed Console SEO fields
  staged in `actors/tools/cfg-airbnb-scraper.json`, not applied.

### 2026-07-13

- README: pricing table now lists the actor-start event ($0.00005, effectively zero) for consistency with sibling actors.

### 2026-07-12

- Invalid input no longer fails the run. The run now finishes cleanly with a clear explanation in its status message, so a typo or an empty list is easy to spot and fix.
- List inputs now accept datasets piped straight from another scraper: entries
  that are objects are read through their usual key names (url/link,
  query/keyword/title, name/company and similar) instead of being rejected.

### 2026-07-11

- Billing: on paid runs every row is billed before it is written, and a run
  now stops visibly if billing fails instead of continuing unbilled.

### 2026-07-09

- Input is now tolerant of common shapes that previously failed the run: a bare
  string / bare object / delimited string for searchUrls or locationQueries is
  coerced into a list instead of erroring. Empty input still errors.

### 2026-07-06

- Initial public release on Apify Store.
- Reliable nightly prices (parsed from the price breakdown), ratings, reviews, coordinates, host type and photos.
- Full-city coverage: automatic geo-tiling continues past the ~270 results where a single Airbnb search stops.
- Optional per-listing availability calendar (up to 12 months ahead).
- Monitor mode: schedule it and get only NEW listings, with Slack/webhook alerts.
- Guest counts (children, infants, pets) and minimum bedrooms / beds / bathrooms for place-name searches.
- Price bounds are applied inside the Airbnb search itself — faster runs, no wasted results.
