# US Real Estate Agents Scraper: Zillow, Realtor, Homes.com (`nice_dev/us-real-estate-agents`) Actor

Find US real estate agents on Zillow, Realtor.com and Homes.com by city, ZIP code, state or name: phones, email, license, brokerage, sales, reviews. Live search or instant database. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/us-real-estate-agents.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.36 / 1,000 agents

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 🏠 What is US Real Estate Agents Scraper?

**US Real Estate Agents Scraper** extracts **real estate agents from [Zillow](https://www.zillow.com), [Realtor.com](https://www.realtor.com) and [Homes.com](https://www.homes.com)**: **name, cell phone, email, license number, brokerage, sales in the last 12 months, price range, rating and reviews**, for any US city, ZIP code or state, or by agent name. It works as a **Zillow and Realtor.com agent API alternative**: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Type a **location** (`Austin, TX`), click **Start**, and download the agents in JSON, CSV or Excel. No login, nothing to set up. Two ways to get them: **live**, the three sites read now for the freshest data, or **database**, an instant answer from our pre-collected database of US agents (it currently holds Realtor.com agents), with a count of the matches before you pay for a single agent.

### 📋 What data can you extract from Zillow, Realtor.com and Homes.com?

One item per agent, 81 fields:

| Category | What you get |
| --- | --- |
| 👤 **Agent** | full name, first and last name, photo, individual or team, team leader, team name and size, pronouns — `Jane Doe` |
| 📞 **Contact** | best phone, cell phone, every number with its type, email and every email found, website, Facebook, Instagram, LinkedIn, X, YouTube and TikTok links — `(512) 555-0142` |
| 🪪 **License** | license number, state, status and expiry date, MLS memberships, NAR member ID — `TX 0612345` |
| 🏢 **Brokerage** | office name, address, phone, email and website — `Example Realty Group` |
| 📍 **Location** | office city, state and ZIP code, latitude and longitude, the cities and ZIP codes the agent serves |
| 🏅 **Profile** | years of experience, active since, bio, languages, specialties, designations (ABR, CRS…), works with buyers and / or sellers, video, questions and answers, Zillow Top Agent, Premier Agent and REALTOR® badges |
| ⭐ **Reputation** | average rating, number of reviews, recommendations, sub-ratings (local knowledge, process expertise, responsiveness, negotiation), the summary of all reviews Zillow shows |
| 📈 **Sales** | sales in the last 12 months (agent and team), career sales, sales in the searched city, active listings, deal price range, average and median sale price, last listing and last sale dates — `34` sales, `250000` to `1500000` USD |
| 🔗 **Same person, every site** | a match key to group the rows of one person across Zillow, Realtor.com and Homes.com (see **Group the same agent across sites** below), every site the agent is on with the profile link and site IDs |
| 🧾 **Options** | client reviews, past sales and active listings, read from the agent's profile, each with its own cap, charged only when delivered |
| 🕒 **Freshness** | when the data was read, when the agent entered our database |

Every field, with an example, is listed in the **Output** section below.

With **Full profile** on (default), the Actor opens each agent's profile: cell and office phones, email, licenses with state and expiry, MLS IDs, website and social links, bio, languages, specialties, service areas and years of experience. Turn it off for the directory-card fields only (name, brokerage, rating, sales counts, price range), charged at the card price. Homes.com always gives the agent card only. Database rows always carry the full profile.

### ✅ Why use US Real Estate Agents Scraper?

- 🏠 **Three sites in one run**: Zillow, Realtor.com and Homes.com, with a `matchKey` to group the rows of one person.
- ⚡ **Live or instant**: read the sites now for the freshest data, or query the pre-collected database (Realtor.com agents for now) with the same filters, no per-search cap from the sites, and a **count of the matches** (by state and by site) before you pay for any agent.
- 📞 **The contact details sales teams need**: cell phone, email, license number and state, brokerage. As an option, the email the agent publishes on their own website when the sites show none.
- 🎛️ **Filters made for prospecting**: sales in the last 12 months, rating, reviews, years of experience, active listings, last activity, brokerage, languages, specialties, Top Agents, paying advertisers, NAR members, teams, "has a cell phone", "has an email".
- 🗺️ **Type a place, not a URL**: a city, a ZIP code, a county or a whole state, plus a radius in miles. Large areas are covered automatically.
- 🔔 **Monitoring built in**: tick **Only new agents**, schedule the Actor, and each run returns (and charges) only the agents it has never delivered before.
- 💳 **Options à la carte**: reviews, past sales, active listings and website emails are off by default, and charged only for the agents they are delivered for.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape US real estate agents

1. Create a free Apify account.
2. Open **US Real Estate Agents Scraper** and type a **Location** (e.g. `Austin, TX`, `78704` or `TX`), or an **Agent name**.
3. Keep **Data source** on live to read the sites now, or pick database for an instant answer (tick **Count only** first to see how many agents match).
4. Set the filters you need and **Max agents** (100 by default, 0 = no limit), then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape real estate agents?

This Actor uses **pay per event** pricing: you pay for each agent delivered, at the prices shown in the **Pricing** tab of the Actor, plus a fee per run start (`apify-actor-start`). Higher Apify plans pay less per agent. The events:

- `agent`: an agent saved from a directory card (Homes.com, or **Full profile** off);
- `agent-with-details`: an agent whose profile was read, in place of `agent` (never both);
- `database-agent`: an agent returned by database mode;
- `reviews`: an agent delivered with at least one client review (option **Add client reviews**);
- `past-sales`: an agent delivered with at least one past sale (option **Add past sales**);
- `agent-listings`: an agent delivered with at least one active listing (option **Add active listings**);
- `email-from-website`: an agent whose email was found on their own website (option **Find missing emails on agent websites**);
- `filter-check`: an agent read in live mode and dropped by one of the Actor's own filters (`detail-filter-check` when that filter needed the profile).

The options are off by default: an agent with no review, no past sale, no listing or no email found on their website costs nothing extra. Filters a site applies in its own search (agent type, languages and deal price, among others) cost nothing extra on that site. Database mode filters before it delivers, so you pay only for the agents returned, and **Count only** saves and charges no agent. Platform usage is included in the price.

### ⚙️ Input

```json
{
    "location": "Austin, TX",
    "maxItems": 200
}
```

Several places and a radius, only agents with a cell phone and 10 sales or more in the last 12 months, their reviews added, only the agents not delivered before:

```json
{
    "locations": ["78704", "Round Rock, TX"],
    "radiusMiles": 5,
    "maxItemsPerQuery": 100,
    "requireCellPhone": true,
    "minSalesLast12Months": 10,
    "includeReviews": true,
    "onlyNew": true,
    "stateKey": "austin-agents"
}
```

Database mode: how many Texas agents have a cell phone and 12 sales or more in the last 12 months (untick `countOnly` to get them, best sellers first):

```json
{
    "mode": "database",
    "location": "TX",
    "requireCellPhone": true,
    "minSalesLast12Months": 12,
    "sortBy": "salesLast12Months",
    "countOnly": true
}
```

Known agents by ID, with their past sales:

```json
{
    "agentIds": ["1918615", "ericbramlett"],
    "includePastSales": true
}
```

Or with your own URLs:

```json
{
    "startUrls": [
        { "url": "https://www.zillow.com/professionals/real-estate-agent-reviews/austin-tx/?isTopAgent=true" },
        { "url": "https://www.realtor.com/realestateagents/austin_tx" }
    ],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `mode` | `live` (default) reads the sites now. `database` answers at once from our pre-collected database, which currently holds Realtor.com agents: no per-search cap. |
| `sites` | `zillow`, `realtor`, `homes` (all three by default). In database mode: the sites an agent must have been found on; the database currently holds Realtor.com agents only, so a choice without `realtor` returns no agent. Homes.com gives the agent card only. |
| `location`, `locations` | City and state (`Austin, TX`), ZIP code (`78704`), county (`Travis County, TX`) or state (`TX` or `Texas`), each searched on its own (max 500 searches per run). In live mode a county or a state is searched Zillow: city by city; Realtor.com and Homes.com: ZIP code by ZIP code. A whole state is a long live run: set **Max agents**. In database mode the same values filter the database. |
| `radiusMiles` | Also search every ZIP code within this many miles of each city or ZIP code (Zillow: city by city; Realtor.com and Homes.com: ZIP code by ZIP code; database mode: keep the agents whose office is within it), up to `100`. `0` = the place itself only. |
| `agentName` | Agent or team name, full or partial (`smith`). Realtor.com searches it nationwide when no location is set; Zillow needs a location; Homes.com returns at most 10 agents from the whole country (the location is not applied there, the filters are). |
| `agentIds` | Look up known agents: Realtor.com fulfillment IDs (`1918615`) or Zillow screen names (`ericbramlett`), up to 1,000. A Zillow encoded ID (X1-ZU…) cannot be opened: give the profile URL or the screen name. A screen name of 7 lowercase letters or digits is taken for a Homes.com key and refused: paste its profile URL in `startUrls` instead. The other search fields are ignored for them; the filters are applied by the Actor to each agent. |
| `startUrls` | Zillow, Realtor.com or Homes.com agent directory pages (`https://www.zillow.com/professionals/real-estate-agent-reviews/austin-tx/?isTopAgent=true`, `https://www.realtor.com/realestateagents/austin_tx`, `https://www.homes.com/real-estate-agents/austin-tx/`) or Zillow and Realtor.com profile pages, up to 1,000 URLs. Filters set on the site are kept, and the Actor's filters are applied to each agent read; the location and name fields are then ignored. Live mode only. |
| `maxItems` | Agents to save for the whole run, after deduplication and filters (`0` = no limit). |
| `maxItemsPerQuery` | Cap for EACH search (location, name or start URL), so that the first one cannot use up the whole **Max agents** budget (`0` = no per-search cap). Live mode only: database mode ranks every location together, **Max agents** caps it. |
| `extractDetails` | **Full profile**, on by default: opens each agent's profile (cell phone, email, licenses, MLS IDs, website, social links, bio, service areas). Off = directory card only; the filters that need the profile are then ignored. |
| `includeReviews`, `maxReviewsPerAgent` | Option, needs **Full profile**: the agent's latest client reviews (text, rating, date, sub-ratings, agent reply), 20 per agent by default, up to 500 (Realtor.com; a Zillow profile shows about 10). |
| `includePastSales`, `maxSoldPerAgent` | Option, needs **Full profile**: the agent's closed deals (address, price, date, buyer or seller side, coordinates), 50 per agent by default, up to 1,000 (Realtor.com; a Zillow profile shows about 5). |
| `includeListings`, `maxListingsPerAgent` | Option, needs **Full profile**: the agent's current for-sale and for-rent listings (address, price, beds, baths, status, link), 50 per agent by default, up to 500 (Realtor.com; a Zillow profile shows a few). |
| `enrichEmails` | Option, needs **Full profile**, live mode only: for agents who have a website but no email on the sites, read that website (the home page, then the contact and legal pages it links to, 3 pages at most) and add the email published there. Slower. With `requireEmail`, such an agent is judged after the visit of its website. |
| `agentType` | `any` (default), `buyers` or `sellers`. |
| `topAgentsOnly` | Zillow Top Agents only (badge given by Zillow on sales and reviews): the agents of the other sites are left out. |
| `specialties` | Agents who list at least one of them: `first-time-home-buyers`, `luxury-homes`, `investment-properties`, `relocation`, `new-construction`… |
| `languages` | Agents who speak at least one of them: `spanish`, `mandarin`, `vietnamese`, `french`… |
| `minPrice`, `maxPrice` | Deal price range in USD, e.g. `250000` to `1000000`: agents whose price range reaches the first and starts below the second (a range that overlaps yours is kept). |
| `postedAfter`, `postedBefore` | Last activity (latest listing or closed sale, `lastActivityAt`; on Zillow only the last closed sale is dated): a date (`2026-01-01`) or a period before now (`90 days`, `6 months`, `1 year`). Agents with no dated activity are dropped. |
| `requireEmail`, `requirePhone`, `requireCellPhone` | Only agents with an email, with any phone, with a phone marked as mobile. |
| `brokerage` | Brokerage name contains this text (`keller williams`, case and accents ignored). |
| `minRating`, `minReviews` | Rating at least this (from `0` to `5`, unrated agents dropped), at least this many reviews. |
| `minSalesLast12Months`, `maxSalesLast12Months` | Closed sales in the last 12 months, at least / at most (newer or part-time agents). |
| `minYearsExperience`, `maxYearsExperience` | Years licensed or active, at least / at most (recruiting new agents). |
| `minTotalSales`, `minActiveListings` | Career sales at least this; listings for sale right now at least this (`1` = agents with listings). |
| `requireFields` | Only agents where every field picked is filled: `website`, `licenseNumber`, `photoUrl`, `bio`, `videoUrl`, `socialMedia.linkedin`, `brokerageEmail`… |
| `excludeTeams`, `teamLeadersOnly` | Individual agents only (no team profiles); only the agents who lead a team (Zillow team profiles). |
| `premiumOnly`, `realtorMembersOnly` | Only agents who pay the site for leads (Zillow Premier Agent, Realtor.com paid agent); only members of the National Association of Realtors (Realtor.com flag). |
| `excludeKeywords` | Drop agents whose name or brokerage contains one of these words (`referral`, case and accents ignored), up to 50 words. |
| `countOnly` | Database mode: how many agents match the location and filters, by state and by site, without saving or charging any agent. The count is in the status message and in the **COUNT** record of the run's key-value store. |
| `sortBy` | Database mode order: `relevance` (default), `salesLast12Months`, `totalSales`, `rating`, `reviewCount`, `lastActivity`, `updatedAt`. Live mode keeps the sites' order. |
| `updatedWithinDays` | Database mode: only the rows refreshed from the sites within this many days (`0` = any age). |
| `databaseOffset` | Database mode: skip this many matches (in a stable order) to fetch a large list over several runs, e.g. `1000`. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the agents never delivered under this memory key (`austin-agents`: letters, digits, `-` and `_`); `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (keep the default: it is included in the price), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

An agent read live from Zillow with the full profile (shortened, contact details made up; every column is listed below):

```json
{
    "id": "zillow:X1-ZU10abcd1234efg_5h6ij",
    "url": "https://www.zillow.com/profile/JaneDoeAustin",
    "source": "zillow",
    "sources": [
        {
            "site": "zillow",
            "url": "https://www.zillow.com/profile/JaneDoeAustin",
            "id": "X1-ZU10abcd1234efg_5h6ij",
            "date": "2026-10-21T12:00:00.000Z"
        }
    ],
    "matchKey": "TX:612345",
    "zillowZuid": "X1-ZU10abcd1234efg_5h6ij",
    "zillowScreenName": "JaneDoeAustin",
    "realtorFulfillmentId": null,
    "name": "Jane Doe",
    "firstName": "Jane",
    "lastName": "Doe",
    "isTeam": false,
    "isTopAgent": true,
    "isPremium": true,
    "brokerage": "Example Realty Group",
    "brokeragePhone": "(512) 555-0100",
    "phone": "(512) 555-0142",
    "cellPhone": "(512) 555-0142",
    "phones": [
        { "number": "(512) 555-0142", "type": "cell", "site": "zillow" },
        { "number": "(512) 555-0100", "type": "business", "site": "zillow" }
    ],
    "email": "jane.doe@example.com",
    "emails": [{ "email": "jane.doe@example.com", "site": "zillow" }],
    "website": "https://www.example.com",
    "licenses": [{ "number": "0612345", "state": "TX", "status": "Active", "expiresAt": "2027-08-31" }],
    "licenseNumber": "0612345",
    "licenseState": "TX",
    "city": "Austin",
    "state": "TX",
    "postalCode": "78704",
    "languages": ["English", "Spanish"],
    "specialties": ["First-time home buyers", "Relocation", "Luxury homes"],
    "worksWithBuyers": true,
    "worksWithSellers": true,
    "experienceYears": 12,
    "rating": 4.9,
    "reviewCount": 187,
    "salesLast12Months": 34,
    "totalSales": 412,
    "salesInSearchArea": 96,
    "forSaleCount": 6,
    "priceRangeMin": 250000,
    "priceRangeMax": 1500000,
    "lastListingDate": "2026-10-14",
    "lastSaleDate": "2026-10-09",
    "lastActivityAt": "2026-10-14",
    "reviews": null,
    "detailLevel": "profile",
    "searchLocation": "Austin, TX",
    "searchUrl": "https://www.zillow.com/professionals/real-estate-agent-reviews/austin-tx/",
    "updatedAt": "2026-10-21T12:00:00.000Z",
    "firstSeenAt": null,
    "scrapedAt": "2026-10-21T12:00:00.000Z"
}
```

A database row has the same columns (the database currently holds Realtor.com agents): `sources` lists the agent's profiles, `updatedAt` tells when the row was last refreshed from the site and `firstSeenAt` when the agent entered the database.

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 81 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `source`, `sources` | `zillow:X1-ZU…`, `realtor:1918615`, or `agent:…` for a merged database row; the profile URL (Zillow first); `zillow`, `realtor`, `homes` or `multi`; every site with its profile URL and ID |
| `matchKey` | **Same person, every site**: license state and number, leading zeros removed (`TX:612345`), else the cell phone, else the email; a Homes.com row carries its card phone (see **Tips**) |
| `zillowZuid`, `zillowScreenName`, `realtorFulfillmentId`, `realtorAgentId`, `homesAgentKey`, `nrdsId` | the agent's ID on each site, and the NAR member ID (Realtor.com) |
| `name`, `firstName`, `lastName`, `photoUrl` | **Agent**: `Jane Doe`, `Jane`, `Doe`, photo URL |
| `isTeam`, `teamLeader`, `teamName`, `teamMemberCount`, `pronouns` | team profile, team leader, team name, agents in the team, pronouns as given by the agent (Zillow) |
| `isTopAgent`, `isPremium`, `isRealtor` | Zillow Top Agent; Zillow Premier Agent or Realtor.com paid agent; NAR member (REALTOR®) |
| `brokerage`, `brokerageAddress`, `brokeragePhone`, `brokerageEmail`, `brokerageWebsite` | **Brokerage**: office name, address on one line, phone, email (Realtor.com listings), website |
| `phone`, `cellPhone`, `phones` | **Contact**: best number (cell first), cell phone, every number with its type (for instance cell, business, office, fax, toll-free, home) and site |
| `email`, `emails` | agent email; every distinct email with its site (`zillow`, `realtor`, `homes`, or `website` with **Find missing emails on agent websites**) |
| `website`, `socialMedia` | website; Facebook, Instagram, LinkedIn, X, YouTube, TikTok and blog links |
| `licenses`, `licenseNumber`, `licenseState`, `mlsIds` | **License**: every license (number, state, status, expiry), main license and its state, MLS memberships |
| `city`, `state`, `postalCode`, `latitude`, `longitude`, `serviceAreas` | **Location**: office city, 2-letter state, ZIP code, map position, cities / ZIP codes / neighborhoods served |
| `languages`, `specialties`, `designations`, `worksWithBuyers`, `worksWithSellers` | **Profile**: languages spoken, specialties, designations (ABR, CRS…), works with buyers, with sellers |
| `videoUrl`, `experienceYears`, `startYear`, `bio`, `questionsAndAnswers` | profile video, years of experience, active since (year), about text, questions the agent answers (Realtor.com) |
| `rating`, `reviewCount`, `recommendationsCount`, `subRatings`, `aiReviewSummary` | **Reputation**: average rating 0-5 (Zillow first), reviews, Realtor.com recommendations, sub-ratings, the summary of all reviews Zillow shows |
| `salesLast12Months`, `teamSalesLast12Months`, `totalSales`, `salesInSearchArea`, `forSaleCount` | **Sales**: sales in the last 12 months, the team's, career sales, sales in the city of the search (Zillow card), active listings |
| `priceRangeMin`, `priceRangeMax`, `averageSalePrice`, `medianSalePrice` | deal price range, average sale price, median sale price (Homes.com), in USD |
| `lastListingDate`, `lastSaleDate`, `lastActivityAt` | `2026-10-14`, `2026-10-09`, the latest of both (what **Active since** and **Last active before** read). `lastActivityAt` needs **Full profile** (`extractDetails`): a results card shows the last listing only, so without the profile it is empty rather than a date the profile would then change |
| `reviews`, `pastSales`, `activeListings` | **Options**: client reviews, closed deals, current listings; `null` when the option is off |
| `detailLevel`, `searchLocation`, `searchUrl` | `card` or `profile`; the location, name or URL that found the agent (`null` for a lookup by ID); the search URL |
| `updatedAt`, `firstSeenAt`, `scrapedAt` | **Freshness**: when the data was read from the site, when the agent entered our database (`null` in live mode), when the row was written (ISO 8601) |

### 💡 Tips

#### How to get more results

Set `maxItems` to `0` and search a whole state or several cities: large areas are covered automatically, and an agent found by several searches is saved once. A live search of a whole state is long: set **Max agents**, or use database mode, which has no per-search cap. In database mode, `databaseOffset` fetches a long list over several runs.

#### How to reduce costs

The price is per agent, so the levers are `maxItems`, `maxItemsPerQuery`, the filters and `onlyNew` for recurring runs (you never pay twice for the same agent). Turn **Full profile** off when the directory card is enough (name, brokerage, rating, sales counts, price range): each agent is then charged `agent` instead of `agent-with-details`, and the filters that need the profile are ignored. Leave the options off unless you need their data. In database mode, run **Count only** first to size the list.

#### Several searches in one run

Fill `locations`: the Actor runs one search per location, on each site picked in `sites` (up to 500 per run). The single `location` field still works and is added to the list. `radiusMiles` adds every ZIP code around each city or ZIP code (ZIP code data: [GeoNames](https://www.geonames.org), CC BY 4.0). An agent found by several searches is saved, and charged, once. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget. You can also paste several directory URLs into `startUrls`: each one is a search of its own, with the same cap.

#### Monitoring: only the new agents

Tick **Only new agents** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the agents already delivered: they are not saved, not charged, and their profile is not even opened. The memory lives in a named key-value store of your account (`us-real-estate-agents-seen`, up to 150,000 agents per key) and is only updated with agents that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's agents), and tick `resetState` once to start over. The sites list agents by relevance, not by date: each search is read through, and the agents you already have are skipped.

#### Filter by last activity

`postedAfter` (**Active since**) and `postedBefore` (**Last active before**) read `lastActivityAt`, the latest of the agent's last listing and last sale (on Zillow only the last closed sale is dated, the listings are not): a date (`2026-01-01`, the whole day is included, New York time) or a period before now (`90 days`, `6 months`, `1 year`). `postedAfter` keeps the active agents, `postedBefore` finds the dormant ones. An agent with no dated activity is dropped as soon as a bound is set. In live mode the date comes from the profile: the filter needs **Full profile**, and Homes.com agents are left out.

#### Database mode

With `mode` set to `database`, the Actor answers from our pre-collected database instead of reading the sites. The database currently holds Realtor.com agents: `sites` keeps the agents found on the sites you pick, so a choice without `realtor` returns no agent. The location, name, ID and filter fields work as in live mode, without the site's per-search caps; `radiusMiles` the agents whose office is within that distance, `updatedWithinDays` the rows refreshed recently, and `sortBy` sets the order. **Count only** (`countOnly`) tells how many agents match, by state and by site, without saving or charging any agent (the run start is still charged): check the size of a list before you buy it.

#### Group the same agent across sites

In live mode each site gives its own row (`source` = `zillow`, `realtor` or `homes`), so one person can come two or three times. `matchKey` helps you group them: license state and number (leading zeros removed), else the cell phone, else the email. A Zillow row and a Realtor.com row of one person share it when both show the same license. A Homes.com card shows no license: its key is the card's phone (its type is not given, toll-free lines are left out), so it meets the other rows only when they have no license and the same cell phone. Group on `matchKey` in your sheet or CRM, and on name and phone for the Homes.com rows.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per real estate agent found on Zillow, Realtor.com and Homes.com. Actor id: `nice_dev/us-real-estate-agents`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/us-real-estate-agents`.

Smallest input, for a cheap first call:

```json
{
    "location": "Austin, TX",
    "maxItems": 10
}
```

Key output fields: `name`, `brokerage`, `phone`, `cellPhone`, `email`, `licenseNumber`, `salesLast12Months` and `matchKey` (to group the rows of one person across sites).

Cost: per agent delivered (`agent`, `agent-with-details` or `database-agent`) plus the run start, at the prices of the **Pricing** tab, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Zillow, Realtor.com and Homes.com?

The Actor only reads what Zillow, Realtor.com and Homes.com show publicly to any anonymous visitor, and the database is built from the same public pages. It logs in to nothing and solves no captcha. Results contain the business contact details of **licensed real estate professionals**, published so that clients can reach them; they are still personal data, protected by the GDPR and by US state privacy laws such as the CCPA: do not store them without a legitimate reason. Use emails and cell phones for legitimate B2B outreach only, in line with CAN-SPAM for emails and with the TCPA and Do Not Call rules for calls and text messages. You are responsible for using the data in compliance with each site's Terms of Use and applicable law; this is not legal advice. This Actor is not affiliated with Zillow, Realtor.com, Homes.com or the National Association of REALTORS®.

#### Does it need a login or a proxy?

No login and no account on the sites. The proxy is included in the price: leave the default setting. A request a site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 60 seconds, doubled at each retry up to 240 seconds).

