Zillow Leads Scraper: Agents, Price & Tax History, FSBO avatar

Zillow Leads Scraper: Agents, Price & Tax History, FSBO

Pricing

from $0.54 / 1,000 enriched-rows

Go to Apify Store
Zillow Leads Scraper: Agents, Price & Tax History, FSBO

Zillow Leads Scraper: Agents, Price & Tax History, FSBO

Zillow listings enriched with agent/broker contact info, full price-history timeline, 20yr+ tax history, foreclosure/distress flags, FSBO signals, schools, and resoFacts (heating/cooling/basement/construction) at scale. Pay-per-event pricing.

Pricing

from $0.54 / 1,000 enriched-rows

Rating

0.0

(0)

Developer

matata_cK

matata_cK

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

8 days ago

Last modified

Share

Zillow Leads & Property Data

Zillow listings with lead-gen fields most Zillow actors leave out: listing agent name, phone, license and brokerage, event-level price history and tax history reaching back up to 27 years per parcel (64% of our current enriched cache carries 20-plus years; county coverage varies), pre-foreclosure and foreclosure flags, FSBO signals, schools, and the full resoFacts set. 232 fields per row, from $0.50 per 1,000 rows, pay-per-event. Every enriched row also self-reports its own depth (tax_history_years, price_history_events, schools_count), so you can filter by it in any spreadsheet instead of taking the marketing on faith. Catalog metros return in seconds from cache; live collection is paced to stay under Zillow's anti-bot radar. Dedup is built in, so repeat orders never charge you twice for the same listing.

Quickstart

  1. Create a free Apify account. The Free plan includes $5 of monthly credit, enough for roughly 5,500 enriched rows here.
  2. Run the 60-second sample: {"mode": "catalog", "minListings": 25}. Fully enriched cached rows, about $0.02 total.
  3. Point a real order at your area: pick a metro from the dropdown, or paste a lat/lng bounding box into bounds for a precise custom area.
  4. Download CSV/Excel/JSON from the dataset tab, or pull rows through the API, client libraries, or an MCP-connected agent.

Use cases

  • Wholesalers and disposition teams: distressed-seller leads. Rows carry foreclosure type, filing and auction dates, past-due amounts, loan amounts, bank-owned and auction flags across any metro bounding box.
  • Loan officers and title/escrow business development: every agent-listed row carries the listing agent's phone, license number and brokerage. In our current cache, 96.1% of enriched agent-listed rows include a direct agent phone and 99.9% include a brokerage, so a metro order doubles as a call list built from active listings.
  • Investors, iBuyers and AVM analysts: event-level price history per property (date, event, price, source, price per sqft) for comp and flip-margin modeling.
  • Tax-lien buyers, insurers, holding-cost analysts: year-by-year assessed value and tax paid per parcel, keyed by parcel number and county; long-held parcels reach back to the late 1990s. Sort candidates by the shipped tax_history_years column when depth matters.
  • AI and proptech builders: 232 typed fields with a published dataset schema, callable as an MCP tool by Claude, Cursor or any MCP client. Fire-poll-fetch pattern documented below for long-running orders.
  • Market researchers and relocation services: metro snapshots that genuinely include suburbs. A Phoenix order returns Scottsdale, Tempe and Glendale rows too; your run's ORDER_SUMMARY lists the exact cities included.

Output example

Real rows from a live catalog run, trimmed for display. Every row carries detail_url, so you can verify any row against its live Zillow page.

