TL;DR
Redfin does not offer an unrestricted public REST API for property listings. Its official Data Center provides downloadable aggregate market CSVs. For listing data, use an authorized MLS or commercial provider. The Python workflow below validates a manually downloaded Redfin CSV, then uses ScrapeGraphAI only on a separate City of Austin open-data source. No Redfin URL is sent to ScrapeGraphAI.
What is the Redfin API?
The phrase "Redfin API" usually refers to something that Redfin does not sell: a self-serve listing API with public documentation, an API key, and endpoints for homes, agents, estimates, or search results. Redfin has no unrestricted public listing REST API. Its official developer-friendly data surface is the Redfin Data Center, where researchers can download aggregate housing-market CSV files.
That is different from property-level listing access. Redfin receives much of that content from multiple listing services, and the rights attached to it do not become public just because a listing appears in a browser. Third-party products advertised as a Redfin API, a Redfin scraper, or an unofficial wrapper are separate services. Their endpoints, data rights, uptime, and pricing are not Redfin's.
Two lanes keep the implementation honest. One validates an official download. The other adds context from a source that permits reuse. Neither lane guesses at hidden Redfin endpoints or automates Redfin pages.
Redfin Data Center versus listing APIs
The right source depends on the question. A market analyst comparing median sale prices by metro needs a different contract from an application displaying active homes.
| Need | Appropriate source | What you receive |
|---|---|---|
| Aggregate housing trends | Redfin Data Center | Downloadable weekly or monthly CSV files |
| Active listings and listing changes | An MLS, IDX feed, broker agreement, or licensed property-data provider | Property-level records under a data license |
| Redfin page content | Redfin website for human browsing | A webpage, not general API permission |
| Local demographic context | A government open-data portal or another licensed publisher | Public statistics with their own definitions and attribution |
| A product called "Redfin API" | A third-party vendor | Vendor-defined coverage, access, and terms |
JSON proves only that a service returned JSON. A repository named after Redfin can still call undocumented website services, and a marketplace listing can still be operated by an unrelated vendor. Check the operator and data license, not the package name.
For general property research, our real estate scraper guide covers schema design and source selection. For Zillow-specific questions, use the separate Zillow scraping guide. This page owns the Redfin data-access question and the aggregate-market workflow. For property-level access, the MLS API guide explains RESO, IDX, credentials, and licensed public-remarks analysis.
What the Data Center provides
Redfin rebuilt its Data Center in May 2026. The launch announcement describes direct downloads, earlier monthly releases, seasonally adjusted views by default, and more consistent definitions between weekly and monthly series.
The download hub currently separates several datasets rather than offering one universal table. The choices include:
- Housing Market Tracker, with weekly and monthly key metrics
- Housing Market Tracker by Property Type
- Balance of Power for buyers and sellers
- Luxury and starter-home price tiers
- Price drops, cancellations, and delist or relist activity
- Investor purchases, financing, migration, and existing-home sales datasets
The interface exposes metric, time, geography, and date controls before producing a CSV. Each downloaded row represents a geography and period. Redfin's download notes also explain common suffixes such as year-over-year, month-over-month, and week-over-week changes. Missing observations can appear as NA.

Screenshot captured July 30, 2026 from the Redfin Data Center Download Hub. View the live source.
Coverage and cadence need closer reading. The methodology page says national and metro data are available weekly and monthly. Broader monthly tables cover more geographies. Major geographies use calendar-month figures, while some smaller areas use rolling three-month periods. Recent observations can change as transactions arrive during the curing window, and some recent estimates are adjusted for expected revisions.