**Apify free plan:** the included residential proxy, needed by Homes.com, is for paying Apify plans only. On the free plan, add your own proxy URLs in **Proxy configuration** to read Homes.com; without them Homes.com is skipped with a message saying so, and a run asking for Homes.com alone stops at once.

#### Which fields can be empty?

No site publishes everything for every agent, and the Actor never invents a value: a field with no data is `null` or an empty list. Homes.com gives the agent card only: no email, license, rating or activity date. On Realtor.com the email is the one the agent publishes on their own listings: an agent with no listing has none (tick **Find missing emails on agent websites** to look for it on the agent's site). With **Full profile** off, the profile fields (email, cell phone, licenses, bio…) are empty and the options are not read. `reviews`, `pastSales` and `activeListings` are `null` when their option is off.

#### Is the data safe to open in Excel or to show on a web page?

Bios, team names and reviews are the agents' and clients' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@`: Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- Database mode: the pre-collected database currently holds Realtor.com agents only.
- Homes.com: the agent card only (name, brokerage, phone, photo, sales), its profile page is not read, so the options give nothing for its agents.
- In live mode, a site that cannot judge a filter you set (Homes.com shows no rating, email, cell phone or activity date) is left out of the run, with a warning in the log; if it was the only site, the run stops and says why.
- For a Zillow area with more than 375 agents, the Actor reads the first 375 of the search, then covers the rest automatically; agents with no recent price range appear only in those first 375, so search their name or exact ZIP code to find them.
- A name search on Homes.com returns at most 10 agents, from the whole country: the location is not applied there, the Actor's filters are. Zillow needs a location for a name search, Realtor.com searches the name nationwide without one.
- A Zillow profile shows about 10 reviews, 5 past sales and a few listings: the caps of the options (up to 500, 1,000 and 500) apply in full to Realtor.com.
- `sortBy` works in database mode only: live mode keeps each site's order. A Realtor.com search address pasted in `startUrls` with another sort (`/sort-ratings/`, `/sort-activelistings/`, `/sort-recommendations/`, `/sort-activity/`) is read in the site's default order (most relevant first): the site only serves that one to the Actor, and the log says so once per address.
- `onlyNew` remembers agent IDs, not their content: an agent whose sales or phone changed is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new agent.
- Database mode answers for the states it holds: a state it does not hold yet is skipped, and the log says so.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the agents already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the agents it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the agents of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the agents saved, filtered out and no longer on the site (removed while the run was reading them), and the requests that failed after every retry. Those requests and the removed agents are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause (a location the Actor does not know says so, instead of "run it again").

If a site changes its pages, you are told instead of paying for blank rows. A results page that counts agents but gives none the Actor can read is an error (listed in `FAILED_REQUESTS`), never a quiet "No agents found". If the first 20 agents read all lack their name, photo or brokerage, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first agents are charged. Each site is judged on its own. An agent that `postedAfter` / `postedBefore` drops because it has no date at all counts among those 20. A run of fewer agents (a scheduled `onlyNew` run that finds a few new ones, a small **Max agents**) is checked at its end, from 5 agents: its agents are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `mode` (type: `string`):

`live` reads Zillow, Realtor.com and Homes.com now (fresh data, capped by the sites: 375 agents per Zillow search, about 9 600 per Realtor.com search, 624 per Homes.com search — larger areas are covered automatically). `database` answers instantly from our pre-collected agent database, which currently holds Realtor.com agents (no site cap, filters on contact, sales and freshness).

## `sites` (type: `array`):

Sites to read in live mode (empty = all). In database mode, the sites an agent must come from: the database currently holds Realtor.com agents only, so a choice without Realtor.com returns no agent. Homes.com gives agent cards (name, brokerage, phone, sales); its profile page (email, license) is not read.

## `startUrls` (type: `array`):

Zillow agent directory URLs (`https://www.zillow.com/professionals/real-estate-agent-reviews/austin-tx/?isTopAgent=true`, filters kept) or profile URLs (`https://www.zillow.com/profile/<name>`), Realtor.com agent directory URLs (`https://www.realtor.com/realestateagents/austin_tx`) or profile URLs (`https://www.realtor.com/realestateagents/<id>`, with the short agent number), Homes.com agent directory URLs (`https://www.homes.com/real-estate-agents/austin-tx/`; a Homes.com profile page is not read). When this list is not empty, the location and name fields below are ignored; the filters are applied by the Actor to each agent read, and caps and monitoring still apply. Live mode only. Max 1 000 URLs.

## `location` (type: `string`):

City and state (`Austin, TX`), ZIP code (`78704`), county (`Travis County, TX`) or state (`TX` or `Texas`). In live mode a county or a state is searched Zillow: city by city; Realtor.com and Homes.com: ZIP code by ZIP code (a state is a long run: set Max agents). In database mode the same values filter the database.

## `locations` (type: `array`):

Several locations in one run (cities, ZIP codes, states), each searched on its own; added to **Location**. Agents found by several searches are saved once. Max 500 searches per run.

## `radiusMiles` (type: `integer`):

Also search every ZIP code whose center is within this distance of each city or ZIP code location (live mode: Zillow city by city, Realtor.com and Homes.com ZIP code by ZIP code), or keep database agents whose office is within it. 0 = the location itself only.

## `agentName` (type: `string`):

Agent or team name, full or partial (`smith`). Realtor.com searches it nationwide when no location is set; Zillow needs a location; Homes.com returns at most 10 agents from the whole country (the location is not applied there, the filters are).

## `agentIds` (type: `array`):

Look up known agents directly: Realtor.com fulfillment IDs (digits, `1918615`) or Zillow screen names (`ericbramlett`). A Zillow encoded ID (X1-ZU…) cannot be opened: give the profile URL or the screen name. A screen name of 7 lowercase letters or digits is taken for a Homes.com key and refused: paste its profile URL in **Start URLs** instead. Other search fields are ignored for these; the filters are applied by the Actor to each agent. Max 1 000.

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

Maximum number of agents to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (location, name or start URL), so that the first search cannot use up the whole **Max agents** budget. 0 = no per-search cap. Live mode only: the database mode ranks every location together, **Max agents** caps it.

## `extractDetails` (type: `boolean`):

Open each agent's profile (extra requests per agent in live mode: 1 on Zillow, 3 on Realtor.com): cell and office phones, email, licenses with state and expiry, MLS IDs, website and social links, bio, languages, specialties, service areas, years of experience. Off = directory-card fields only (name, brokerage, rating, sales counts, price range). Database rows always carry the full profile.

## `includeReviews` (type: `boolean`):

Add the agent's latest client reviews (text, rating, date, sub-ratings, agent reply). Needs **Full profile**. Charged per agent that has reviews (see Pricing).

## `maxReviewsPerAgent` (type: `integer`):

Reviews kept per agent when **Add client reviews** is on. A Zillow profile shows about 10 reviews: the higher caps apply to Realtor.com.

## `includePastSales` (type: `boolean`):

Add the agent's closed deals (address, price, date, buyer or seller side, coordinates). Needs **Full profile**. Charged per agent that has past sales (see Pricing).

## `maxSoldPerAgent` (type: `integer`):

Past sales kept per agent when **Add past sales** is on. A Zillow profile shows about 5 past sales: the higher caps apply to Realtor.com.

## `includeListings` (type: `boolean`):

Add the agent's current for-sale and for-rent listings (address, price, beds, baths, status, link). Needs **Full profile**. Charged per agent that has listings (see Pricing).

## `maxListingsPerAgent` (type: `integer`):

Active listings kept per agent when **Add active listings** is on. A Zillow profile shows a few listings only: the higher caps apply to Realtor.com.

## `enrichEmails` (type: `boolean`):

For agents who have a website but no email on the sites, read that website (home page, then the contact and legal pages it links to, 3 pages at most) and add the email published there, tagged `website` in `emails`. Needs **Full profile** (the website is on it). Live mode only. Slower. With **Only agents with an email** on, an agent with a website but no email on the sites is judged after that visit. Charged per agent whose email is found (see Pricing).

## `agentType` (type: `string`):

Buyers, sellers or both (Zillow `isBuying` / `isSelling`, Realtor.com agent type).

## `topAgentsOnly` (type: `boolean`):

Only Zillow Top Agents (badge given by Zillow on sales and reviews). The agents of the other sites are left out when this is on.

## `specialties` (type: `array`):

Agents who list at least one of these specialties.

## `languages` (type: `array`):

Agents who speak at least one of these languages.

## `minPrice` (type: `integer`):

Agents whose deals reach this price (Zillow price range, Realtor.com listing price range).

## `maxPrice` (type: `integer`):

Agents whose price range reaches below this price: an agent whose cheapest deals are under it is kept, even if the range goes much higher (Zillow price range, Realtor.com listing price range).

## `postedAfter` (type: `string`):

Only agents with a listing or a closed sale on or after this date (`lastActivityAt`): `2026-01-01`, or a period before now such as `90 days`, `6 months`. On Zillow only the last closed sale is dated, not the listings. Agents with no dated activity are dropped.

## `postedBefore` (type: `string`):

Only agents whose last listing or sale (`lastActivityAt`) is on or before this date (the whole day is included), or older than a period such as `1 year` (dormant agents). On Zillow only the last closed sale is dated, not the listings.

## `requireEmail` (type: `boolean`):

Drop agents without an email address. With **Find missing emails on agent websites** on, an agent with a website is judged after that visit.

## `requirePhone` (type: `boolean`):

Drop agents without any phone number.

## `requireCellPhone` (type: `boolean`):

Drop agents without a phone marked as mobile / cell.

## `brokerage` (type: `string`):

Only agents whose brokerage name contains this text (`keller williams`, case and accents ignored).

## `minRating` (type: `number`):

Only agents rated at least this (0-5). Unrated agents are dropped when set.

## `minReviews` (type: `integer`):

Only agents with at least this many reviews.

## `minSalesLast12Months` (type: `integer`):

Only agents with at least this many closed sales in the last 12 months.

## `maxSalesLast12Months` (type: `integer`):

Only agents with at most this many closed sales in the last 12 months (newer or part-time agents).

## `minYearsExperience` (type: `integer`):

Only agents licensed or active for at least this many years.

## `maxYearsExperience` (type: `integer`):

Only agents active for at most this many years (recruiting new agents).

## `minTotalSales` (type: `integer`):

Only agents with at least this many closed sales in their whole career on the site.

## `minActiveListings` (type: `integer`):

Only agents with at least this many listings for sale right now (1 = agents with listings).

## `requireFields` (type: `array`):

Only agents where every field picked here is filled.

## `excludeTeams` (type: `boolean`):

Drop profiles that stand for a whole team (keep individual agents).

## `teamLeadersOnly` (type: `boolean`):

Only agents who lead a team (Zillow team profiles).

## `premiumOnly` (type: `boolean`):

Only agents who pay the site for leads (Zillow Premier Agent, Realtor.com paid agent): active buyers of marketing.

## `realtorMembersOnly` (type: `boolean`):

Only agents flagged as members of the National Association of Realtors (Realtor.com flag).

## `excludeKeywords` (type: `array`):

Drop agents whose name or brokerage contains one of these words (case and accents ignored).

## `countOnly` (type: `boolean`):

Return how many agents match the location and filters, split by state and source, without saving or charging any agent (the run start is still charged). The count is written to the run's key-value store (`COUNT`) and the status message.

## `sortBy` (type: `string`):

Order of the database agents (live mode keeps the sites' order).

## `updatedWithinDays` (type: `integer`):

Only database agents whose record was refreshed from the sites within this many days. 0 = any age (each row carries `updatedAt`).

## `databaseOffset` (type: `integer`):

Skip this many matching database agents (in a stable order) to fetch a large list in several runs.

## `onlyNew` (type: `boolean`):

Skip the agents that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their profile is not even opened. First run = everything is new.

## `stateKey` (type: `string`):

Name of the memory used by **Only new agents**. Give each schedule / task its own key (e.g. `austin-agents`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget the agents delivered before under this **Memory key**, then run as a first run.

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

Used for the Zillow requests (Realtor.com needs no proxy). Keep the default: it is included in the price. A residential proxy chosen here is refused (cost). Homes.com always goes through our included residential proxy, for paying Apify plans only: on the Apify free plan, add your own proxy URLs here to read Homes.com; without them Homes.com is skipped with a message (a run asking for Homes.com alone stops at once).

## `maxConcurrency` (type: `integer`):

Maximum parallel requests.

## `maxRequestsPerMinute` (type: `integer`):

Request rate cap for the whole run.

## `minRequestIntervalMs` (type: `integer`):

Pause between two requests to the same site, in milliseconds.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. A request the site turns away is also retried on a new proxy session up to 10 times without using up these retries (Zillow).

## `debugLog` (type: `boolean`):

Log every request.

## Actor input object example

```json
{
  "mode": "live",
  "sites": [
    "zillow",
    "realtor",
    "homes"
  ],
  "startUrls": [],
  "location": "Austin, TX",
  "locations": [],
  "radiusMiles": 0,
  "agentIds": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "extractDetails": true,
  "includeReviews": false,
  "maxReviewsPerAgent": 20,
  "includePastSales": false,
  "maxSoldPerAgent": 50,
  "includeListings": false,
  "maxListingsPerAgent": 50,
  "enrichEmails": false,
  "agentType": "any",
  "topAgentsOnly": false,
  "specialties": [],
  "languages": [],
  "requireEmail": false,
  "requirePhone": false,
  "requireCellPhone": false,
  "requireFields": [],
  "excludeTeams": false,
  "teamLeadersOnly": false,
  "premiumOnly": false,
  "realtorMembersOnly": false,
  "excludeKeywords": [],
  "countOnly": false,
  "sortBy": "relevance",
  "updatedWithinDays": 0,
  "databaseOffset": 0,
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 5,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

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

No description

# 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 = {
    "location": "Austin, TX",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/us-real-estate-agents").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 = {
    "location": "Austin, TX",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/us-real-estate-agents").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 '{
  "location": "Austin, TX",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/us-real-estate-agents --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/us-real-estate-agents"
        }
    }
}
```

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/kolxNUjXdSExbXnwq/builds/zWBDGCsdh8M1Am2oT/openapi.json
