# Zillow Scraper 🏠 (for sale, rent and sold, all-in price) (`tagadanar/zillow-listings-search`) Actor

Every Zillow search result for a city or ZIP in clean JSON: price, beds, baths, square feet, Zestimate, rent Zestimate, status, days on Zillow, address and coordinates. For sale, for rent or sold, with the filters you would set on the site. One flat price per listing, platform usage included.

- **URL**: https://apify.com/tagadanar/zillow-listings-search.md
- **Developed by:** [Tagada Data](https://apify.com/tagadanar) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.23 / 1,000 listing scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Zillow Scraper: for sale, for rent and recently sold

Type a city or a ZIP code and get every Zillow search result back as clean JSON. Price, bedrooms, bathrooms, square feet, lot size, days on Zillow, status, the full address, latitude and longitude, the brokerage, the photo and the link to the listing, plus the Zestimate, rent Zestimate and county assessed value wherever Zillow publishes them. For sale, for rent, or recently sold, with the same filters you would set on the site.

One flat price per listing delivered. Platform usage (proxies, compute) is already in that price, so the number you see is the number you pay. No API key, no login, no browser.

### What it costs against the paid tools

| | This actor | Typical Zillow actor on Apify | Zillow's own data |
| --- | --- | --- | --- |
| Price | $1.75 per 1,000 listings, all-in | $2.40 per 1,000 (search), $3.60 per 1,000 (per listing page) | No public API for listing search |
| Platform usage | Included | Usually billed on top | n/a |
| Markets | For sale, for rent, sold, in one actor | Usually one per actor | n/a |
| Zestimate and rent Zestimate | On every row Zillow publishes one for | Often detail-page only | Sign-in |
| Setup | Paste a city name | Paste a city name | Contact sales |

If you have used a scraper that charges per listing page, note the difference: the fields most people actually want (price, beds, baths, sqft, Zestimate, days on market, coordinates) are all in Zillow's search results already. This actor reads those and charges you once. It deliberately does not open each listing's own page, because that page costs about twenty times more to fetch and adds a description and a few facts. Paying detail-page prices for search-page data is the most common way to overspend on this category.

### What you get

One record per listing:

| Field | Description |
| --- | --- |
| `recordType` | Always `listing` |
| `zpid` | Zillow's property ID |
| `url` | Link to the listing |
| `status` | `FOR_SALE`, `FOR_RENT`, `SOLD`, `PENDING`, and so on |
| `statusText` | Status in Zillow's own words, e.g. "House for sale" |
| `listingType` | Which market this search read: `forSale`, `forRent` or `sold` |
| `price` | Price as a number. For rentals, monthly rent |
| `priceText` | Price as shown on the card, e.g. `$330,000` or `$2,101,990+` |
| `currency` | Currency code |
| `beds` | Bedrooms |
| `baths` | Bathrooms |
| `livingArea` | Living area in square feet |
| `lotAreaValue`, `lotAreaUnit` | Lot size and its unit (sqft or acres) |
| `homeType` | Single family, Condo, Townhouse, Multi family, Lot, Manufactured, Apartment |
| `zestimate` | Zillow's estimated value |
| `rentZestimate` | Zillow's estimated monthly rent |
| `taxAssessedValue` | Assessed value from the county record |
| `daysOnZillow` | How long the listing has been up |
| `address` | Full address on one line |
| `addressStreet`, `addressCity`, `addressState`, `addressZipcode` | The same address split into fields |
| `isUndisclosedAddress` | True when the seller withheld the street address |
| `latitude`, `longitude` | Coordinates |
| `brokerName` | Brokerage on the listing |
| `listingAgent` | Agent attribution line, when there is one |
| `isZillowOwned` | Zillow-owned listing |
| `isBuilding` | True for a rental building or complex rather than a single unit |
| `hasImage`, `imgSrc` | Whether there is a photo, and its URL |
| `searchLocation` | The city or ZIP you asked for, so multi-city runs stay sortable |
| `searchUrl` | The exact Zillow URL the row came from |
| `capturedAt` | When the run collected the row (ISO) |

Fields are null when Zillow genuinely does not publish them for that listing, which is common for the Zestimate and the assessed value. Nothing is guessed or filled in.

### Who uses this

- **Investors and flippers.** Pull a whole ZIP for sale and recently sold in two runs, compare asking prices against sold prices and the Zestimate, and find the gap.
- **Agents and teams.** Watch inventory in your farm area: what came on this week, what is sitting, what sold and at what price.
- **Property managers and rental operators.** Read the rental market in your city at a given bedroom count and set your own rent from real comparables, with `rentZestimate` next to the asking rent.
- **Proptech and analysts.** Coordinates on every row means the output drops straight into a map, a BI tool, or a model, with no geocoding step.
- **iBuyer and renovation lead-gen.** Filter old, low-priced, single-family inventory in a metro and get a working list of addresses.

### Input examples

Every listing in one city, for sale:

```json
{
  "locations": ["Austin, TX"],
  "listingType": "forSale",
  "maxListingsPerSearch": 200
}
```

Several ZIP codes, houses only, in a price band, newest first:

```json
{
  "locations": ["78704", "78745", "78702"],
  "listingType": "forSale",
  "minPrice": 400000,
  "maxPrice": 900000,
  "minBeds": 3,
  "homeTypes": ["houses"],
  "sortBy": "newest",
  "maxListingsPerSearch": 300
}
```

Recently sold comparables for a neighbourhood:

```json
{
  "locations": ["Austin, TX"],
  "listingType": "sold",
  "minBeds": 3,
  "maxListingsPerSearch": 500
}
```

Rentals under a budget, cheapest first:

```json
{
  "locations": ["Denver, CO"],
  "listingType": "forRent",
  "maxPrice": 2500,
  "sortBy": "priceAsc"
}
```

A search you drew on Zillow's map, pasted straight from the browser:

```json
{
  "locations": ["https://www.zillow.com/austin-tx/?searchQueryState=%7B%22mapBounds%22%3A%7B%22west%22%3A-97.85%2C%22east%22%3A-97.68%2C%22south%22%3A30.19%2C%22north%22%3A30.32%7D%7D"],
  "maxListingsPerSearch": 200
}
```

### Frequently asked questions

**How many listings can I get from one city?**
Zillow itself stops paginating a search at 20 pages, about 820 listings, no matter how many results it says it has. That is a limit on Zillow's side, not on this actor. To go past it, split the area into ZIP codes or add a price band, and run several searches: the run log tells you when a search hit that ceiling.

**Does it return sold prices?**
Yes. Set `listingType` to `sold` and you get recently sold homes with their sale price in `price`, plus the Zestimate and the county assessed value next to it. That is the comparison most people are after.

**Can I search by neighbourhood or by drawing an area?**
Yes. Do the search on zillow.com, copy the URL out of the address bar, and paste it into `locations`. A map-drawn boundary travels with the URL and is used as-is.

**Do I need a proxy, an API key or a Zillow login?**
No. Everything needed to reach Zillow is built in and paid for out of the per-listing price. There is no proxy setting to configure and nothing to sign in to.

**Is the Zestimate really included?**
Yes, on every row Zillow publishes one for, along with the rent Zestimate and the county assessed value. They come straight from Zillow's own search payload, not from an estimate of ours. Zillow does not attach a Zestimate to every listing (on a typical for-sale city page it is roughly a third of rows, and the assessed value is on about seven in ten), so those fields are null where the site itself has nothing.

**Why do I sometimes get slightly fewer rows than I asked for?**
Zillow returns 41 results per page, so a cap of 100 lands mid-page, and a listing that Zillow serves without an ID or a link is dropped rather than shipped half-empty. You are only charged for rows you actually receive.

**What happens if Zillow blocks the run?**
Zillow uses PerimeterX. This actor rotates its exit and its browser fingerprint on every retry, and if pages are still being refused it stops instead of running up your bill, and says so in the run status. Re-running a few minutes later almost always clears it.

**Is scraping this legal?**
The actor reads only public listing pages that Zillow serves to any visitor, and collects no personal data about buyers, sellers or agents beyond the brokerage attribution printed on the listing. Check Zillow's terms and your own use case before you build a product on it.

### Related actors

Doing comparables or yield work in other markets? See our [Rightmove](https://apify.com/tagadanar/rightmove-scraper) scraper for the UK, [ImmobilienScout24](https://apify.com/tagadanar/immobilienscout24-scraper) for Germany, and the French set ([SeLoger](https://apify.com/tagadanar/french-real-estate-seloger), [PAP](https://apify.com/tagadanar/french-real-estate-pap), [DVF sold prices](https://apify.com/tagadanar/french-real-estate-dvf)).

Keywords: zillow scraper, zillow api, zillow data extractor, zillow listings, zillow sold prices, zillow rentals, real estate scraper, mls data alternative, property data, zestimate api, housing market data, real estate comps, zip code home search.

# Actor input Schema

## `locations` (type: `array`):

One per line. A city with its state (<code>Austin, TX</code>), a ZIP code (<code>78704</code>), a neighbourhood slug, or a Zillow search URL you pasted from your browser (map-drawn searches keep their boundary). Mix them freely.

## `listingType` (type: `string`):

Which market to read. Ignored for a pasted URL that already says <code>/rentals/</code> or <code>/sold/</code>.

## `minPrice` (type: `integer`):

In dollars. For rentals this is monthly rent. Leave empty for no minimum.

## `maxPrice` (type: `integer`):

In dollars. For rentals this is monthly rent. Leave empty for no maximum.

## `minBeds` (type: `integer`):

Leave empty for any.

## `minBaths` (type: `integer`):

Leave empty for any.

## `homeTypes` (type: `array`):

Leave empty for every type Zillow shows by default.

## `sortBy` (type: `string`):

Which listings you get first, and therefore which ones you get at all when you cap the results.

## `maxListingsPerSearch` (type: `integer`):

Stop each city / ZIP after this many listings. Zillow itself never paginates past 820 results for one search, so narrow the filters if you need more than that from one place. You are only charged for listings actually delivered.

## Actor input object example

```json
{
  "locations": [
    "Austin, TX"
  ],
  "listingType": "forSale",
  "sortBy": "relevance",
  "maxListingsPerSearch": 40
}
```

# Actor output Schema

## `listings` (type: `string`):

One item per listing in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "locations": [
        "Austin, TX"
    ],
    "listingType": "forSale",
    "maxListingsPerSearch": 40
};

// Run the Actor and wait for it to finish
const run = await client.actor("tagadanar/zillow-listings-search").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "locations": ["Austin, TX"],
    "listingType": "forSale",
    "maxListingsPerSearch": 40,
}

# Run the Actor and wait for it to finish
run = client.actor("tagadanar/zillow-listings-search").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "locations": [
    "Austin, TX"
  ],
  "listingType": "forSale",
  "maxListingsPerSearch": 40
}' |
apify call tagadanar/zillow-listings-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tagadanar/zillow-listings-search"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Hu7imr4fbYKhPEkZu/builds/vZY8a8zciFqQULxpy/openapi.json
