# Changelog of Zillow & Redfin Property Data Scraper (`scrapebench/zillow-redfin-property-scraper`) Actor

- **URL**: https://apify.com/scrapebench/zillow-redfin-property-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/scrapebench/zillow-redfin-property-scraper.md

## Changelog

### 2026-10-09 — Zillow rows gain valuation/geo/tax fields; miss breaker re-sized (public 0.0.24, internal 0.0.18)

- **New Zillow fields (additive; no existing key changed meaning).** Inventory: full key tree of
  the 38 bench payloads (capture run `MBHdR73GzbDOIrAOM`). Meaning proven by count:
  - `latitude`/`longitude` (shared keys with Redfin): 38 distinct points for 38 homes; 3 m median /
    9 m max from Redfin's point for the same 36 homes — rooftop, not a centroid.
  - `taxAssessedValue` (shared): equals Redfin's county total on 33 of 35. No tax year in the
    payload, so `taxYear` stays Redfin-only.
  - `county` (shared): same "Philadelphia County" form as Redfin on 37/37.
  - `zestimate` (new key, not `estimatedValue`): 0 of 32 equal to the Redfin Estimate, median 10.7%
    apart — a different model. `rentZestimate` (monthly): ~0.83% of the Zestimate, 38 distinct.
  - `price` + `priceLabel`: Zillow's bare `price` is the Zestimate on 26 off-market homes and the tax
    value on 5 more — so it is emitted only for FOR_SALE ("Listed at Price") and SOLD/RECENTLY_SOLD
    ("Last Sold Price", == `lastSoldPrice` 6/6), in Redfin's own label wording.
  - Not emitted: signed Google street-view/static-map URLs (test enforces); undated `lastSoldPrice`.
  - Live fill on `source: zillow` (`TnNfNyuQafrh8Z0G6`, 37 hits): zestimate 33, rentZestimate 37,
    lat/long 37 (37 distinct), county 36, taxAssessedValue 36, price/priceLabel 7.
- **Miss breaker** 8-of-20 / 15-in-a-row → fewer than 3 of the first 50, 200 misses in a row, or
  10 refused/timed-out lookups in a row (break-even ~2.7% matched; arithmetic in `main.py`).
  A 60-address garbage batch stopped at 50 with the free `no_match` row (`6JTo1ZfxJ9ZSqCEUu`,
  $0.0065); the bench batch ran to the end, 38/38 (`qleA9LyKYrbDehu1n`).
- Listing: `priceLabel` description corrected to Redfin's real values ('Listed at Price',
  'Last Sold Price', 'Sold Price' — it said 'Price'). Charging unchanged (`fyxBjgWzfbyo9izVk`: 3/3).

### 2026-10-09 — Zillow source fixed (public 0.0.22+, internal 0.0.16+)

- **Diagnosis** (throwaway probe actor on Apify, every egress gated on an example.com control):
  headless Chromium got PerimeterX "Press & Hold" 403 from the container IP, Apify datacenter and
  Apify residential alike — a browser-fingerprint wall, not an IP wall, not a parser change, not a
  URL change. `curl_cffi` impersonating Chrome got 200 + `__NEXT_DATA__` through residential
  (12/12); datacenter worked 3/3 then 0/25; container 0/3. `zillowstatic.com/autocomplete/v3` is
  unwalled from every egress and returns zpid + streetNumber. Probe runs `UBwV1LDRbdYAa4c98`,
  `WcwHwOSIyAKLW5Jxo`, `bvNaabC8FP0kPZKfk`; the old path's 0/10 was `VvRiYP5G96rhnSGgG`.
- **Fix:** Zillow uses no browser. Autocomplete (direct, free) → first `Address` suggestion with
  the asked street number (#140 guard at the source; `make_result` re-checks the resolved record)
  → `/homedetails/<zpid>_zpid/` via curl_cffi over residential, up to 4 fresh IPs, 8s each →
  unchanged parser (verified on a live capture). Refusal raises → free not-found row with reason.
- `auto` now falls back to Zillow on every run (was: only with `useProxy`). Measured on the bench
  batch: Redfin-only 36/38 real → auto 38/38 (`fSGVH3LtnTI9rpwCN`, 76s, $0.0063); 0 of 12 invented
  addresses matched by either source.
- `source: zillow` on the same batch: 2 attempts 32/38 (`RGpWIRt8H2gSoYXOB`), 4 attempts 36/38
  (`aDLHJs0BEobBjTmsx`, one curl timeout escaped the retry), transport errors retried 38/38
  (`wVj1DZ5cKa903QoVl`, 197s, $0.038 ≈ $0.001/hit incl. residential). Zillow miss ~0.5s.
- Listing, input schema, both website articles, target_difficulty and the recon doc updated to
  match. New dependency `curl_cffi`.

### 2026-10-09 — warm Redfin session per batch (public build 0.0.19, internal 0.0.13)

- Redfin lookups reuse ONE warmed context+page (`browser.RedfinSession`) instead of opening a
  fresh context and loading redfin.com + waiting for the `RF_BROWSER_ID` cookie per address. A
  miss now ends after the single autocomplete call. Re-warms when the page closed/crashed, when a
  lookup was cut off by the timeout, or when a lookup on a reused session raises (403 after retry
  \= cookie lost/flagged) — that lookup is retried once on the fresh session. Batch, `build_records`
  and Standby all use it; the proxied path (`useProxy`) and Zillow/Realtor are unchanged.
- Measured on Apify, same 50-address batch (`docs/actors/zillow-redfin-bench-input.json`: 38 real
  Philadelphia parcels + 12 invented house numbers), 2 GB, serial, source auto:

  | | before (0.0.18, `zKz0vZjN8cipu8qcc`) | after (0.0.19, `KMfFyxBnArtRZaCaI`) |
  |---|---|---|
  | found | 34 (2 were 403 errors) | 36 (0 errors) |
  | run time | 672 s | 64 s |
  | compute units | 0.373 | 0.036 |
  | usage USD | $0.0790 ($0.00158/lookup) | $0.0042 ($0.000083/lookup) |
  | hit / miss avg | 13.7 s / 11.8 s | 1.14 s / 0.23 s |
  | break-even match rate (net $0.0032/match) | ~49% | ~3% |

  Long session, 300 addresses on the internal twin (`3xb78Le8SmTEW2ga1`): 269/299 found, 0 errors,
  hit avg 1.05 s, 307 s, $0.035 — no decay, so no periodic re-warm.
- No pricing change. Spending-cap stop + free `truncated` notice re-verified on the public actor
  (`0HXOdVZrKtZ40BG4C`: cap $0.008 → 2 events, 2 addresses listed as skipped).
- Zillow fallback measured, behaviour unchanged: `source: zillow` + residential proxy found 0 of 10
  (the 4 Redfin misses AND 3 Redfin hits as controls) in `VvRiYP5G96rhnSGgG`. `auto` only tries
  Zillow behind the proxy, so it costs nothing on the default path.

### 2026-10-08 — spending-cap stop + miss breaker (0.0.17)

- Stop the batch when `charge_result` returns False (free `truncated` notice); miss breaker
  (free `no_match` notice). Concurrency 3 tried and reverted.
