---
title: "Redfin scraper"
description: "Scrape Redfin property pages: price, beds, baths, square feet, sale and tax history, schools and listing agent as JSON. One API request from Python, Node.js or MCP."
canonical: https://www.zenrows.com/scrape/redfin
updated: 2026-10-20
---

# Redfin scraper

The Zenrows Redfin scraper turns a Redfin property URL into structured JSON: address, list price, beds, baths, square feet, year built, MLS ID, listing agent, sale history, tax history, assigned schools, climate risk scores and the Redfin Estimate. Fetch in Adaptive Stealth Mode opens the page and Extract returns the fields. Run it from an API call, the CLI or an AI agent.

- Site: redfin.com
- Returns: Home facts, sale and tax history, schools and agents

## Run it: one Redfin home in, JSON out

```json Input
{
  "url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906",
  "mode": "auto",
  "extract": "auto"
}
```

```json Output
{
  "address": {
    "street_address": "426 Bradshaw St",
    "city": "Cedar Hill",
    "state": "TX",
    "postal_code": "75104",
    "county": "Dallas County",
    "latitude": 32.5957926,
    "longitude": -96.9625839
  },
  "home_facts": {
    "bedrooms": 3,
    "bathrooms_total": 2,
    "square_feet": 1686,
    "year_built": 1984,
    "stories": 1,
    "property_type": "single_family",
    "parking": "2 car garage",
    "zoning_code": "SF-10",
    "apn": "160335200E0150000"
  },
  "listing": {
    "list_price": 304516,
    "mls_id": "13920975",
    "listing_source": "NTREIS",
    "date_listed": "Aug 24, 2018",
    "listing_url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906"
  },
  "listing_agent": {
    "agent_name": "Cynthia Peterson",
    "brokerage": "RE/MAX Preferred Associates"
  },
  "sale_history": [
    { "event_date": "Aug 27, 2018", "event_type": "price_changed", "price": 214900, "price_per_sqft": 127, "source": "NTREIS #13920975" },
    { "event_date": "Aug 24, 2018", "event_type": "listed", "price": 220000, "price_per_sqft": 130, "source": "NTREIS #13920975" }
  ],
  "tax_history": [
    { "year": 2025, "assessed_value": 316980, "land_value": 55000, "improvement_value": 261980, "property_tax": 7286 }
  ]
}
```

Request and output of a live call to a Redfin property page on Oct 20, 2026, trimmed for length. A field is returned when the page shows it.

## What data can you scrape from Redfin?

A Redfin property page is one of the richest public records of a US home: listing facts, a full sale and tax history, schools and neighborhood scores. These are the fields teams use most, with the path where each lives in the JSON.

| Field | What it holds | Example |
|---|---|---|
| `address.street_address`, `address.city`, `address.postal_code` | The home's address | `426 Bradshaw St`, `Cedar Hill`, `75104` |
| `address.county` | County, for joining to tax and permit data | `Dallas County` |
| `address.latitude`, `address.longitude` | Coordinates | `32.5957926`, `-96.9625839` |
| `listing.list_price` | The price on the page, in US dollars | `304516` |
| `listing.mls_id` | The MLS number of the listing | `13920975` |
| `listing.listing_source` | The MLS the listing came from | `NTREIS` |
| `home_facts.bedrooms`, `home_facts.bathrooms_total` | Beds and baths | `3`, `2` |
| `home_facts.square_feet` | Living area | `1686` |
| `home_facts.year_built` | Year of construction | `1984` |
| `home_facts.property_type` | Single family, condo, townhouse and so on | `single_family` |
| `home_facts.apn` | Assessor's parcel number | `160335200E0150000` |
| `listing_agent.agent_name`, `listing_agent.brokerage` | Listing agent and brokerage | `Cynthia Peterson`, `RE/MAX Preferred Associates` |
| `redfin_estimate.difference_from_list` | Gap between the Redfin Estimate and the list price | `90000` |
| `sale_history[]` | Every listing, price change and sale with date, price and price per sq ft | `{ "event_type": "listed", "price": 220000, … }` |
| `tax_history[]` | Assessed, land and improvement value and the tax bill per year | `{ "year": 2025, "property_tax": 7286, … }` |
| `schools[]` | Assigned schools with grades, distance and rating | `{ "name": "Cedar Hill High School", "rating": "2/10" }` |
| `climate_risks[]` | Flood, fire, heat, wind and air scores out of 10 | `{ "risk_type": "flood", "score": "1/10" }` |
| `lifestyle_scores[]` | Car dependence, bikeability, noise and calm scores | `{ "descriptor": "Car required", "score": 2.3 }` |

## How to scrape Redfin with Zenrows