{
"zpid": 7466127,
"mls_id": "7060254",
"detail_url": "https://www.zillow.com/homedetails/10122-W-Turney-Ave-Phoenix-AZ-85037/7466127_zpid/",
"address_street": "10122 W Turney Avenue",
"address_city": "Phoenix",
"address_state": "AZ",
"address_zipcode": "85037",
"price": "300000",
"status_text": "For Sale by Agent",
"bedrooms": 4,
"bathrooms": 2,
"living_area": "2057",
"home_type": "SINGLE_FAMILY",
"days_on_zillow": 2,
"is_enriched": 1,
"agent_name": "Ashley Zavala Salazar",
"agent_phone": "602-736-9971",
"broker_name": "eXp Realty",
"price_per_sqft": "146",
"price_history_events": 6,
"tax_history_years": 24,
"price_history": [
{ "date": "2026-07-30", "price": "300000", "event": "Listed for sale", "source": "ARMLS" },
{ "date": "2020-10-02", "price": "275000", "event": "Sold", "source": "Public Record" }
],
"tax_history": [
{ "year": 2025, "tax_paid": "1792", "assessed_value": "37730" },
{ "year": 2024, "tax_paid": "1591.42", "assessed_value": "39710" }
]
}

Usage examples

Python (apify-client, pip install apify-client):

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("germane_binoculars/zillow-leads-property-data").call(
run_input={"mode": "catalog", "metro": "Chicago", "minListings": 25},
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["address_street"], item["price"],
item.get("agent_name"), item.get("agent_phone"))

JavaScript (npm install apify-client):

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client
.actor('germane_binoculars/zillow-leads-property-data')
.call({ mode: 'catalog', metro: 'Chicago', minListings: 25 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items)
console.log(item.address_street, item.price, item.agent_phone);

Both snippets wait for completion automatically; a catalog order returns in seconds. Full worked workflows, including a dedup-against-previous-export notebook: kmatata/zillow-leads-property-data.

Pricing

You wantModeTypical cost
25-row instant sample (fully enriched)catalog, minListings: 25~$0.02
One-metro snapshot, 1,000 enriched rowscatalog~$0.90
Custom county, 1,000 bare rowscustom_search, depth: listings~$0.50
Full custom metro, 1,000 enriched rowscustom_search, enriched depth~$0.90
Daily new-listings feed, 200 enrichedrecent_activity~$0.18

Billing is per event: actor-start once per run ($0.00001), then listing-row at $0.0005 ($0.50 per 1,000) or enriched-row at $0.0009 ($0.90 per 1,000), whichever depth you actually received. No subscription and no minimum spend. Apify subscription plans stack automatic per-tier discounts on top (Bronze, Silver, Gold — up to ~40% off), applied at run time.

Configuration

Every order needs exactly one mode. The Console form shows all modes' fields at once (Apify can't hide fields conditionally), so use this table to know which section applies to you. Your run's key-value store gets an ORDER_SUMMARY record stating exactly which inputs were applied, the exact bounding box your metro or bounds resolved to (resolved_bounds), and, once rows arrive, which cities were included.

ModeRequiredOptionalIgnored
catalognothingmetro scopes the snapshotbounds, depth, source
custom_searchbounds OR metrodepth, minListings, minEnrichedsource
recent_activitynothing (source defaults to agent)source, minEnrichedbounds, metro, depth
  • Catalog: instant snapshot from our pre-seeded metros (currently New York City, Los Angeles, Chicago, Houston and Phoenix, around 1,000 listings each). Returns in seconds because it never waits on live collection. Scoping it to one metro only filters the cache; it never triggers collection.
  • Custom search: a named metro (no lat/lng math) or a bounds object for a precise area the metro list doesn't cover. In bounds, north/south are latitude, east/west are longitude, and north/east must be numerically larger than south/west; a swapped box is rejected with an explanatory error rather than returning nothing. This catches a whole urbanized area including suburbs, not just the named city. It is also the only mode with the cheaper depth: listings option.
  • Recent activity: Zillow's own sitemap feed of newest and changed listings nationwide, no bounds needed. Price and status changes bump a listing's position even if it has been on market for months, so daily runs plus dedup give you a genuine change feed. source: owner returns FSBO listings where the owner posts their own contact; source: agent is the bulk of inventory. Always fully enriched, because the feed itself carries nothing beyond a listing URL.

Minimums are floors, not targets