Screenshot captured July 30, 2026 from Redfin's methodology page. View the live source.
Those details are not footnotes. A monthly Austin metro value and a rolling three-month Austin ZIP-code value should not be placed on the same time axis without labeling their period definitions.
Choose the download from the question
Start with the decision you need to support, not the CSV with the most columns. The standard Housing Market Tracker is suitable for a regional pulse: prices, sales, listings, inventory, pending sales, and market speed. Choose the property-type version when the analysis must distinguish houses, condos, townhouses, or another category exposed by the download. Use a specialist dataset when the question is specifically about investors, financing, price drops, or buyer and seller balance.
Cadence is another modeling choice. Weekly data can reveal a turn sooner, but it is noisier and more likely to move as records arrive. Monthly data is easier to present in an executive report. It can still change, so neither should be treated as an immutable historical fact. Redfin publishes release timing and dataset-specific notes on the methodology page.
Save the choices beside the file: dataset name, selected metrics, geography type, date range, weekly or monthly cadence, download time, and file checksum. Those fields explain why two analysts can visit the same download hub and produce different numbers. A reproducible market result starts with the download configuration, not with the pandas code.
Access, documentation, keys, and pricing
There is no public Redfin API key flow for listings. Redfin does not publish an official listing endpoint catalog, authentication header, SDK, sandbox, quota, or rate-limit table. This guide therefore contains no Redfin request code.
The Data Center is different. A user chooses a dataset in the download hub and downloads a CSV. That workflow needs no API token, but it is still governed by the source's terms and the notes attached to the dataset. The data is aggregate market research, not a substitute for a licensed listing feed.
Redfin does not publish a fixed price for a listing API because it does not advertise that product. The official Data Center downloads are accessible from the site. Property-level commercial access, where available through an MLS or another provider, follows that provider's pricing and license. Ask vendors for provenance, redistribution rights, refresh cadence, deleted-record behavior, geographic coverage, and a sample contract before comparing prices.
If a vendor claims to provide a Redfin data API, verify five points before writing integration code:
- Who operates the API?
- Which source licenses the underlying records?
- Is the response an aggregate statistic or a property listing?
- May you store, display, or redistribute it?
- What happens when the upstream page or schema changes?
A cheap endpoint with unclear rights can be more expensive than a licensed feed once an application depends on it.
Why unofficial Redfin integrations are fragile
Unofficial wrappers tend to copy browser requests, parse internal JSON, or depend on page markup. They can appear convenient because the initial example is short. The maintenance cost arrives later.
An internal hostname can change without versioning. Session checks, bot controls, cookies, and request signatures can change independently. Fields built for Redfin's interface are not a public contract. A successful response does not prove permission to store or reuse the content. The breakage can also be silent: a field keeps its type but changes meaning, or the page returns a partial result that still validates.
Publishing community endpoint snippets here would turn a current implementation detail into false documentation. If property-level records are a product requirement, start with an MLS, IDX partner, or a provider that can grant the necessary rights. If aggregate trends are enough, start with the Data Center CSV.
Redfin's automation boundary
Redfin's Terms of Use, updated September 29, 2025, prohibit automated crawling, querying, or database scraping without prior express written permission. The terms also place restrictions on MLS content.
For this tutorial, that creates a simple rule: ScrapeGraphAI never receives a Redfin URL. The Redfin file is downloaded manually from the official interface, uploaded by the analyst, and parsed locally with pandas and Pydantic. The only URL extraction uses a City of Austin dataset whose open-data terms allow public reuse.
Put the restriction in the architecture, not in a disclaimer after the example:
- Redfin aggregate data enters as a user-supplied file.
- Redfin provenance stays attached to every derived metric.
- ScrapeGraphAI reads a separate, permissioned public source.
- The two sources remain separate records with separate definitions.
Read the web scraping legality guide for a broader checklist. It is not legal advice, and permission can depend on the source, jurisdiction, contract, data type, and intended use.
Design the schema before parsing
Housing datasets are easy to flatten badly. A value such as 590000 is meaningless until the record says whether it is a median sale price, a median home value, a list price, or a model estimate. The same problem applies to time and geography.
The Redfin schema below requires the dimensions that make a market statistic usable. It validates a selected subset of a manually uploaded Housing Market Tracker by Property Type file.
from datetime import date, datetime, timezone
from typing import Literal
from pydantic import BaseModel, Field, field_validator
class RedfinMarketRow(BaseModel):
geography: str = Field(min_length=1)
region_type: str = Field(min_length=1)
period_end: date
property_type: str = Field(min_length=1)
median_sale_price_usd: float = Field(ge=0)
inventory: int = Field(ge=0)
homes_sold: int = Field(ge=0)
median_days_on_market: float = Field(ge=0)
source: Literal["Redfin Data Center"] = "Redfin Data Center"
retrieved_at: datetime
@field_validator("geography", "region_type", "property_type")
@classmethod
def reject_placeholders(cls, value: str) -> str:
cleaned = value.strip()
if cleaned.lower() in {"", "n/a", "unknown", "none"}:
raise ValueError("required dimension is missing")
return cleaned
class PublishedHousingFact(BaseModel):
year: int = Field(ge=1900, le=2100)
metric: Literal[
"median home price",
"median household income",
"median gross rent",
]
value: float = Field(ge=0)
unit: Literal["USD"] = "USD"
class PublicHousingContext(BaseModel):
geography: Literal["Austin, Texas"]
facts: list[PublishedHousingFact] = Field(min_length=1)Neither class claims that Redfin's median sale price equals Austin's median home price. Keeping separate models makes that difference harder to erase in an accidental join.
Upload and validate a Redfin CSV
In the Redfin download hub, choose Housing Market Tracker by Property Type, select the period and geography you need, then download the CSV yourself. Upload that file to Colab when prompted. Do not place the file in a public repository unless its terms and your intended use allow it.
Header matching is intentionally strict. Inspect print(redfin_raw.columns.tolist()) and adjust the mapping to the file you downloaded. If two columns could match, stop instead of making a fuzzy guess.
from google.colab import files
import pandas as pd
uploaded = files.upload()
if len(uploaded) != 1:
raise ValueError("Upload exactly one Redfin Data Center CSV")
redfin_filename = next(iter(uploaded))
redfin_raw = pd.read_csv(redfin_filename, na_values=["NA"])
redfin_raw.columns = [
str(column).strip().lower().replace(" ", "_")
for column in redfin_raw.columns
]
def require_column(frame: pd.DataFrame, *candidates: str) -> str:
matches = [name for name in candidates if name in frame.columns]
if len(matches) != 1:
raise ValueError(
f"Expected exactly one of {candidates}; found {matches}"
)
return matches[0]
columns = {
"geography": require_column(redfin_raw, "region", "geography"),
"region_type": require_column(redfin_raw, "region_type"),
"period_end": require_column(redfin_raw, "period_end"),
"property_type": require_column(redfin_raw, "property_type"),
"median_sale_price_usd": require_column(
redfin_raw, "median_sale_price"
),
"inventory": require_column(redfin_raw, "inventory"),
"homes_sold": require_column(redfin_raw, "homes_sold"),
"median_days_on_market": require_column(
redfin_raw, "median_days_on_market", "median_dom"
),
}
uploaded_at = datetime.now(timezone.utc)
redfin_rows = []
for raw_row in redfin_raw.head(25).to_dict(orient="records"):
candidate = {
target: raw_row[source]
for target, source in columns.items()
}
candidate["retrieved_at"] = uploaded_at
redfin_rows.append(RedfinMarketRow.model_validate(candidate))
assert redfin_rowsThe head(25) cap is deliberate for the notebook demonstration. Production analysis can process the whole file after column and null-rate checks. If the validation fails, stop and inspect the offending row. Replacing missing inventory with zero would invent market conditions.
Add licensed local context with ScrapeGraphAI
The City of Austin Demographics Stats at a Glance dataset publishes annual demographic and housing facts. Its portal marks the dataset as last updated March 19, 2026, with underlying data last updated September 19, 2023. The city's open-data terms state that portal data is public domain unless a dataset says otherwise and request attribution.
ScrapeGraphAI's job here is narrow: turn explicitly published Austin fields into a validated object. It is not a Redfin scraper and does not visit Redfin.
import json
import os
import requests
from scrapegraph_py import ScrapeGraphAI
AUSTIN_PAGE = (
"https://data.austintexas.gov/City-Government/"
"Demographics-Stats-at-a-Glance/ghdg-7f7z"
)
AUSTIN_DATA = (
"https://data.austintexas.gov/resource/ghdg-7f7z.json"
"?$order=year%20DESC&$limit=5"
)
PROMPT = """
Extract only facts explicitly published in this City of Austin dataset.
Use the geography Austin, Texas. Return the year, metric, numeric value,
and USD unit for median home price, median household income, and median
gross rent when present. Do not calculate, estimate, or rename a metric.
"""
def validate_response(response) -> PublicHousingContext:
if response.status != "success":
raise RuntimeError(response.error or response.status)
payload = response.data.json_data
if isinstance(payload, str):
payload = json.loads(payload)
return PublicHousingContext.model_validate(payload)
def extract_austin_context(client: ScrapeGraphAI) -> PublicHousingContext:
try:
response = client.extract(
prompt=PROMPT,
url=AUSTIN_PAGE,
schema=PublicHousingContext.model_json_schema(),
)
return validate_response(response)
except Exception as first_error:
source_response = requests.get(AUSTIN_DATA, timeout=30)
source_response.raise_for_status()
licensed_text = json.dumps(source_response.json())
try:
response = client.extract(
prompt=PROMPT,
html=licensed_text,
schema=PublicHousingContext.model_json_schema(),
)
return validate_response(response)
except Exception as retry_error:
raise RuntimeError(
f"URL extraction failed: {first_error}; "
f"licensed-source retry failed: {retry_error}"
) from retry_error
with ScrapeGraphAI(api_key=os.environ["SGAI_API_KEY"]) as client:
austin_context = extract_austin_context(client)
austin_source_url = AUSTIN_PAGE
austin_retrieved_at = datetime.now(timezone.utc)There is one normal URL attempt. The fallback obtains the same licensed dataset through its official Socrata resource endpoint and passes the returned text to the extractor. There is no stealth mode, no browser impersonation, and no Redfin URL in either call.
In Colab, getpass places the ScrapeGraphAI key in the process environment without printing it. The current Python SDK documentation is the source for the extract, model_json_schema(), and model_validate() pattern.
Build a normalized market snapshot
Normalization should not imply comparability. Redfin rows describe sale-market metrics under Redfin's methodology. Austin facts retain the labels published by the city dataset. They can share an export while staying separate records.
snapshot_rows = []
for row in redfin_rows:
common = {
"record_type": "redfin_market_metric",
"geography": row.geography,
"period": row.period_end.isoformat(),
"property_type": row.property_type,
"source": row.source,
"retrieved_at": row.retrieved_at.isoformat(),
}
for metric, value, unit in [
("median sale price", row.median_sale_price_usd, "USD"),
("inventory", row.inventory, "homes"),
("homes sold", row.homes_sold, "homes"),
("median days on market", row.median_days_on_market, "days"),
]:
snapshot_rows.append(
{**common, "metric": metric, "value": value, "unit": unit}
)
for fact in austin_context.facts:
snapshot_rows.append(
{
"record_type": "austin_open_data_context",
"geography": austin_context.geography,
"period": str(fact.year),
"metric": fact.metric,
"value": fact.value,
"unit": fact.unit,
"property_type": None,
"source": "City of Austin Open Data",
"retrieved_at": austin_retrieved_at.isoformat(),
}
)
snapshot = pd.DataFrame(snapshot_rows)
snapshot.to_csv("redfin_market_snapshot.csv", index=False)
required_output = {
"record_type", "geography", "period", "metric", "value", "unit",
"property_type", "source", "retrieved_at",
}
assert required_output.issubset(snapshot.columns)
assert not snapshot.empty
assert snapshot["geography"].str.len().gt(0).all()
assert snapshot["source"].nunique() == 2
round_trip = pd.read_csv("redfin_market_snapshot.csv")
assert len(round_trip) == len(snapshot)The export name describes the research task, not a blended Redfin dataset. Keep record_type and source. Without them, the Austin context can be mistaken for a Redfin metric.
For broader competitive work, the same separation applies to a market research scraping pipeline. A price scraping workflow may observe displayed prices, but it should not collapse asking prices, sold prices, nightly rates, and estimates into one field.
What valid schemas can still get wrong
Pydantic catches missing fields, negative numbers, and unexpected labels. It cannot prove that a valid number means what you think it means.
Consider four common failures:
- Geography mismatch: Austin city, Austin metro, Travis County, and a Redfin market can cover different boundaries.
- Period mismatch: A calendar month, a rolling three-month period, and an annual observation cannot share a period label.
- Price mismatch: Median sale price, median home price, median list price, and automated valuation are different metrics.
- Revision mismatch: A recently downloaded Redfin value may be revised after more transactions enter the curing window.
Do not rename fields until they appear to match. Store the original metric label, source, geography, period, retrieval time, and methodology URL. If a chart compares unlike concepts, say so in the title and caption.
A schema-valid extraction still needs source checking. Open the Austin source, find the returned year and metric, and compare the value before publishing. Quarantine a fact that cannot be matched. A polished CSV is not evidence by itself.
Production checks
The notebook proves the data shape, not production readiness. A recurring pipeline needs stronger controls.
Pin the source contract. Record the chosen Redfin dataset, download options, cadence, geography level, file checksum, and methodology URL. Keep the raw licensed file outside source control unless publication is permitted.
Validate headers before rows. Fail on missing or ambiguous columns. Track null rates and numeric coercion failures. A new column name should trigger review, not a guessed mapping.
Keep provenance at record level. Store source URL or dataset name, retrieval time, covered period, and transformation version. Cache source material according to its rights and revision policy.
Separate observations from calculations. If you compute price growth or affordability ratios, save the formula and input record identifiers. Label normalized classifications as derived rather than source text.
Respect revisions. Re-download recent periods on a controlled schedule and compare checksums. Do not overwrite a published research result without retaining its earlier input version.
Minimize personal data. The workflow needs aggregate statistics, not names, contact details, or household records. Do not join open data in ways that reidentify people.
Use rate discipline. The ScrapeGraphAI call reads one licensed public source and has one fallback. For recurring jobs, add caching, bounded retries, backoff, monitoring, and a source-owner contact path. Our web scraping API guide covers the general production tradeoffs.
Keep the booking boundary clear. A housing-research dataset is not a real-estate transaction system. Listing status, availability, disclosures, and offers require authorized current sources and human review.
Run it in Google Colab
The linked notebook, Redfin Data Center and Permissioned Housing Research with ScrapeGraphAI, includes the pinned install, hidden key input, manual Redfin upload, source-specific schemas, one extraction attempt, one licensed-text fallback, assertions, and CSV download.
Use this sequence:
- Download the chosen CSV manually from the Redfin Data Center.
- Open the notebook and run the pinned installation cell.
- Enter the ScrapeGraphAI key through
getpass. - Upload exactly one Redfin CSV when prompted.
- Review the detected headers and validated rows.
- Run the Austin extraction and verify its facts against the live source.
- Download
redfin_market_snapshot.csv.
Do not share a notebook with saved credentials or private input files. Colab's viewer permission does not make a data license transferable.
Does Redfin have a public API?
No unrestricted public listing REST API is documented by Redfin. The official Redfin Data Center provides aggregate market downloads. Property-level applications should use an authorized MLS, IDX feed, broker agreement, or licensed vendor.
Is the Redfin Data Center an API?
No. It is an official download and research interface that produces datasets such as CSV files. Treat those files as versioned inputs rather than pretending the download controls are REST documentation.
Can I get a Redfin API key?
Redfin does not publish a self-serve listing API key flow. A key sold by a marketplace or third party belongs to that vendor, not automatically to Redfin.
Can ScrapeGraphAI scrape Redfin listings?
Not in this tutorial. Redfin's terms restrict automated crawling and scraping without prior express written permission. ScrapeGraphAI is used only on a separate City of Austin open-data source. The Redfin CSV is downloaded manually and parsed locally.
What is the safest way to automate Redfin market research?
Use official Data Center downloads for aggregate trends, record the selected options and methodology, and validate the file locally. Add other sources only when their reuse terms permit it, and preserve each source's definitions.
Are unofficial Redfin APIs reliable?
They may work temporarily, but undocumented endpoints are not stable contracts. Review the operator, provenance, permissions, schema guarantees, and upstream dependency before relying on one.