1. **Create a free Zenrows account** and copy your API key from the dashboard.
2. **Pick the Redfin URL.** Property pages follow `/STATE/City/Street-Address-ZIP/home/ID`. Search pages by city (`/city/30818/TX/Austin`) or ZIP code (`/zipcode/78704`) list the homes whose pages you then request.
3. **Send it with the default configuration**: Adaptive Stealth Mode to open the page and Extract to return the fields.
4. **Read `parsed`** from the response. `home_facts` and `listing` give you the current state; `sale_history` and `tax_history` give you the past.
5. **Run your property list in Batch**, with the Redfin home ID or the APN as the ID of each task.

```python Python
# pip install requests
import json
import requests

params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906",
    "mode": "auto",     # Adaptive Stealth Mode
    "extract": "auto",  # structured JSON
}
response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
response.raise_for_status()

home = response.json()["parsed"]
print(home["address"]["street_address"], home["home_facts"]["square_feet"], home["listing"]["list_price"])
for event in home["sale_history"]:
    print(event["event_date"], event["event_type"], event["price"])
```

```javascript Node.js
// Node 18+ (built-in fetch)
const params = new URLSearchParams({
  apikey: "YOUR_ZENROWS_API_KEY",
  url: "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906",
  mode: "auto",
  extract: "auto",
});

const response = await fetch(`https://api.zenrows.com/v1/?${params}`, {
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);

const { parsed } = await response.json();
console.log(parsed.home_facts.bedrooms, parsed.home_facts.bathrooms_total, parsed.listing.mls_id);
console.table(parsed.tax_history);
```

```bash cURL
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.redfin.com%2FTX%2FCedar-Hill%2F426-Bradshaw-St-75104%2Fhome%2F31248906&mode=auto&extract=auto"

# Only the fields you need
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.redfin.com%2FTX%2FCedar-Hill%2F426-Bradshaw-St-75104%2Fhome%2F31248906&mode=auto&extract=auto&extract_fields=listing.list_price%2Chome_facts.square_feet%2Csale_history%2Ctax_history"
```

```python Python SDK
# pip install zenrows
from zenrows import ZenRowsClient

client = ZenRowsClient("YOUR_ZENROWS_API_KEY")
response = client.get(
    "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906",
    params={"mode": "auto", "extract": "auto"},
)
for school in response.json()["parsed"]["schools"]:
    print(school["name"], school["grades"], school["distance_miles"], school.get("rating"))
