Zillow Property & Agent Data Scraper
Pricing
Pay per event
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
Actor stats
7
Bookmarked
329
Total users
68
Monthly active users
7.7 hours
Issues response
4 minutes ago
Last modified
Categories
Share
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
- Pick a mode: ZIP codes (discover), ZPID list (enrich knowns), or Zillow URL (browser-configured filters).
- Fill in the inputs for that mode. The other modes' inputs are ignored.
- (Optional) Toggle enrichments — Walk Score and Photos — for ZIP mode.
- Run the actor.
- 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 knownzpidsfor ZPID mode, or paste azillowUrl(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. Set0for 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 Mode —
mode("zip"/"zpid"/"url"). - ZIP Mode Inputs —
zipCodes,maxPropertiesPerZip. - Search Filters (ZIP only) —
status_type,price_min/max,rentMinPrice/MaxPrice,beds_min/max,baths_min/max,doz,sort,keywords. - Home Type —
isSingleFamily,isTownhouse,isCondo,isMultiFamily,isApartment,isManufactured,isLotLand. - Listing Type & Status —
isForSaleByOwner,isForSaleByAgent,isNewConstruction,isAuction,isForSaleForeclosure,includeForeclosed,includePreForeclosure,isPendingUnderContract,isAcceptingBackupOffers. - Property Features —
sqft_min/max,lotSize_min/max,built_min/max,parkingSpots_min,hasGarage,hasPool,hasAirConditioning,isWaterfront,isBasementFinished,isBasementUnfinished,hoa_max. - View Filters —
isCityView,isMountainView,isParkView,isWaterView. - Tour Filters —
isOpenHousesOnly,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 Filters —
schools,schoolsRating,includeUnratedSchools. - ZPID Mode Inputs —
zpids. - URL Mode Inputs —
zillowUrl. - Enrichment Options (ZIP only) —
enrichWalkScore,enrichPhotos.
Highlights:
- Tri-state filters (
isAuction,isNewConstruction,isForSaleForeclosure) acceptany(no filter),only(require), orexclude(block). Flip one toexcludeto 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.
| Event | Trigger | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|---|
apify-actor-start | Actor starts (1 event per GB of memory, minimum 1) | $0.00005 | $0.00005 | $0.00005 | $0.00005 | $0.00005 | $0.00005 |
PROPERTY_ENRICHED | Detailed 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
| Plan | Max properties / run | Max ZIPs / run | Agent emails | Per-event price |
|---|---|---|---|---|
| Free | 15 | 20 | Masked (j***@e***.com) | $0.08 / PROPERTY_ENRICHED |
| Paid (Bronze → Diamond) | Unlimited (budget-bounded) | Unlimited | Full (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.
| Key | What it is | When |
|---|---|---|
RUN_SUMMARY | JSON 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.html | Auto-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_APPLIED | Array<{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_ITEMS | Per-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 datasetcurl -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: 5to 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"/searchQueryStatewithrs: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 intozillowUrl.
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 maxPropertiesPerZipcontrols 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
searchQueryStatereturns; cap upstream by using narrower URL filters
Tune Enrichments to Match the Use Case:
enrichWalkScore: trueandenrichPhotos: trueare 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_SUMMARYstorage 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_ITEMSlists every ZIP / ZPID / URL skipped, with category (filter/not_found/error/not_resolvable/duplicate/free_limit) — re-run theerrorcohort, fix thenot_resolvablecohort by switching to a wider city/countyFREE_LIMITS_APPLIEDlists which free-plan caps fired this run (15-property cap / 20-ZIP cap / $1.20 budget / agent-email masking) — empty/omitted on paid runsstatus.htmlis 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
priceHistoryandtaxHistoryarrays 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/bikeScoreandphotos[](full gallery) require their respectiveenrichWalkScore/enrichPhotostoggles and ZIP mode — they don't fire in ZPID or URL modesagentEmailfalls through a 3-step lookup chain — theagentEmailSourcecolumn tells you which step succeeded (property_details/agent_profile_direct/agent_search_fallback);nullhere 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, orsearchQueryStateURLs withrs:true) require home-type filters to be set or upstream returns zero results - The actor sets
isSingleFamily/isTownhouse/isCondo/isApartmentautomatically when none are passed; user filters take priority
❓ FAQ
Is it legal to scrape Zillow?
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/bikeScoreneedenrichWalkScore: true; fullphotosneedsenrichPhotos: 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.
🔗 Related actors
| You want to scrape Zillow by… | Use this actor |
|---|---|
| Multi-mode (ZIP + ZPID + URL) — full flexibility | This actor |
| ZIP code only — simpler input, single-mode | zillow-zip-search |
| Search URL — paste any Zillow search page | zillow-url-search |
| MCP server — connect Claude Desktop / Cursor / ChatGPT to Zillow tools | zillow-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.