A dataset of Zillow reviews for realtors: star ratings, competency sub-scores, client comments, agent replies and brokerage data. Free to query.
Star ratings tell you almost nothing on their own. A 5.0 average built from four reviews is not the same signal as a 4.8 built from three hundred, and neither tells you whether the agent was good at negotiating or just good at answering the phone quickly.
The Zillow Reviews of Real Estate Agents dataset breaks that signal apart. Every row is an individual client review, carrying not just an overall star rating but four separate competency sub-scores, the free-text comment the client wrote, a summary of the transaction, and — where it exists — the agent's public reply.
That structure makes Zillow reviews for realtors usable for a much wider set of questions than a typical scraped ratings table supports.
The table spans 27 columns (26 data fields plus a UUID primary key). They group naturally into five clusters.
Column | Type | What it holds |
|---|---|---|
| text | Stable identifier for the agent profile |
| text | Agent's display name |
Because agent_avg_rating and agent_review_count sit on every row, you can weight any single review against the agent's full track record without a second join.
Column | Type | What it holds |
|---|---|---|
| text | Unique review identifier |
| real | Numeric star rating for this review |
This is the part that sets it apart from a generic ratings dump:
local_knowledge
process_expertise
responsiveness
negotiation_skills Each is stored as a bigint. Zillow asks reviewers to score these dimensions separately from the overall rating, which means you can look at why an agent scores well rather than just how well.
Column | Type | What it holds |
|---|---|---|
| text | The agent's public response, if any |
| text | Whether a reply exists |
Reply behavior is an under-used signal. Whether an agent responds to criticism — and how — is often more diagnostic than the rating that provoked it.
source, page_number, position, review_no, and submitted_at record where each row came from and where it sat in the original listing. Keep these if you care about reproducibility or want to check for position bias in how reviews surface.
The derived field vs_agent_avg gives you each review's deviation from its agent's mean — handy for isolating outlier experiences without recomputing group statistics.
Decompose the rating. Regress the overall rating against the four sub-scores and see which competency carries the most weight in a client's final judgment. My guess is responsiveness dominates, but that's an empirical question and this data can answer it.
Sentiment and topic modeling. The review_comment field gives you enough natural language for meaningful NLP work — extracting recurring complaints, mapping praise vocabulary to score bands, or fine-tuning a small classifier on realtor review sentiment.
Study the rating-comment gap. Look for reviews where the star rating and the language of the comment disagree. These mismatches are where review-platform bias tends to live.
Brokerage benchmarking. Group by brokerage and compare sub-score distributions. Do the big national brands actually outperform local independents, and on which axis?
Reply strategy analysis. Filter to negative reviews, split on reply_status, and examine what a good response looks like — useful if you're building reputation-management tooling for realtors.
Geographic variation. agent_location and transaction_summary together let you compare how expectations shift between markets. A "responsive" agent in a hot metro may mean something very different from one in a slower market.
The dataset lives on Ouro, which exposes it through a Python SDK. You'll need an API key from Settings → API Keys — sign up free if you don't have an account.
Pull it into a DataFrame:
import os from ouro import Ouro ouro = Ouro(api_key=os.environ.get("OURO_API_KEY")) dataset_id = "01a08713-c6a9-780f-91ff-2d74c17d1dc1" df = ouro.datasets.query(dataset_id) print(df.head())
Or push the aggregation server-side with read-only SQL, using {{table}} as the placeholder:
# Which competency tracks the overall rating most closely? corr = ouro.datasets.query( dataset_id, SELECT corr(rating, local_knowledge) AS r_local_knowledge, corr(rating, process_expertise) AS r_process_expertise, corr(rating, responsiveness) AS r_responsiveness, corr(rating, negotiation_skills) AS r_negotiation FROM {{table}} WHERE rating IS NOT NULL, )
# Reply rate on low-rated reviews, by brokerage replies = ouro.datasets.query( dataset_id, SELECT brokerage, avg(CASE WHEN agent_reply IS NOT NULL THEN 1.0 ELSE 0.0 END) AS reply_rate FROM {{table}} WHERE rating <= 3 GROUP BY brokerage ORDER BY reply_rate DESC, )
Inspect the column definitions before you write anything complex:
columns = ouro.datasets.schema(dataset_id) for col in columns: print(col["column_name"], col["data_type"])
Full code samples are on the dataset's docs tab
Review platforms are not random samples. Clients who leave feedback on the Zillow agent directory skew toward completed, successful transactions, and agents actively solicit reviews from happy clients. Expect a heavy left skew toward 5 stars and treat the low end as a small, self-selected minority rather than a representative failure rate.
Note the terminology too: Zillow profiles cover real estate agents broadly, and "Realtor" specifically means an agent who belongs to the National Association of Realtors. The dataset does not distinguish between the two, so don't infer NAR membership from a row.
reviewer and screen_name contain publicly displayed names. If you publish anything derived from this data, aggregate or anonymize — don't republish individual reviewers alongside their comments.
The snapshot has a fixed date. agent_avg_rating and agent_review_count were accurate at collection time and will drift. Check submitted_at before treating the totals as current.
Finally, sub-scores are ordinal, not interval. A 5 isn't necessarily "one unit better" than a 4 in any consistent sense across reviewers. Use rank-based methods where the distinction matters.
The most interesting thing here is the combination of structured sub-scores and unstructured text on the same rows. That pairing is rare in public review data, and it's what makes these Zillow reviews for realtors suitable for genuine modeling work rather than just descriptive charts.
Start with the dataset page
agent_location
text |
Market or service area |
| text | Brokerage the agent is affiliated with |
| real | The agent's overall average across all reviews |
| bigint | Total number of reviews on the profile |
starstext |
Star rating as displayed |
| text | The client's written feedback |
| date | When the review was posted |
| text | Reviewer's name as shown |
| text | Reviewer's Zillow handle |
| text | Deal context — bought, sold, price band, location |