Zillow Property & Agent Data Scraper avatar

Zillow Property & Agent Data Scraper

Pricing

Pay per event

Go to Apify Store
Zillow Property & Agent Data Scraper

Zillow Property & Agent Data Scraper

Extract Zillow property listings with agent contacts (emails, phones), price history, and 70+ data fields. Three search modes: ZIP codes, ZPID list, or any Zillow URL. 40+ filters including tri-state controls for auctions, foreclosures, and new construction. Volume discounts - save up to 37.5%!

Pricing

Pay per event

Rating

4.7

(3)

Developer

Andrey Afanasenko

Andrey Afanasenko

Maintained by Community

Actor stats

7

Bookmarked

329

Total users

68

Monthly active users

7.7 hours

Issues response

4 minutes ago

Last modified

Share

Apify Actor

Zillow listings and agent contacts in one structured row per property — search by ZIP codes, enrich a list of ZPIDs, or scrape a Zillow URL with the filters you've already configured in the browser.


🎯 What it does

You point the actor at one of three input shapes — a list of ZIP codes, a list of Zillow Property IDs (ZPIDs), or a single Zillow search URL — and you get one structured row per property in the dataset.

Each row carries the listing essentials (price, status, address, beds/baths, sqft, year built), the financial layer (Zestimate, rent Zestimate, HOA fee, property tax, last-sold, full price history), the agent contact layer (agent name, email, phone, license, brokerage), and the property-detail layer (heating, cooling, appliances, view, fireplace, pool, garage, photos, schools).

ZIP-code mode adds 40+ filters that narrow the search before the actor pays for an enrichment. Tri-state filters (isAuction, isNewConstruction, isForSaleForeclosure) accept any / only / exclude, so you can flip a single field to filter foreclosures out without losing the rest of your filter set. Optional enrichments — Walk Score / Transit Score / Bike Score and full photo galleries — attach extra columns when you enable them.


📦 Output sample

One dataset row, ZIP mode, listing in Beverly Hills with full enrichment enabled:

{
"zpid": "20533889",
"hdpUrl": "https://www.zillow.com/homedetails/123-Palm-Dr-Beverly-Hills-CA-90210/20533889_zpid/",
"price": 5500000,
"status": "ForSale",
"homeType": "SINGLE_FAMILY",
"streetAddress": "123 Palm Dr",
"city": "Beverly Hills",
"state": "CA",
"zipcode": "90210",
"county": "Los Angeles County",
"neighborhood": "Beverly Hills Flats",
"latitude": 34.0901,
"longitude": -118.4065,
"bedrooms": 5,
"bathrooms": 4,
"bathroomsFull": 3,
"bathroomsHalf": 2,
"livingArea": 3500,
"lotSize": "10,890 sqft",
"yearBuilt": 2010,
"stories": 2,
"architecturalStyle": "Contemporary",
"agentName": "Jane Doe",
"agentEmail": "jane@example-realty.com",
"agentEmailSource": "property_details",
"cellPhone": "310-555-0199",
"brokerName": "Luxury Real Estate Inc.",
"zestimate": 5600000,
"rentZestimate": 18500,
"pricePerSqft": 1571,
"hoaFee": 500,
"propertyTax": 68500,
"priceChange": -100000,
"datePriceChanged": "2026-04-12",
"hasPool": true,
"hasGarage": true,
"garageSpaces": 3,
"hasBasement": false,
"view": "City, Mountain(s)",
"heating": "Forced Air, Natural Gas",
"cooling": "Central Air",
"daysOnZillow": 14,
"lastSoldDate": "2018-06-22",
"lastSoldPrice": 4250000,
"lastTaxAssessedValue": 4400000,
"priceHistory": [
{"date": "2026-04-12", "price": 5500000, "event": "Price changed", "pricePerSquareFoot": 1571, "source": "Listing", "postingIsRental": false},
{"date": "2018-06-22", "price": 4250000, "event": "Sold", "pricePerSquareFoot": 1214, "source": "Public Record", "postingIsRental": false}
],
"schools": [
{"name": "Beverly Vista Elementary", "level": "Elementary", "rating": 9, "distance": 0.6}
],
"mainPhoto": "https://photos.zillowstatic.com/fp/abc123-uncropped_scaled_within_1536_1152.jpg",
"photos": ["https://photos.zillowstatic.com/fp/abc123-uncropped_scaled_within_1536_1152.jpg"],
"walkScore": 95,
"transitScore": 80,
"bikeScore": 88,
"listingType": "FSBA, openHouse"
}