minListings (default 1000) floors the total row count and minEnriched (default 500) floors how many of those rows are fully enriched. You may receive more than the minimum in one delivery (KV transport ships in roughly 100-row chunks); you receive less only if timeoutSecs (default 7200) runs out first, in which case you get a partial dataset. Set both lower (10 to 50) for a quick test before committing to a full order. Note that for live-collection modes a low timeoutSecs is raised to the 7200s default (disclosed in ORDER_SUMMARY): cutting the wait just cuts how much of your order completes. minEnriched is forced to 0 internally for custom_search with depth: listings, since bare orders never enrich anything by design.

Bare vs enriched schema

A depth: listings row contains ONLY non-null fields from this whitelist, never agent contact, history or resoFacts, even when we already hold that listing fully enriched (we strip it back down so you never pay the listings rate for enriched-rate data):

GroupFields
Identity / linkzpid, mls_id, detail_url
Price / statusprice, status_type, status_text
Size / typebedrooms, bathrooms, living_area, home_type, lot_area_value, lot_area_unit
Locationaddress_street, address_city, address_state, address_zipcode, latitude, longitude
Timingyear_built, days_on_zillow
Open househas_open_house, open_house_start, open_house_end
Occupancy / ownershipis_non_owner_occupied, is_zillow_owned, country
Listing-type flagsis_fsba, is_fsbo, is_bank_owned, is_coming_soon, is_for_auction, is_foreclosure, is_new_home, is_open_house, is_pending

Enriched rows (the default in every mode) add everything from the use-case list above: agent/broker contact block, foreclosure detail, complete price-history and tax-history timelines, assigned plus nearby schools, HOA fees, mortgage figures, and the resoFacts long tail. Individual fields Zillow didn't return for a specific listing are omitted rather than sent as null.

Why enrichment takes time

Bare listings come straight off search results and arrive fast. Enrichment fetches each listing's detail page, and Zillow runs PerimeterX plus AWS WAF in front of those pages, so we pace requests with jittered timing, warmup ramping and adaptive backoff instead of hammering and risking the whole collection session. Expect a default-sized order (1,000 listings, 500 enriched) to take roughly 90 to 190 real minutes when it needs live collection; cache-satisfiable orders return in seconds regardless. If a run ever looks slow mid-order, that pacing is why, not a stall. If collection is briefly interrupted, your order queues and resumes automatically.

Example inputs

Paste any block into the Input JSON tab (toggle above the input form).

Quick test:

{
"mode": "custom_search",
"bounds": { "north": 40.75, "east": -73.7, "south": 40.7, "west": -73.78 },
"depth": "listings",
"minListings": 10
}

Instant catalog sample:

{ "mode": "catalog" }

Catalog scoped to one metro:

{ "mode": "catalog", "metro": "Chicago" }

Full metro order by name:

{
"mode": "custom_search",
"metro": "Phoenix",
"depth": "enriched",
"minListings": 1000,
"minEnriched": 500
}

Bare listings for a precise area:

{
"mode": "custom_search",
"bounds": { "north": 34.34, "east": -118.12, "south": 33.7, "west": -118.67 },
"depth": "listings",
"minListings": 1000
}

Daily change feed:

{ "mode": "recent_activity", "source": "agent" }

FSBO only:

{ "mode": "recent_activity", "source": "owner" }

Repeat order with dedup:

{
"mode": "custom_search",
"bounds": { "north": 40.75, "east": -73.7, "south": 40.7, "west": -73.78 },
"dedupZpids": [43814015, 43828670],
"dedupMlsIds": ["A12046823"]
}

FAQ

How much does 1,000 Zillow listings with agent contact cost?

About $0.90: 1,000 enriched rows at $0.0009 each, plus a $0.00001 start fee. Bare listings at the same size cost about $0.50.

Does this give me the owner's phone number?

Only on FSBO listings, where the owner posts their own contact; run mode: recent_activity with source: owner for those. On agent-listed homes you get the listing agent's phone, license and brokerage instead, which is what 96% of our enriched agent-listed rows carry.

