# Realtor Com Agent Scraper With Market & Brokerage Stats (`scrapier/realtor-com-agent-scraper-with-market-brokerage-stats`) Actor

Realtor.com Agent Scraper extracts agent profiles with market and brokerage stats, including names, locations, specialties, sales data, brokerage details, ratings, contact info, and profile URLs. Ideal for agent research, lead generation, market analysis, and real estate intelligence.

- **URL**: https://apify.com/scrapier/realtor-com-agent-scraper-with-market-brokerage-stats.md
- **Developed by:** [Scrapier](https://apify.com/scrapier) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

### Realtor.com Agent Scraper — Zip-Level Market & Brokerage Stats

Realtor.com Agent Scraper With Market & Brokerage Stats pulls real estate agent records from Realtor.com by zip code and turns the same scrape into territory and brokerage analytics, with zero extra requests. Each run returns individual agent rows — name, contact fields, listing and sold counts, ratings — plus, in a separate dataset, one market-by-zip summary and one per-brokerage rollup computed from the agents already collected. Every response is structured JSON, ready to pass to an LLM, load into a spreadsheet, or feed a monitoring pipeline. No Realtor.com account or login is required to run it.

### What is Realtor.com Agent Scraper With Market & Brokerage Stats?

It is an Apify Actor that queries Realtor.com's agent-search system by zip code and returns two things from a single run: the individual agent records themselves, and territory-level rollups built on top of them. Where most Realtor.com agent scrapers stop at a flat agent list, this Actor also groups the same agents by zip code and by brokerage and computes agent counts, listing and sold totals, averages, and a ranked top performer for each group — written to its own `market-breakdown` dataset. No Realtor.com login, cookie, or session is required; every field returned is already visible to an anonymous visitor on Realtor.com's public agent-search and profile pages.

Key capabilities:

- 📍 Query by one or more US zip codes — each becomes its own territory rollup
- 🔢 Control sample depth per zip with `maxItemsPerZipcode` (1–10,000)
- 🔀 Choose the order agents are drawn from Realtor.com with `sortOrder` (relevance, ratings, most sales, most recent activity, or testimonials)
- 🏆 Rank agents within each zip by sold count and attach market-share percentages
- 🗺️ Build a market-by-zip summary (agent count, listings, sold, reviews, average rating, top agent)
- 🏢 Build a per-brokerage rollup across every zip queried in the run
- 🔁 Tune resilience with `maxRetries` (1–10) and start proxied or direct with `proxyConfiguration`

Both rollup toggles default to on, so a run with only `zipCodes` supplied still returns the full market-by-zip and brokerage breakdown out of the box — you opt out of a rollup rather than opting into it.

### What data can you get with Realtor.com Agent Scraper With Market & Brokerage Stats?

A single run extracts three result types: the raw agent records, a market-by-zip rollup, and a brokerage rollup. The two rollups share one dataset; agent records live in the default dataset.

| Result Type | Extracted Fields | Primary Use Case |
| --- | --- | --- |
| Individual agent record | `zip_code`, `agent_name`, `website`, `email`, `listing_count`, `sold_count`, `office_phone`, `mobile_phones`, `areas_serviced`, `zip_codes_serviced`, `office_company_name`, `company_website`, `review_count`, `agent_photo`, `type`, `brokerage`, `average_rating`, `top_agent_rank`, `top_agent_in_zip`, `agent_market_share` | Lead lists, agent profiling, in-zip ranking |
| Market-by-zip rollup | `type`, `zip_code`, `agent_count`, `total_listings`, `total_sold`, `avg_listings`, `avg_sold`, `total_reviews`, `avg_reviews`, `avg_rating`, `rated_agent_count`, `top_agent_name`, `top_agent_sold` | Territory sizing, comparing markets side by side |
| Brokerage rollup | `type`, `brokerage`, `agent_count`, `total_listings`, `total_sold`, `avg_listings`, `avg_sold`, `total_reviews`, `avg_reviews`, `avg_rating`, `zip_codes`, `zip_count` | Brokerage recruiting, competitive coverage analysis |

#### Market-by-zip and brokerage rollups

This is the capability the three ranked competitors do not offer: none of them aggregate the agents they scrape into territory or brokerage statistics — each returns a flat agent list and stops there. This Actor computes both rollups from the agents already pulled for your `zipCodes`, so there is no separate scraping pass. The market-by-zip rollup answers "how big and how active is this territory" — agent count, total and average listings/sold/reviews, average rating, and the top-selling agent. The brokerage rollup answers the same questions grouped by `brokerage` instead of by zip, spanning every zip code in the run. Both row types share one dataset and are distinguished by their `type` value (`market_zip` or `market_company`):

```json
{
  "type": "market_zip",
  "zip_code": "07101",
  "agent_count": 10,
  "total_listings": 84,
  "total_sold": 212,
  "avg_listings": 8.4,
  "avg_sold": 21.2,
  "avg_rating": 4.61,
  "top_agent_name": "Maria Chen, agent",
  "top_agent_sold": 47
}
```

#### Individual agent records

Each agent row carries the same 20 fields regardless of which zip it came from, plus the in-zip ranking fields when `rankTopAgents` is on. `office_phone`, `mobile_phones`, `areas_serviced`, `office_company_name`, `company_website`, `review_count`, and `agent_photo` come straight from Realtor.com's public agent-branding profile; `listing_count`, `sold_count`, and `average_rating` come from the same search and profile responses used to build the rollups above. Every row also carries `zip_code` so a merged export across multiple queried zips stays sortable and filterable without a join, and `type` distinguishes an agent row (`"agent"`) from the rollup rows that live in the separate `market-breakdown` dataset.

### How does Realtor.com Agent Scraper work?

For each zip code, the Actor first calls Realtor.com's `SearchAgents` GraphQL operation with the zip as `postal_code`, paginating in pages of 24 agents at a time until it has collected `maxItemsPerZipcode` agents or Realtor.com's `matching_rows` count for that zip is exhausted. The search request always applies `agent_type: ['BUYER']` and `agent_filter_criteria: 'NRDS_AND_FULFILLMENT_ID_EXISTS'`, and honors `sortOrder` as the GraphQL `sort` variable.

Each agent returned by the search then gets a second call — the `AgentBrandingProfile` operation — which is where `office_phone`, `mobile_phones`, `areas_serviced`, `office_company_name`, `company_website`, and `agent_photo` come from; the search response alone does not carry them. `listing_count`, `sold_count`, `type`, `brokerage`, and `average_rating` are filled in from a mix of the branding response and the enrichment fields already present in the original search payload, so no third request is needed per agent.

Both calls go through the same `frontdoor/graphql` endpoint using `curl_cffi` with Chrome TLS impersonation, a realistic header set including `rdc-client-name` and a page-matching `referer`, and the current proxy tier. A response is only accepted once its JSON payload contains the expected container (`search_agents` or `agent_branding.branding`); an HTTP 403/429/503, a non-JSON body, or a present-but-empty container is treated as a block and triggers a retry with proxy escalation rather than being parsed as zero results.

Once a full zip's agent sample is collected and every agent's branding profile has been fetched, the Actor computes `top_agent_rank`, `top_agent_in_zip`, and `agent_market_share` (when `rankTopAgents` is on) across that in-memory sample before pushing a single row per agent. Only after all requested zip codes have finished does it build the `market_zip` and `market_company` rollups from every agent row collected across the whole run, and write them to the separate `market-breakdown` dataset.

### Why not build this yourself?

Realtor.com does not publish a public API for agent search — there is no documented endpoint you can call with a zip code and get back agents. The data lives behind the same internal GraphQL endpoint (`frontdoor/graphql`) that powers realtor.com's own agent-search pages, and it is protected by an anti-bot layer that TLS/JA3-fingerprints the client making the request.

Getting consistent access means matching a real browser's TLS handshake (this Actor impersonates Chrome via `curl_cffi`), recognizing that the anti-bot system can return an HTTP 200 with a data-less response shell instead of an outright block, and escalating through proxy tiers — direct, then datacenter, then sticky residential — when a request is refused or comes back empty. That is infrastructure to build, monitor, and pay for before you have a single agent record, and it breaks again the next time Realtor.com's protection changes.

A DIY build also has to make two separate GraphQL calls line up correctly: one to search agents by zip code and paginate through results, and a second per-agent call to the agent-branding profile to pull phones, office details, and served areas — the search response alone does not carry them. Getting the rollups right on top of that means holding the full per-zip sample in memory until every agent in that zip has been fetched, since the ranking and market-share numbers can only be computed once the whole sample is in hand — not streamed row by row.

### What's the difference between an agent list and a market & brokerage rollup?

An agent list is one row per agent — a name, a phone number, a listing count. A market & brokerage rollup is a summary computed *across* those agents: how many agents work a zip code, what they sold combined and on average, and which brokerage dominates a territory. The two competitor Actors reviewed for this README (`cleansyntax/realtor-com-agents-scraper` and `scraped/realtor-com-agents-by-zip-code`) both return only the first — a per-agent CSV or dataset, with no aggregation step.

The distinction matters once you're comparing more than a handful of zip codes: a flat agent list tells you who is in a market, but answering "which of these ten zip codes has the most active agent pool" or "which brokerage sells the most across my territory" means exporting the list and aggregating it yourself, every time you re-run the scrape. That aggregation step is also easy to get subtly wrong by hand — averages need to exclude agents with no rating rather than treating a missing value as zero, and a brokerage rollup needs its agents grouped by a normalized brokerage name so "Coastal Metro Realty" and "Coastal Metro Realty " don't split into two rows.

Realtor.com Agent Scraper With Market & Brokerage Stats returns both objects from one run — the agent rows in the default dataset, and the `market_zip` / `market_company` rollups in the `market-breakdown` dataset — so the aggregation step is already done, using the same normalization and null-handling rules, by the time your run finishes.

### How to scrape Realtor.com agents with Realtor.com Agent Scraper With Market & Brokerage Stats

1. Open the Actor on Apify, sign in with your Apify account, and click **Try for free**
2. Enter one or more US zip codes into **Target Zip Codes** (`zipCodes`) — this is the only required input
3. Set **Agents Sampled Per Zip Code** (`maxItemsPerZipcode`) and **Agent Sort Order** (`sortOrder`) to control which and how many agents feed the rollups
4. Leave **Rank Top Agents Per Zip**, **Territory Market Stats**, and **Brokerage Breakdown** on (their defaults) to get the full rollup, or turn any off to skip that computation
5. Click **Start**, then download or stream results as JSON or CSV from the default dataset and the `market-breakdown` dataset

#### How to run multiple zip codes in one job

`zipCodes` is an array — add as many zip codes as you want compared side by side, one per list entry. Zip codes are processed one at a time in the order supplied; within each zip, agent-profile fetches run concurrently in batches of 5. Every zip in the array gets its own `market_zip` row (when `marketStats` is on) and contributes to the shared `market_company` rollups (when `aggregateByCompany` is on).

### ⬇️ Input

| Parameter | Required | Type | Description | Example Value |
| --- | --- | --- | --- | --- |
| `zipCodes` | Yes | array | One or more zip codes to size up as markets. Each zip becomes one territory rollup. Add several to compare markets side by side. | `["07101", "00501"]` |
| `maxItemsPerZipcode` | No | integer | How many agents to pull per zip code before computing the rollup. A larger sample makes the market and brokerage averages more representative. Minimum 1, maximum 10000. Default `10`. | `50` |
| `sortOrder` | No | string | Order agents are drawn from Realtor.com before the sample is taken. Enum: `RELEVANT_AGENTS`, `RATINGS_REVIEWS`, `MOST_SALES`, `MOST_RECENT_ACTIVITY`, `TESTIMONIALS_RECOMMENDATIONS`. Default `RELEVANT_AGENTS`. | `MOST_SALES` |
| `rankTopAgents` | No | boolean | Rank agents within each zip by sold count (ties broken by review count, then rating) and add `top_agent_rank`, `top_agent_in_zip`, and `agent_market_share` to every agent row. Default `true`. | `true` |
| `marketStats` | No | boolean | Build one market-by-zip summary per zip. Written to the separate `market-breakdown` dataset. Default `true`. | `true` |
| `aggregateByCompany` | No | boolean | Group the scraped agents by brokerage/office and roll up per-firm totals and averages across all zips. Written to the same `market-breakdown` dataset. Default `true`. | `true` |
| `maxRetries` | No | integer | How many times to retry a blocked or unstable request before rotating the proxy IP. Minimum 1, maximum 10. Default `3`. | `3` |
| `proxyConfiguration` | No | object | Starts direct (no proxy). On an anti-bot block or data-less response it falls back to datacenter, then sticky residential with retries. Enable Apify Proxy here to start proxied. | `{"useApifyProxy": false}` |

In the Apify Console input editor, `rankTopAgents`, `marketStats`, and `aggregateByCompany` are grouped under a **Market Breakdown Options** section, and `proxyConfiguration` sits under an **Advanced** section — both purely organizational; every parameter still accepts the same values through the API regardless of which UI section it appears in.

#### Example input

```json
{
  "zipCodes": ["07101", "00501"],
  "maxItemsPerZipcode": 25,
  "sortOrder": "MOST_SALES",
  "rankTopAgents": true,
  "marketStats": true,
  "aggregateByCompany": true,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

**Common pitfall:** `zipCodes` must be a JSON array of strings, even for a single zip code (`["07101"]`, not `"07101"`) when calling through the API — the Apify Console input editor handles the conversion for you, a raw API call does not. A zip code with no agents on Realtor.com does not raise an error; it simply returns zero agent rows for that zip, and no `market_zip` row is written for it (a rollup with `agent_count: 0` is never produced).

### ⬆️ Output

Results are split across two Apify datasets. The default dataset holds one row per agent (the `row_result` charged event) and has a defined Console table view — **Scraped Agents** — showing all 20 fields with icon labels. A separate dataset named `market-breakdown-<runId>` holds the market-by-zip and brokerage rollups, written uncharged — they cost nothing beyond the agent rows the run already charged for, since no extra Realtor.com requests are made to build them. That dataset has no custom Console view defined, so its rows display in the Console's automatic table using whatever keys are present on each row; `market_zip` and `market_company` rows carry different key sets, so filter or sort on `type` first when browsing it directly. Both datasets export as JSON, CSV, or Excel from the Apify Console, or can be read through the Apify API.

#### Scraped agent records

```json
[
  {
    "zip_code": "07101",
    "agent_name": "Maria Chen, agent",
    "website": "https://www.realtor.com/realestateagents/5f2a1c9b8e7d4a0012f3b4c5",
    "email": null,
    "listing_count": 12,
    "sold_count": 47,
    "office_phone": "(973) 555-0142",
    "mobile_phones": "(973) 555-0198",
    "areas_serviced": "Newark, Irvington, East Orange",
    "zip_codes_serviced": null,
    "office_company_name": "Garden State Realty Group",
    "company_website": "https://www.gardenstaterealty.example.com",
    "review_count": 34,
    "agent_photo": "https://ap.rdcpix.com/example/agent-headshot.jpg",
    "type": "agent",
    "brokerage": "Garden State Realty Group",
    "average_rating": 4.9,
    "top_agent_rank": 1,
    "top_agent_in_zip": true,
    "agent_market_share": 22.17
  },
  {
    "zip_code": "07101",
    "agent_name": "David Ortiz, agent",
    "website": "https://www.realtor.com/realestateagents/6a3b2d0c9f8e5b0123a4c5d6",
    "email": null,
    "listing_count": 6,
    "sold_count": 19,
    "office_phone": "(973) 555-0177",
    "mobile_phones": "(973) 555-0201, (973) 555-0233",
    "areas_serviced": "Newark, Belleville",
    "zip_codes_serviced": null,
    "office_company_name": "Coastal Metro Realty",
    "company_website": null,
    "review_count": 11,
    "agent_photo": "https://ap.rdcpix.com/example/agent-headshot-2.jpg",
    "type": "agent",
    "brokerage": "Coastal Metro Realty",
    "average_rating": 4.6,
    "top_agent_rank": 2,
    "top_agent_in_zip": false,
    "agent_market_share": 8.96
  },
  {
    "zip_code": "00501",
    "agent_name": "Jasmine Lee, non-realtor",
    "website": "https://www.realtor.com/realestateagents/7b4c3e1d0a9f6c1234b5d6e7",
    "email": null,
    "listing_count": 3,
    "sold_count": 9,
    "office_phone": null,
    "mobile_phones": "(631) 555-0119",
    "areas_serviced": null,
    "zip_codes_serviced": null,
    "office_company_name": "Holtsville Home Team",
    "company_website": "https://www.holtsvillehometeam.example.com",
    "review_count": 2,
    "agent_photo": "",
    "type": "agent",
    "brokerage": "Holtsville Home Team",
    "average_rating": null,
    "top_agent_rank": 1,
    "top_agent_in_zip": true,
    "agent_market_share": 100.0
  }
]
```

#### Market-breakdown dataset

```json
[
  {
    "type": "market_zip",
    "zip_code": "07101",
    "agent_count": 25,
    "total_listings": 138,
    "total_sold": 212,
    "avg_listings": 5.5,
    "avg_sold": 8.5,
    "total_reviews": 402,
    "avg_reviews": 16.1,
    "avg_rating": 4.61,
    "rated_agent_count": 21,
    "top_agent_name": "Maria Chen, agent",
    "top_agent_sold": 47
  },
  {
    "type": "market_company",
    "brokerage": "Garden State Realty Group",
    "agent_count": 4,
    "total_listings": 31,
    "total_sold": 88,
    "avg_listings": 7.8,
    "avg_sold": 22.0,
    "total_reviews": 96,
    "avg_reviews": 24.0,
    "avg_rating": 4.75,
    "zip_codes": "00501, 07101",
    "zip_count": 2
  }
]
```

### ⚠️ Known data limitations

- **`website` is a Realtor.com profile link, not the agent's personal site.** It is built as `https://www.realtor.com/realestateagents/<fulfillment_id>` from the agent's own profile ID. Realtor.com's agent-branding API does expose a separate `website` field on the agent, office, and broker objects, and `company_website` is populated from the office or broker version of that field — but the agent's own personal-site value from that field is not the one written into the `website` output key.
- **`email` is always `null`.** Realtor.com's agent-search and agent-branding endpoints used by this Actor do not return an email address for any agent.
- **`zip_codes_serviced` is always `null`.** `areas_serviced` (named service-area labels) is populated from the same profile response; a corresponding list of serviced zip codes is not available from it.
- **The agent pool is scoped to buyer-representation agents with both an NRDS ID and a fulfillment ID.** The underlying search always applies `agent_type: BUYER` and `agent_filter_criteria: NRDS_AND_FULFILLMENT_ID_EXISTS` — this is not exposed as an input, so agents outside that filter are not sampled.
- **Zip codes are processed sequentially, not in parallel** — one full zip's agent list and detail fetches complete before the next zip begins.
- **Agent pagination is fetched in fixed pages of 24**, regardless of `maxItemsPerZipcode` — a request for 25 agents still triggers two full pagination calls before the sample is trimmed to 25.
- **`average_rating` is `null`, not `0`, for agents with no reviews.** The Actor reports the absence of a rating rather than a fabricated zero, and `rated_agent_count` in the market-by-zip rollup only counts agents that actually have a numeric rating.

### How can I use the data extracted with Realtor.com Agent Scraper With Market & Brokerage Stats?

- 🏢 **Brokerage recruiters and team leads:** pull `aggregateByCompany` rollups across a set of zip codes to see which brokerage has the highest `total_sold` and `agent_count` in a territory before making a recruiting outreach list.
- 🗺️ **Territory and market planners:** compare `avg_sold`, `avg_listings`, and `avg_rating` from the market-by-zip rollup across multiple zip codes to decide where to open or staff an office.
- 📞 **Outreach and marketing teams:** use `agent_name`, `office_phone`, `mobile_phones`, and `company_website` from the agent rows to build a contact list scoped to a specific zip code or brokerage.
- 🤖 **AI engineers and LLM developers:** an agent issues a zip-code query, receives typed JSON agent rows and rollups back, and passes both straight into an LLM's context — no cleanup step before it is usable as grounding data.

Each of these workflows reads from the same run — there is no separate "analytics mode" to switch into. A recruiter and an AI agent can consume the exact same `market-breakdown` dataset from one run, just projecting different fields out of it.

### How do you monitor market and brokerage stats over time?

Territory conditions change: agents close deals, review counts climb, and which brokerage leads a zip code shifts month to month. The discipline is straightforward — run the same `zipCodes` on a recurring schedule and diff the `market-breakdown` rows between runs rather than re-reading the full agent list each time.

The fields worth diffing are `avg_sold` and `total_sold` (is the territory getting more active), `avg_rating` (is service quality trending), and `top_agent_name` / `top_agent_sold` (has zip-level leadership changed). On the brokerage side, diff `total_sold` and `agent_count` per `brokerage` to see which firm is gaining or losing agents and volume across your tracked zips.

A typical loop: schedule a weekly run across your target zip codes, keep each run's `market-breakdown-<runId>` dataset, and compare the current run's `market_zip` and `market_company` rows against the previous run's for the same `zip_code` or `brokerage` key. Alert when `top_agent_name` changes for a zip you're tracking, or when `total_sold` moves past a threshold you set. Apify's built-in Schedule feature runs the Actor on a recurring cadence without any code of your own; the diffing itself happens outside the Actor, against the datasets each scheduled run produces.

### Integrate Realtor.com Agent Scraper With Market & Brokerage Stats and automate your workflow

Realtor.com Agent Scraper With Market & Brokerage Stats works with any language or tool that can send an HTTP request, since it runs as a standard Apify Actor.

#### REST API with Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("<YOUR_USERNAME>/realtor-com-agent-scraper-with-market-brokerage-stats").call(run_input={
    "zipCodes": ["07101", "00501"],
    "maxItemsPerZipcode": 25,
    "sortOrder": "MOST_SALES",
})