74 columns are documented in the .actor/dataset_schema.json — you can see every column's type, description, and example in the Apify Console's dataset view.


⚡ Quick start

  1. Pick a mode: ZIP codes (discover), ZPID list (enrich knowns), or Zillow URL (browser-configured filters).
  2. Fill in the inputs for that mode. The other modes' inputs are ignored.
  3. (Optional) Toggle enrichments — Walk Score and Photos — for ZIP mode.
  4. Run the actor.
  5. Download in JSON / CSV / Excel / XML, or read the dataset programmatically (see § Programmatic / API use).

Minimal ZIP-mode input:

{
"mode": "zip",
"zipCodes": ["94103"],
"maxPropertiesPerZip": 5
}

That's a single ZIP, capped at 5 properties — under $0.50 to test on the free plan.


🧭 How to use it (in one screen)

  • Choose Operation Mode: ZIP Code Search · ZPID List · Zillow URL.
  • Inside Selected Mode: enter zipCodes (e.g., 94103) for ZIP mode, paste known zpids for ZPID mode, or paste a zillowUrl (Zillow's own filter UI / saved search / sold-listings page) for URL mode.
  • Tuning (ZIP mode only — URL/ZPID embed their own filters): ⚙️ Enrichments (Walk/Transit/Bike Score · full photo gallery) + 🔬 Advanced Filtering (price · beds/baths · home type · tri-state new-construction/auction/foreclosure · rental amenities · school rating).

💡 Tips & Best Practices for cost, performance, and data-quality patterns.


⭐ Choose your mode

Mode 1 — Search by ZIP Codes (mode: "zip")

Discovery mode. You pass one or more ZIP codes plus any of the 40+ filters; the actor walks Zillow's results, applies the filters server-side where Zillow supports them and client-side otherwise, and enriches the keepers.

  • All filters apply — price/beds/baths range, home type, listing type, view, school rating, rental amenities, tri-state foreclosure / auction / new-construction.
  • Enrichments available — Walk Score, Transit Score, Bike Score, full photo galleries.
  • maxPropertiesPerZip — hard cap per ZIP code. Set 0 for unlimited (paid plans only).
  • Best for: cold discovery in a market, lead generation, comp pulls.

Mode 2 — Process a ZPID List (mode: "zpid")

Enrichment mode for properties you already know. You pass a list of ZPIDs and the actor pulls full property + agent + history data for each.

  • Filters ignored — you've already chosen the properties.
  • Enrichments disabled — Walk Score / Photos toggles do nothing in this mode.
  • Best for: re-enriching a saved list, comp lookups, monitoring known properties.

Mode 3 — Search by Zillow URL (mode: "url")

You configure the filters in your browser at zillow.com, copy the resulting URL, and paste it in. The actor preserves Zillow's full filter state (including ones the input schema doesn't expose).

  • Filters embedded in URL — input-schema filters are ignored.
  • Enrichments disabled.
  • Best for: complex filter combinations Zillow's UI can express but the input schema can't.

🛠 Input

The input schema is grouped into the following sections (open the Input tab in the Apify Console for the full UI):

  • Operation Modemode ("zip" / "zpid" / "url").
  • ZIP Mode InputszipCodes, maxPropertiesPerZip.
  • Search Filters (ZIP only)status_type, price_min/max, rentMinPrice/MaxPrice, beds_min/max, baths_min/max, doz, sort, keywords.
  • Home TypeisSingleFamily, isTownhouse, isCondo, isMultiFamily, isApartment, isManufactured, isLotLand.
  • Listing Type & StatusisForSaleByOwner, isForSaleByAgent, isNewConstruction, isAuction, isForSaleForeclosure, includeForeclosed, includePreForeclosure, isPendingUnderContract, isAcceptingBackupOffers.
  • Property Featuressqft_min/max, lotSize_min/max, built_min/max, parkingSpots_min, hasGarage, hasPool, hasAirConditioning, isWaterfront, isBasementFinished, isBasementUnfinished, hoa_max.
  • View FiltersisCityView, isMountainView, isParkView, isWaterView.
  • Tour FiltersisOpenHousesOnly, is3dHome.
  • Rental-Only Filters — pet policies (largeDogsAllowed, smallDogsAllowed, catsAllowed, noPets) + amenities (inUnitLaundry, furnished, hardwoodFloor, utilitiesIncluded, disabledAccess, shortTermLease, outdoorSpace, controlledAccess, highSpeedInternet, elevator, acceptsApplications, incomeRestricted, parkingAvailable).
  • School Filtersschools, schoolsRating, includeUnratedSchools.
  • ZPID Mode Inputszpids.
  • URL Mode InputszillowUrl.
  • Enrichment Options (ZIP only)enrichWalkScore, enrichPhotos.