```

## Input

The scraper takes one Redfin URL per request, plus the options below. The same parameters go into every task of a Batch job, so the input for a portfolio is a list of property URLs with these settings.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | string | required | A Redfin property page (`/home/ID`), or a city, ZIP or neighborhood search |
| `mode` | string | `auto` | Adaptive Stealth Mode: starts with the lightest setup and adds rendering and protected-site handling only when the page needs them |
| `extract` | string | `auto` | Returns the page's fields as JSON in `parsed`, next to the raw `html` |
| `extract_fields` | string | all fields | Comma-separated paths to keep. A bare group name keeps the whole group, such as `sale_history` |
| `proxy_country` | string | any | Two-letter country code. Redfin covers the US and Canada; set `us` if you want every request to come from the US |
| `wait_for` | string | none | A CSS selector to wait for before the page is read |
| `session_id` | integer | none | Reuse the same session across several requests, up to 10 minutes |

```json Batch input
{
  "type": "regular",
  "status": "closed",
  "zenrows_params": {
    "mode": "auto",
    "extract": "auto"
  },
  "tasks": [
    { "url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906", "external_id": "31248906" },
    { "url": "https://www.redfin.com/zipcode/78704", "external_id": "search-78704" },
    { "url": "https://www.redfin.com/city/30818/TX/Austin", "external_id": "search-austin" }
  ]
}
```

## Output

Every response is JSON with two keys: `html`, the page as Redfin served it, and `parsed`, the structured record. A property page returns the groups below, plus `photos`, `payment_estimate` and `description` when the page carries them. The sample is the trimmed output of a live request; this home is off market, so a few listing fields are `null`.

```json Property page
{
  "address": {
    "street_address": "426 Bradshaw St",
    "city": "Cedar Hill",
    "state": "TX",
    "postal_code": "75104",
    "county": "Dallas County",
    "neighborhood": null,
    "latitude": 32.5957926,
    "longitude": -96.9625839
  },
  "listing": {
    "list_price": 304516,
    "listing_status": null,
    "days_on_redfin": null,
    "mls_id": "13920975",
    "listing_source": "NTREIS",
    "date_listed": "Aug 24, 2018",
    "listing_url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906"
  },
  "listing_agent": {
    "agent_name": "Cynthia Peterson",
    "brokerage": "RE/MAX Preferred Associates"
  },
  "redfin_estimate": {
    "estimated_value": null,
    "difference_from_list": 90000
  },
  "sale_history": [
    { "event_date": "Aug 27, 2018", "event_type": "price_changed", "price": 214900, "price_per_sqft": 127, "source": "NTREIS #13920975" },
    { "event_date": "Aug 13, 2014", "event_type": "listed", "price": 134900, "price_per_sqft": 80, "source": "NTREIS #13000862" }
  ],
  "tax_history": [
    { "year": 2025, "assessed_value": 316980, "land_value": 55000, "improvement_value": 261980, "property_tax": 7286 },
    { "year": 2024, "assessed_value": 316980, "land_value": 55000, "improvement_value": 261980, "property_tax": 7282 }
  ],
  "schools": [
    { "name": "Bray Elementary School", "type": "public", "grades": "K-5", "assignment": "assigned", "distance_miles": 0.4, "rating": null },
    { "name": "Bessie Coleman Middle School", "type": "public", "grades": "6-8", "assignment": "assigned", "distance_miles": 2.7, "rating": "4/10" }
  ],
  "climate_risks": [
    { "risk_type": "flood", "score": "1/10", "description": "Minimal risk of a flood reaching the building in the next 30 years" },
    { "risk_type": "heat", "score": "7/10", "description": "7 days above 110° expected this year, 24 days in 30 years" }
  ],
  "lifestyle_scores": [
    { "category": "Almost all errands need a car", "descriptor": "Car required", "score": 2.3 },
    { "category": "Virtually no nearby noise", "descriptor": "Silent zone", "score": 9.9 }
  ]
}
```

Each history array is a list of flat objects, so `pd.DataFrame(home["tax_history"])` gives you a ready table per home. For an LLM, ask for the page as Markdown instead of JSON.

## Scrape Redfin from your coding agent

Claude Code, Cursor, Codex and any MCP client can scrape Redfin from a prompt.

The agent sends the same Fetch and Extract request and reads the same groups, so "how much has the tax bill on this house grown since 2015" is one request and a subtraction. Connect the Zenrows MCP server once, or install the CLI and let the agent run it in the terminal.

```bash Connect the MCP server (Claude Code)
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp
```

```json MCP config (Cursor, Claude Desktop, Windsurf)
{
  "mcpServers": {
    "zenrows": {
      "command": "npx",
      "args": ["-y", "@zenrows/mcp"],
      "env": { "ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY" }
    }
  }
}
```

```bash Or run the CLI
npx -y @zenrows/cli init
zenrows extract "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906"
```

```text Then ask your agent
Use Zenrows to open this Redfin home and summarize it for a buyer:
https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906
Include beds, baths, square feet, year built, every sale and price
change, the property tax trend since 2015, the assigned schools with
ratings, and the flood and heat risk scores.
```

## Scrape Redfin at scale with Batch and Crawl

**Batch** runs a list of Redfin URLs as one job, in parallel, with your own ID on each result. Use the home ID at the end of the URL as `external_id`, and a portfolio of thousands of addresses comes back as one dataset you can join to your records.

**Crawl** collects the property pages for you. Start it on a ZIP code or city search, keep only links that contain `/home/`, and it returns every property page it reaches. Feed that list to Batch with Extract for the structured records.

```bash Batch (cURL)
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "regular",
    "status": "closed",
    "zenrows_params": { "mode": "auto", "extract": "auto" },
    "tasks": [
      { "url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906", "external_id": "31248906" },
      { "url": "https://www.redfin.com/zipcode/78704", "external_id": "search-78704" }
    ]
  }'

# Read the results when the job finishes
curl "https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY"
```

```bash Batch (CLI)
# homes.jsonl: one {"url": "...", "external_id": "...", "extract": "auto"} per line
zenrows batch create homes.jsonl --wait
zenrows batch results <job-id> --out redfin-homes.jsonl
```

```bash Crawl (cURL)
curl -X POST "https://api.zenrows.com/v1/crawls" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.redfin.com/zipcode/78704",
    "depth": 2,
    "max_pages": 300,
    "include_patterns": ["/home/"]
  }'
```

**Scheduled jobs** run the same Batch job on a timetable, on Zenrows' side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can tell your app when a run finishes, and you can pause or resume the schedule at any time, with no cron server to keep alive.

```bash Scheduled job (cURL)
# Runs every day on Zenrows' side; each run keeps its own results
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "type": "scheduled",
      "status": "closed",
      "schedule": {
        "rate": {
          "every": 1,
          "unit": "day"
        }
      },
      "zenrows_params": {
        "mode": "auto",
        "extract": "auto"
      },
      "tasks": [
        {
          "url": "https://www.redfin.com/TX/Cedar-Hill/426-Bradshaw-St-75104/home/31248906",
          "external_id": "redfin"
        }
      ]
    }'