for agent in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(agent["agent_name"], agent["zip_code"], agent["sold_count"])
```

#### No-code tools (n8n, Make, Zapier)

In n8n, use the Apify node — or an HTTP Request node pointed at the Apify run endpoint with your token — and pass the same JSON input shown above. In Make, use the Apify module and map the dataset output into a Google Sheets or Airtable step. Both handle scheduling, so a recurring zip-code sweep needs no code.

#### Scheduled monitoring and delivery

Set up a recurring run from the Apify Console's Schedule tab to re-query the same zip codes on a cadence you choose, or trigger runs from your own system through the Apify API. Results land in each run's own default and `market-breakdown` datasets, which you can pull with `apify_client` or export directly from the Console after each run finishes.

### Is it legal to scrape Realtor.com agent data?

Scraping publicly accessible web data is broadly permitted, and this Actor collects only what Realtor.com already shows to any anonymous visitor on its agent-search and agent-profile pages — no login, cookie, or paywall is bypassed.

Agent records include personal identifiers of a real, named individual — `agent_name`, phone numbers, and a photo — so personal-data regimes such as GDPR and the CCPA can apply depending on your jurisdiction and how you use the data, even though the information is published by the agent or their brokerage for professional, public-facing purposes. The market-by-zip and brokerage rollups, by contrast, are aggregate statistics with no per-agent identifiers, which puts them closer to business/market data than personal data. Scraping for internal market research or monitoring carries a different risk profile than scraping to build a resale contact database or to train a model. Consult your legal team before storing agent-level data in bulk or using it for outreach at scale.

This Actor does not collect `email` at all — it is always `null` — so email-specific anti-spam obligations that apply to competitor scrapers exposing agent emails do not arise from this Actor's output.

### ❓ Frequently asked questions

#### What zip code formats does Realtor.com Agent Scraper accept?

`zipCodes` takes standard US 5-digit zip codes as strings, one entry per zip (for example `"07101"`). There is no country, language, or region parameter — the Actor only queries Realtor.com's US agent-search system.

#### How do I control which agents get sampled first?

Use `sortOrder`. `RELEVANT_AGENTS` is the default mix; `MOST_SALES` biases the sample toward top producers before your `maxItemsPerZipcode` cutoff is applied; the other options (`RATINGS_REVIEWS`, `MOST_RECENT_ACTIVITY`, `TESTIMONIALS_RECOMMENDATIONS`) reorder the same underlying agent pool.

#### How does Realtor.com Agent Scraper handle Realtor.com's anti-bot measures?

Requests are sent through `curl_cffi` with Chrome TLS/JA3 impersonation rather than a default HTTP client signature. A response is treated as blocked when the endpoint returns HTTP 403, 429, or 503, or when it returns HTTP 200 with a non-JSON or data-less body (a soft-block shell). On a block, the Actor retries up to `maxRetries` times and escalates its proxy: direct connection first, then Apify datacenter proxy, then sticky residential proxy, with exponential backoff between attempts.

#### Does Realtor.com Agent Scraper build market and brokerage rollups?

Yes. When `marketStats` is on, each zip code gets one `market_zip` summary row. When `aggregateByCompany` is on, agents are grouped by `brokerage` into `market_company` rows spanning all queried zips. Both row types are written to the separate `market-breakdown` dataset, uncharged, and are skipped entirely if both options are turned off.

#### How many agents does Realtor.com Agent Scraper return per zip code?

Up to `maxItemsPerZipcode` (default 10, maximum 10,000), or fewer if Realtor.com has fewer matching agents for that zip. Agents are paginated in blocks of 24 per request internally until your target is reached or Realtor.com's result count for that zip is exhausted.

#### What happens if a zip code returns no agents?

The Actor pushes zero agent rows for that zip and moves on — it does not raise an error for a valid but agent-less zip code. No `market_zip` rollup row is produced for a zip with zero agents, since the rollup is built from the agents actually collected.

#### Does Realtor.com Agent Scraper work with Claude, ChatGPT, and AI agent frameworks?

Yes. It is callable as a standard HTTP endpoint through the Apify API, so any agent framework that can make a request — LangChain, CrewAI, a custom tool definition — can invoke it and receive typed JSON agent rows and rollups directly, without an MCP server.

#### How does Realtor.com Agent Scraper compare to other Realtor.com agent scrapers?

Checked on the Apify Store on 26 July 2026: `cleansyntax/realtor-com-agents-scraper` returns a flat, CSV-oriented agent list with a fixed column order and no market or brokerage aggregation. `scraped/realtor-com-agents-by-zip-code` returns a similar flat agent list and caps trial users at 5 records per its own listing. `memo23/realtor-search-cheerio` offers a large pre-collected agent database alongside live scraping, with social-profile fields, but likewise does not compute territory or brokerage rollups. This Actor's difference is the `market-breakdown` dataset — market-by-zip and per-brokerage aggregation computed from the same scrape, with no extra requests.

#### Can I use Realtor.com Agent Scraper without managing proxies or Realtor.com credentials?

Yes. No Realtor.com account, login, or cookie is needed — the Actor handles the anti-bot proxy escalation (direct → datacenter → sticky residential) automatically based on `maxRetries` and your `proxyConfiguration`. The only credential you need is your own Apify account to run the Actor.

#### What does `agent_market_share` actually measure?

It is each agent's `sold_count` divided by the total `sold_count` of every agent sampled in that same zip code, expressed as a percentage rounded to two decimal places. It is computed only when `rankTopAgents` is on and only against the agents actually collected for that zip — not against every agent Realtor.com has for the area, since only `maxItemsPerZipcode` of them were sampled. When the zip's total sold count is zero, `agent_market_share` is `null` for every agent in it rather than a division-by-zero value.

#### Can I fetch the agent dataset and the market-breakdown dataset separately through the API?

Yes. `client.dataset(run["defaultDatasetId"])` returns only the agent rows. The rollups live in the separate `market-breakdown-<runId>` dataset, which you can look up through the run's storage in the Apify Console or the Apify API — they are never mixed into the default dataset, so a plain agent export never picks up a `market_zip` or `market_company` row by accident. `market_zip` and `market_company` rows share that one rollup dataset and are distinguished by `type` rather than split into two separate datasets, since both are cheap summary rows computed from the same in-run agent sample.

### 💬 Your feedback

Found a bug or missing a field, or need a rollup metric this Actor doesn't compute yet? Open an issue on this Actor's Issues tab on Apify and it will be looked at — field or metric requests backed by a sample zip code are the fastest to act on.

# Actor input Schema

## `zipCodes` (type: `array`):

One or more zip codes to size up as markets (e.g. \['07101', '00501']). Each zip becomes one territory rollup. Add several to compare markets side by side.

## `maxItemsPerZipcode` (type: `integer`):

How many agents to pull per zip code before computing the rollup. A larger sample makes the market and brokerage averages more representative. Example: 50 → up to 50 agents per zip feed the stats. Default is 10.

## `sortOrder` (type: `string`):

Order agents are drawn from Realtor.com before the sample is taken. Use MOST\_SALES to bias the sample toward top producers, or RELEVANT\_AGENTS for the default mix.

## `rankTopAgents` (type: `boolean`):

Rank agents within each zip by sold count (ties broken by review count, then rating) and add top\_agent\_rank, top\_agent\_in\_zip, and agent\_market\_share to every agent row. Default is true.

## `marketStats` (type: `boolean`):

Build one market-by-zip summary per zip (agent count, total & average listings/sold/reviews, average rating, top agent). Written to a separate 'market-breakdown' dataset. Default is true.

## `aggregateByCompany` (type: `boolean`):

Group the scraped agents by brokerage/office and roll up per-firm totals and averages across all zips. Written to the same 'market-breakdown' dataset. Default is true.

## `maxRetries` (type: `integer`):

How many times to retry a blocked or unstable request before rotating the proxy IP. Default is 3.

## `proxyConfiguration` (type: `object`):

Starts direct (no proxy). On an anti-bot block or data-less response it falls back to datacenter, then sticky residential with retries. Enable Apify Proxy here to start proxied.

## Actor input object example

```json
{
  "zipCodes": [
    "07101",
    "00501"
  ],
  "maxItemsPerZipcode": 10,
  "sortOrder": "RELEVANT_AGENTS",
  "rankTopAgents": true,
  "marketStats": true,
  "aggregateByCompany": true,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped items in the Actor's 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 = {
    "zipCodes": [
        "07101",
        "00501"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapier/realtor-com-agent-scraper-with-market-brokerage-stats").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 = {
    "zipCodes": [
        "07101",
        "00501",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapier/realtor-com-agent-scraper-with-market-brokerage-stats").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 '{
  "zipCodes": [
    "07101",
    "00501"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scrapier/realtor-com-agent-scraper-with-market-brokerage-stats --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapier/realtor-com-agent-scraper-with-market-brokerage-stats"
        }
    }
}

```

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/gmgczZFmMggZUqFeb/builds/EmD51gvmQcG9CQ9om/openapi.json