Highlights:

  • Tri-state filters (isAuction, isNewConstruction, isForSaleForeclosure) accept any (no filter), only (require), or exclude (block). Flip one to exclude to drop foreclosures without losing your other filter logic.
  • Filter and enrichment options are silently ignored in ZPID and URL modes — they're not errors, they just don't apply.
  • Free plan caps: 15 properties / 20 ZIPs per run, agent emails masked. See § Free vs paid plan.

JSON snippets per mode:

{ "mode": "zip", "zipCodes": ["94103"], "status_type": "ForSale", "maxPropertiesPerZip": 50, "isAuction": "exclude", "hasPool": true }
{ "mode": "zpid", "zpids": ["20521978", "95547747"] }
{ "mode": "url", "zillowUrl": "https://www.zillow.com/homes/for_rent/?searchQueryState=..." }

💰 Pricing

Pay-per-event. You pay only when the actor finishes enriching a property — not for failed lookups, ZIPs that produced zero matches, or URL fetches that didn't yield a property.

EventTriggerFreeBronzeSilverGoldPlatinumDiamond
apify-actor-startActor starts (1 event per GB of memory, minimum 1)$0.00005$0.00005$0.00005$0.00005$0.00005$0.00005
PROPERTY_ENRICHEDDetailed property info fetched (price, agent contacts, price history, specs)$0.08$0.07$0.06$0.05$0.05$0.05

Cost math:

  • 100 properties on Free ≈ 100 × $0.08 + actor-start ≈ $8.00
  • 100 properties on Diamond ≈ 100 × $0.05 + actor-start ≈ $5.00
  • 1,000 properties on Diamond ≈ 1,000 × $0.05 + actor-start ≈ $50.00

The actor self-bounds via budgetManager — it stops paying for new enrichments once the budget cap is reached, and records freeLimitBudget to the FREE_LIMITS_APPLIED storage record. You can set a custom per-run budget in the input.


🆓 Free vs paid plan

PlanMax properties / runMax ZIPs / runAgent emailsPer-event price
Free1520Masked (j***@e***.com)$0.08 / PROPERTY_ENRICHED
Paid (Bronze → Diamond)Unlimited (budget-bounded)UnlimitedFull (jane@example.com)$0.07 → $0.05 / PROPERTY_ENRICHED

Free-plan ceilings hit during a run are recorded to the FREE_LIMITS_APPLIED storage record so API consumers can switch on them without log-parsing.


🧪 Filter recipes

Single-family for-sale homes in 90210, exclude auctions, must have pool:

{
"mode": "zip",
"zipCodes": ["90210"],
"status_type": "ForSale",
"maxPropertiesPerZip": 50,
"isSingleFamily": true,
"isCondo": false,
"isAuction": "exclude",
"hasPool": true
}

New-construction-only in three Texas ZIPs:

{
"mode": "zip",
"zipCodes": ["78701", "78704", "78745"],
"status_type": "ForSale",
"isNewConstruction": "only",
"maxPropertiesPerZip": 100
}

Furnished pet-friendly rentals in NYC with in-unit laundry:

{
"mode": "zip",
"zipCodes": ["10001", "10011"],
"status_type": "ForRent",
"rentMinPrice": 2500,
"rentMaxPrice": 6000,
"beds_min": 1,
"largeDogsAllowed": true,
"inUnitLaundry": true,
"furnished": true
}

Re-enrich a saved list of ZPIDs (ZPID mode):

{
"mode": "zpid",
"zpids": ["20521978", "95547747", "12345678"]
}

🔁 Resume / checkpoint

Long ZIP-mode runs are checkpointed automatically. If the run is aborted, paused, or hits the platform timeout, resurrecting the run picks up from the last checkpoint instead of restarting — you don't pay for already-enriched properties twice.

Resurrect from the Apify Console: open the aborted run → Resurrect button. The next run instance reads the actor's per-run KVS state and skips the already-processed batch. Live progress is mirrored to the status.html storage record while the run is active.


📡 Live status & storage records

The actor writes structured records to its key-value store on every run. Read them with Actor.getValue(<KEY>) from another actor, or fetch them via the REST API.

