# Redfin Search Scraper — Listings & Agent Copy (`thenetaji/redfin-search-scraper`) Actor

Every home for sale in one US city, ZIP code, neighborhood, county, state or school district, exported as rows with the price, beds, baths, area, MLS number and status, full address and map coordinates. The listing agent's own description is on every row, not behind a second lookup.

- **URL**: https://apify.com/thenetaji/redfin-search-scraper.md
- **Developed by:** [The Netaji](https://apify.com/thenetaji) (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 $0.85 / 1,000 listings

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

## Redfin Search Scraper

Every home for sale in one Redfin region, exported as a spreadsheet of listings in a single
request. Each row carries the asking price and the price per square foot, the bedroom and
bathroom count, the interior area, the year built, the MLS number and status, the full
address, the neighbourhood, the map coordinates, the days on market, and the listing
agent's own written description of the property.

A region is a city, a ZIP code, a neighbourhood, a county, a state or a school district —
anything Redfin publishes a listing page for. The whole region arrives in one response
rather than across a walk, which is not a convenience: it is the only way this site can be
read, and it decides how the Actor is used. That is answered under Frequently asked
questions.

### Accepted input

`region` is required and is the Redfin region page to read, given either as a full link
such as `https://www.redfin.com/zipcode/94110` or as its path, `/zipcode/94110`. A pasted
link is reduced to its path. The address must come from Redfin itself rather than be
assembled: the number in a ZIP-code address is the ZIP code and not Redfin's own identifier
for the region — `94110` is region `39017` — and everything the listing service needs is
read off that page rather than derived from the number.

`maxItems` bounds the run and defaults to `350`, which is what Redfin's own search asks for.
It accepts up to `1000`, and a value of `0` asks for the maximum. On this site it is not a
cap applied after the fact; it is the size of the single request the run makes, and it is
the only lever there is on how much of a region comes back.

```json
{
  "region": "https://www.redfin.com/zipcode/94110",
  "maxItems": 350
}
```

### Response fields

```json
{
  "property_id": "801881",
  "listing_id": "184290163",
  "url": "https://www.redfin.com/CA/San-Francisco/331-Bartlett-St-94110/home/801881",
  "mls_number": "424012345",
  "mls_status": "Active",
  "price": 1095000,
  "price_per_sqft": 712,
  "hoa": 450,
  "beds": 3,
  "baths": 2,
  "sqft": 1538,
  "lot_size": null,
  "year_built": 1900,
  "property_type": 6,
  "ui_property_type": "PROPERTY_TYPE_HOUSE",
  "is_new_construction": false,
  "street": "331 Bartlett St",
  "unit": null,
  "city": "San Francisco",
  "state": "CA",
  "zip": "94110",
  "country_code": "US",
  "neighborhood": "Mission District",
  "latitude": 37.7529,
  "longitude": -122.4194,
  "time_zone": "US/Pacific",
  "days_on_market": 14,
  "time_on_redfin_ms": 1209600000,
  "description": "Beautifully updated Edwardian in the heart of the Mission.",
  "has_3d_tour": true,
  "has_video_tour": false,
  "photo_count": 42
}
```

Fields absent from a listing are returned as null rather than omitted, so every row has the
same shape.

`url` is the whole key on this site. It is the home's page, and it is also the exact value
[Redfin Property Scraper](https://apify.com/thenetaji/redfin-property-scraper) accepts,
which is what makes the two Actors compose: a region is read here, and each home is then
read in depth there without an address ever being constructed. That matters more on Redfin
than the equivalent does elsewhere. The street-address slug in the middle of a Redfin
property address is part of the key rather than decoration, so it cannot be rebuilt from
`property_id`, and a search row is the only reliable way to obtain one.

`property_id` and `listing_id` answer different questions. The first identifies the home and
survives everything that happens to it; the second identifies this particular listing of it.
A property withdrawn and relisted keeps `property_id` and is given a new `listing_id`, so
the pair distinguishes a new listing from a new home.

`description` is the listing agent's own prose and it is on the search row itself. Most
sources hold that back for a per-property request, which turns a region's worth of agent
copy into one request per home; here it arrives with the listings.

`property_type` is Redfin's own numeric code and `ui_property_type` is the label beside it.
Both are republished exactly as they arrive rather than mapped onto names of this Actor's
choosing, because a mapped code is a wrong category that reads like a right one.

`listing_tags` and `key_facts` are Redfin's own marketing tags and fact rows for the
listing, republished whole. No capture has pinned a stable field list for either, so they
are not split into columns that would be null on most rows.

### Behaviour on partial results

A region that exists and has nothing for sale returns a successful run with no rows. A
region address that Redfin serves no listing page for stops the run with a message naming
the shape an address takes, because an empty dataset from a mistyped address is
indistinguishable from a genuinely empty region, and that is the worse of the two failures.
When Redfin refuses rather than answers — a non-zero result code beside an empty listing
set — the run log says so explicitly instead of reporting an empty market.

### Frequently asked questions

**Why is there no page number, and why does `maxItems` behave like a page size?**
Because Redfin has no second page. It ignores `page_number` entirely: page 2 of a measured
region came back byte-identical to page 1, 350 homes, none of them new. It ignores the sort
order as well, so a walk cannot even reach different homes by re-asking in a different
sequence. A paging loop against this site would look like it was working — one request per
page, rows arriving — while collecting the same homes over and over. `maxItems` is the size
of the one request the run makes, and it is the only coverage lever there is.

**The run log says the region may hold more listings. What is the next step?**
A larger request, not another one. When Redfin returns exactly as many listings as were
asked for, the region may hold more, and the run log says so at the end. Raising `maxItems`
toward `1000` is the fix. Measured, `1000` returned San Francisco's entire 814-home for-sale
inventory in one request, and Austin returned a full `1000`.

**What happens to a region that holds more than 1,000 homes for sale?**
It is subdivided. A ZIP code is a region here in exactly the same way a city is, so a city
read as its ZIP codes is several full allowances rather than one, and the same is true of
its neighbourhoods. The run log says plainly when a region came back at the ceiling, which
is when subdividing is worth doing.

**Can sold listings be collected?**
No, and that is a measurement rather than a policy. Redfin's own sold search sends a
parameter that looks like it selects them; sending exactly that returns the for-sale
listings unchanged — 350 results, all 350 already in the for-sale set, every status `Active`
or `Coming Soon`. An input for sold listings would therefore be a label on a lie, so there
is none. Every row this Actor produces is a live for-sale listing.

**Why is `lot_size` null on so many rows?**
Because Redfin declined to state it. It is one of the fields the site gates, and null is the
honest rendering of a value that arrived with no number on it. It is not the same as a lot
size of zero, and it is not a parsing failure. The county's own record of the lot is on the
deep record instead, under `parcel_info` in
[Redfin Property Scraper](https://apify.com/thenetaji/redfin-property-scraper).

**Why did a run for a ZIP code return listings in a different state?**
It did not, if the address came from Redfin. It can, if the address was assembled by hand:
the number in `/zipcode/94110` is the ZIP code, and Redfin's own identifier for that region
is `39017`. An address built from the wrong number resolves to a real region somewhere else
and returns a healthy-looking page of listings for it. The run log prints which region
actually answered, and which market it sits in, for exactly this reason.

**Is a Redfin account, cookie or API key required?**
No. No account, session cookie or key of any kind is supplied to the Actor or needed by it.

### Related Actors

[Redfin Property Scraper](https://apify.com/thenetaji/redfin-property-scraper) reads one
home in depth from the `url` on any row here: the long-form property history and public
records, the agent and brokerage panel, assigned schools, flood, fire, heat and wind risk
scores, the county parcel record, the estimate history and Redfin's own comparable sales.
It costs one request per home, so it is the second pass over a shortlist rather than a way
to read a market.

# Actor input Schema

## `region` (type: `string`):

The Redfin region page to read, as a full link such as https://www.redfin.com/zipcode/94110 or as its path, /zipcode/94110. Cities, ZIP codes, neighborhoods, counties, states and school districts are all regions. Paste the address from Redfin's own URL bar, or use the `url` a search row already carries — a Redfin address cannot be assembled from an identifier.

## `maxItems` (type: `integer`):

How many listings to collect, up to 1000. This is the only coverage lever on this site: the whole region arrives in one request, and there is no second page to fetch, so a larger number is the only way to reach more homes. Redfin's own search asks for 350. Set 0 to ask for the maximum.

## Actor input object example

```json
{
  "region": "/zipcode/94110",
  "maxItems": 25
}
```

# Actor output Schema

## `dataset` (type: `string`):

All records scraped by this run

# 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 = {
    "region": "https://www.redfin.com/zipcode/94110",
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/redfin-search-scraper").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 = {
    "region": "https://www.redfin.com/zipcode/94110",
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/redfin-search-scraper").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 '{
  "region": "https://www.redfin.com/zipcode/94110",
  "maxItems": 25
}' |
apify call thenetaji/redfin-search-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/GnzE9txojuTdmOx1Y/builds/Jxln0pAmsRWRol5gP/openapi.json