Is scraping Zillow legal? Public listing data has been collected within the bounds set by hiQ v. LinkedIn, but terms-of-service, copyright and privacy obligations still apply downstream. We ship only public listing fields, no raw HTML and no gated data, and compliant outreach (TCPA/CAN-SPAM) remains your responsibility.

Why is enrichment slower than other Zillow scrapers?

Because we refuse to burn sessions. Detail pages sit behind PerimeterX and AWS WAF, so we pace collection to keep the session alive rather than bursting and getting blocked. See "Why enrichment takes time" above.

How is this different from other Zillow actors on the store?

Depth and pricing. Most ship search-card fields only; this actor adds the agent contact block, event-level price history, 20-plus years of tax history, foreclosure detail and schools as standard enriched fields, at $0.90 per 1,000 enriched rows. For breadth-only needs, the related actors below may serve you fine.

Can AI agents call this?

Yes. It works through Apify's actors-mcp-server (local stdio or hosted at mcp.apify.com). The input schema is written self-documenting for agents, and the published dataset schema (232 typed fields) lets an agent know the result shape up front. See the MCP section below.

How do I avoid paying twice on repeat orders?

Copy the DEDUP_UPDATE record from your previous run's key-value store into dedupZpids/dedupMlsIds. Already-shipped zpids and MLS IDs are never re-shipped or re-charged. Re-listings get a new zpid but keep their MLS ID, which is why both fields exist.

My run finished SUCCEEDED with zero rows. What happened?

Usually rejected input, such as a malformed bounds box. Check the run's Key-value store tab (not Dataset): a rejected order writes a STATUS key there containing the reason. We don't fail the run outright because the collection service itself worked.

Related Apify actors for narrower jobs:

Integrations: connect through Make, Zapier, n8n, Google Sheets, webhooks, or the Apify API and client libraries (JavaScript, Python, CLI).

Support

Issues and feature requests: github.com/kmatata/zillow-leads-property-data/issues. For anything order-specific, check the run's key-value store first (see the zero-rows question above), then reach out through the Apify Store page. Custom extraction needs beyond the current input schema are welcome there too.

Dedup details

Every delivery that returns at least one row writes a DEDUP_UPDATE key to that run's key-value store: the union of what you told us you had plus everything just shipped. Copy its two arrays into dedupZpids and dedupMlsIds on your next order. The record lives in the key-value store rather than the dataset deliberately, so it never appears as a stray non-listing row when you iterate items programmatically.

Use from an AI agent (MCP)

This Actor is callable as a tool via Apify's actors-mcp-server. Two connection modes:

  • Local stdio (npx @apify/actors-mcp-server): exposes any Actor your API token can run. Filter to just this Actor with --tools germane_binoculars/zillow-leads-property-data.
  • Hosted remote (https://mcp.apify.com): discovers Actors from the Apify Store, with output-schema inference that the local path lacks.

The input fields are written self-documenting for agents: each description states when the field applies and how its value is interpreted, so an LLM can construct a valid call without reading this README. Catalog mode is the fastest smoke test: cached rows in seconds, no bounds, no polling. Here is an AI agent doing exactly that through Apify's MCP server: one tool call, three enriched listings summarized back with agent contacts (phone numbers redacted here):

An AI agent calling this actor via the Apify MCP server and receiving enriched Zillow leads

Long-running orders: fire, poll, fetch

Live-collect orders take real wall-clock time, so the natural agent flow has three steps:

  1. Fire: call the Actor tool with your input and a generous timeoutSecs. The tool blocks up to 45 seconds (its own waitSecs cap, separate from your timeoutSecs), then returns a status line like "RUNNING for 40s. 2 results so far" plus a nextStep telling you how to poll. That is not an error; the run is collecting.
  2. Poll: follow nextStep with get-actor-run (waitSecs: 30) repeatedly until status reads SUCCEEDED, or TIMED-OUT with partial results.
  3. Fetch: call get-dataset-items with the returned datasetId.

Set timeoutSecs comfortably above the worst case for your order size; see "Minimums are floors" and "Why enrichment takes time" for what to expect.