KeyWhat it isWhen
RUN_SUMMARYJSON object with search params, property counts, email-discovery rates, billing totals, and any free-tier limits hit during the run.End of every run.
status.htmlAuto-updating HTML status page with live scrape progress, budget, and ETA. Open from the Storage tab while the run is active.Streaming throughout the run.
FREE_LIMITS_APPLIEDArray<{id, message}> — every free-tier ceiling hit during the run (property cap, ZIP cap, budget exhaustion, email masking). Empty / omitted on paid runs.End of every free-plan run.
USER_MESSAGE{id, title, body} onboarding banner shown to a paying user on their 1st and 3rd paid run. Empty / omitted otherwise.First / third paid run only.
SKIPPED_ITEMSPer-item list of zpids / ZIPs / URLs skipped during the run, with reason and category (filter / not_found / error / free_limit). Useful to re-run the error cohort.End of every run.

🤖 Programmatic / API use

Start a run from your own code via the Apify REST API:

TOKEN="<your-apify-token>"
ID=YwkoMBHoFxCLI4gpM
# Start a synchronous run + read dataset
curl -s -X POST "https://api.apify.com/v2/acts/$ID/run-sync-get-dataset-items?token=$TOKEN&format=json" \
-H 'Content-Type: application/json' \
-d '{"mode":"zip","zipCodes":["94103"],"maxPropertiesPerZip":5}'

Read the run-end summary from another script:

RUN_ID="<run-id-from-the-launch-call>"
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID/key-value-store/records/RUN_SUMMARY?token=$TOKEN"