# Or at set times: "schedule": { "calendar": { "times_of_day": ["09:00"],
#   "cadence": { "weekly": { "days": ["mon", "thu"] } } }, "timezone": "Europe/Madrid" }
```

## What teams do with Redfin data

| Use case | Data they pull | Who runs it |
|---|---|---|
| Automated valuation and comps | Square feet, beds, baths, year built, sale history and coordinates | Lenders, appraisers and AVM teams |
| Property tax analysis | Assessed, land and improvement values and the tax bill per year | Tax consultants and investors appealing assessments |
| Portfolio monitoring | List price, status and price changes on homes you own or finance | Single-family rental operators and lenders |
| Insurance and climate risk | Flood, fire, heat, wind and air scores per address | Insurers and risk analysts |
| Buyer and relocation tools | Assigned schools, ratings, lifestyle scores and the full home record | Proptech and relocation teams |
| Agent and brokerage research | Listing agent and brokerage per listing and MLS | Brokerages and recruiting teams |

## Tips for scraping Redfin

- **Keep the home ID.** Every property URL ends in `/home/NUMBER`. Store it as your key; the street part of the URL is for people and can change.
- **Read `listing.listing_url` back.** Redfin redirects an old or wrong address slug to the right one, so the canonical URL in the response is the one to store.
- **Collect URLs from ZIP code searches.** `/zipcode/78704` lists the homes in one ZIP. Crawl it with a `/home/` filter, then send the property pages to Batch.
- **Ask only for the groups you need.** A bare group name such as `tax_history` keeps the whole array, which cuts the response for tax or history jobs.
- **Use the APN to join public records.** `home_facts.apn` is the county parcel number, the same key county assessors use.
- **Read `event_type` in the sale history.** Listings, price changes, pending and sold events sit in one array. Filter on the type before you compute days on market or sale prices.

## Frequently asked questions

### Is it legal to scrape Redfin?

Collecting publicly available property data from Redfin, such as prices, home facts and sale history shown to every visitor, is common practice for valuation and market research. Pages name listing agents, and personal data is protected by laws such as the CCPA and the GDPR, so collect only what you have a legitimate reason to use. Check Redfin's terms of use for your use case and ask your lawyer if you are unsure.

### Is there an official Redfin API?

No. Redfin does not offer a public API for property or listing data. Its Data Center publishes downloadable housing market files, such as median sale price and inventory by metro, city and ZIP code, but not individual homes. For property-level data, teams request the property pages with a Redfin scraper API.

### What Redfin pages can I scrape?

Property pages ( /STATE/City/Address-ZIP/home/ID ) return the full record shown on this page. City, ZIP code and neighborhood searches give you the list of homes to request.

### Can I scrape Redfin sold homes and sale history?

Yes. Every property page carries sale_history , with each listing, price change and sale, its date, price and price per square foot, and the MLS it came from. Request the homes in a ZIP code to build a set of recent sales.

### How do I scrape Redfin with Python?

Send a GET request to https://api.zenrows.com/v1/ with your API key, the Redfin URL, Adaptive Stealth Mode and Extract turned on, as in the Python example above. response.json()["parsed"] holds the home as a dictionary of groups. The zenrows package on PyPI wraps the same request.

### How many Redfin homes can I scrape?

Send one URL per request, or a whole list as a Batch job that runs the URLs in parallel and keeps your ID on each result. One Batch job accepts more than 10,000 URLs, sent inline or as a CSV file.

### Can I get the same fields from Redfin, Zillow and other real estate sites?

Yes. Extract has a canonical real estate schema: send extract=standard instead of extract=auto and Redfin, Zillow and Idealista pages come back with the same field names. The default stays auto , which keeps Redfin's own groups such as tax_history and climate_risks .

### Can AI agents scrape Redfin with Zenrows?

Yes. Connect the Zenrows MCP server to Claude, Cursor, Codex or any MCP client, or give your agent the Zenrows CLI. Ask about a home or a ZIP code in plain language and the agent calls Fetch and Extract for you.

### Can I scrape Redfin on a schedule?

Yes. Create a scheduled Batch job with your Redfin URLs and the same settings as a single request, and Zenrows runs it on its side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can notify your app when a run finishes, and the schedule can be paused or resumed at any time.

### Which integrations work with the Redfin scraper?

Zenrows works with n8n, Make, Zapier, Clay, Dify, LangChain, LlamaIndex, CrewAI and Mastra, plus the Python, Node.js and Go SDKs and any HTTP client. Send results to Google Sheets, a database or a data warehouse from the workflow tool you already use. See the integrations page for the full list.

### What format does the data come in?

JSON by default, with the structured record in parsed and the original page in html . You can also ask for the page as Markdown or plain text, which is what most LLM and RAG pipelines want.

All scrapers: https://www.zenrows.com/scrape. Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