The actor is also available via the Apify MCP server (https://mcp.apify.com) — any MCP-aware client (Claude Desktop, Cursor, etc.) can call it as a tool. Input + output schemas in this repo are the source of truth the MCP client reads to build the call signature.


💡 Tips & Best Practices

🎯 Getting Maximum Results

Start Small & Validate Before Scaling:

  • Start with one ZIP (or one URL) and maxPropertiesPerZip: 5 to verify the filter shape returns what you expect
  • Spot-check the first dataset row — confirm agentEmail, walkScore, photos[] aren't unexpectedly null for your filters
  • Test sold-listings recipes (status_type: "RecentlySold" / searchQueryState with rs:true / /recently_sold/<Location>_rb/ URL) on a small radius before unleashing them on a metro area

Pick the Right Mode for Your Inputs:

  • ZIP mode — broad discovery + full filter control. Layer status_type + price band + home-type checkboxes + tri-state filters for tight targeting.
  • ZPID mode — re-enrich a saved list of known properties (filters + enrichments are deliberately ignored).
  • URL mode — Zillow's own filter UI can express combinations the input schema doesn't (e.g. polygon-drawn map searches). Configure in the browser, copy the resulting URL with searchQueryState=..., paste into zillowUrl.

Layer Filters That Drop Matches Pre-Enrichment:

  • Tri-state filters (isNewConstruction, isAuction, isForSaleForeclosure) drop matches before the per-property paid event fires
  • School filters (schoolsRating, schools) and rental-amenity filters likewise narrow the candidate set without burning enrichments
  • View / waterfront / pool / 3D-tour filters are server-side on Zillow — they don't add cost

💰 Cost Optimization Strategies

Cap Costs Predictably:

  • Use Apify's built-in Maximum cost per run Run option (right panel) to set a hard ceiling — covers all events including apify-actor-start
  • maxPropertiesPerZip controls per-ZIP volume in ZIP mode; metros like NYC / LA can resolve to thousands of listings if uncapped
  • ZPID-mode cost = list length × $0.05–$0.08 (your tier) — easy to predict
  • URL-mode cost depends on what searchQueryState returns; cap upstream by using narrower URL filters

Tune Enrichments to Match the Use Case:

  • enrichWalkScore: true and enrichPhotos: true are off by default — turn them on only when downstream consumers actually use those columns
  • Each enrichment is one extra paid event tier per property
  • For lead-scoring runs, agent contact data ships by default — no extra toggle needed

Cache What You've Already Pulled:

  • Schedule overnight runs and re-process the dataset offline (Excel / SQL / pandas) instead of re-running the actor for filter tweaks
  • The RUN_SUMMARY storage record makes cost auditing easy — pull it for every run

⚡ Performance Optimization

Batching & Bounds:

  • Each ZIP search batches up to 5 ZIPs per upstream call — batches whose resolved city overlaps an earlier batch are skipped automatically (no re-fetching, no double-charging)
  • URL-mode pagination is bounded at 15 s per page — transient slowdowns surface as a normal retry/error, not a 12-minute hang

Watch the Storage Records:

  • SKIPPED_ITEMS lists every ZIP / ZPID / URL skipped, with category (filter / not_found / error / not_resolvable / duplicate / free_limit) — re-run the error cohort, fix the not_resolvable cohort by switching to a wider city/county
  • FREE_LIMITS_APPLIED lists which free-plan caps fired this run (15-property cap / 20-ZIP cap / $1.20 budget / agent-email masking) — empty/omitted on paid runs
  • status.html is a live HTML view of progress; readable in Apify Console's Live View tab while the run is active

🔍 Data Quality & Validation

Spot-Check Before Scaling:

  • Before running on 50 ZIPs, validate one ZIP — open 2–3 dataset rows, click propertyUrl, confirm the data matches the live Zillow listing
  • Cross-check priceHistory and taxHistory arrays for completeness on listings that have been on the market a while
  • Verify agentEmail (when unmasked) on a sample by clicking the agent's Zillow profile

Know What's Nullable:

  • The 74-column dataset has many nullable columns — Zillow doesn't always publish every field for every listing (e.g. FSBO listings often omit agent details; rental listings may omit priceHistory)
  • walkScore / transitScore / bikeScore and photos[] (full gallery) require their respective enrichWalkScore / enrichPhotos toggles and ZIP mode — they don't fire in ZPID or URL modes
  • agentEmail falls through a 3-step lookup chain — the agentEmailSource column tells you which step succeeded (property_details / agent_profile_direct / agent_search_fallback); null here means the email wasn't publicly available on the Zillow listing

Sold Listings Need Home-Type Defaults:

  • Sold-listings queries (/recently_sold/<Location>_rb/ URLs, or searchQueryState URLs with rs:true) require home-type filters to be set or upstream returns zero results
  • The actor sets isSingleFamily / isTownhouse / isCondo / isApartment automatically when none are passed; user filters take priority

❓ FAQ

The actor extracts only publicly available property listing data; private user data is never touched. Listings include personal information for licensed agents (a public role), which can fall under GDPR and similar regimes — make sure you have a legitimate basis to process it. If unsure, consult legal counsel.

What's the difference between ZIP, ZPID, and URL modes?

  • ZIP — discovery from scratch, full filter control.
  • ZPID — enrichment of properties you already know.
  • URL — Zillow's own UI does the filtering; you paste the resulting URL.

ZIP mode is the only mode where input-schema filters and enrichment toggles take effect.

Can I get agent emails and phone numbers?

Yes. agentEmail, cellPhone, agentLicenseNumber, brokerPhoneNumber ship in the output when Zillow exposes them. The agentEmailSource column tells you which lookup path produced the email (property_details → from the Zillow listing page; agent_profile_direct → from the agent's Zillow profile; agent_search_fallback → name-search match).

On the free plan emails are masked (j***@e***.com); upgrade to a paid plan for the unmasked value.

How do I get a Zillow URL for URL mode?

Configure your filters at zillow.com in the browser, then copy the full address-bar URL (it includes a long searchQueryState=... query string) and paste it into zillowUrl.

Why are some fields null?

Zillow doesn't always publish every field for every listing. Common reasons:

  • The data isn't in Zillow's feed for this property.
  • An enrichment toggle was off (walkScore / transitScore / bikeScore need enrichWalkScore: true; full photos needs enrichPhotos: true).
  • The listing is incomplete (e.g. agent details on FSBO listings).

Missing values are returned as null, not the string "N/A".

Can I schedule runs?

Yes — use Apify's built-in Schedules tab on the actor's detail page to trigger the run hourly / daily / on a cron. Each scheduled run is its own paid run.

What happens when a run is aborted mid-way?

The actor checkpoints progress per ZIP / per batch. Resurrecting the aborted run resumes from the last checkpoint (see § Resume / checkpoint). The status.html storage record reflects live progress while the run is active.


You want to scrape Zillow by…Use this actor
Multi-mode (ZIP + ZPID + URL) — full flexibilityThis actor
ZIP code only — simpler input, single-modezillow-zip-search
Search URL — paste any Zillow search pagezillow-url-search
MCP server — connect Claude Desktop / Cursor / ChatGPT to Zillow toolszillow-mcp-server

🛟 Support & feedback

  • Bug or feature request — file an issue from the actor's Issues tab on the Apify Console.
  • Custom solutions / enterprise needs — reach out via the actor's Apify Store page.

⚖️ Disclaimer

This actor is a tool for automated retrieval of publicly available data. You're responsible for using it in compliance with Zillow's terms of service, your jurisdiction's data-protection laws (GDPR, CCPA, etc.), and any third-party licenses. The author makes no representation about the accuracy or fitness of Zillow's underlying data for any purpose.