# RateMyAgent Scraper — Real Estate Agent Leads AU NZ US (`scrapersdelight/ratemyagent-scraper`) Actor

178,789 real-estate agents from RateMyAgent (38,170 AU · 8,985 NZ · 131,634 US): name, agency, suburb/state/postcode, star rating, review count, sold + for-sale counts, median sale price, property-type specialties, recent sold listings, plus phone and email — each on ~99% of rows.

- **URL**: https://apify.com/scrapersdelight/ratemyagent-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Real estate, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 per agent returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## RateMyAgent Scraper — Real Estate Agent Leads (AU · NZ · US)

Turn [RateMyAgent](https://www.ratemyagent.com.au) into a clean real-estate agent lead
list. One row per agent: **agentName, jobTitle, agencyName, suburb, state, postcode,
rating, reviewCount, soldCount, forSaleCount, avgDaysOnMarket, avgSalePrice,
medianSalePrice, totalSalesValue, specialties, phone, email, websiteUrl, socials, bio**
and up to 10 **recent listings** with addresses and sold prices.

**Email and phone are each on ~99% of rows** — email 1,354 of 1,366 agents (**99.1%**) and
phone 1,356 of 1,366 (**99.3%**) across every validation run listed below, including a full
1,000-agent walk. Measured, and not rounded up to 100%: on the 1,000-agent run specifically,
email was 98.8%. No login, no cookies, no CAPTCHA solving.

**Scope: 178,789 agents** — counted live off RateMyAgent's own sitemaps on 2026-08-16,
not quoted from a marketing page:

| Directory | Sitemap shards | Agent URLs | Unique agents |
|---|---|---|---|
| `ratemyagent.com.au` — **Australia** | 4 | 72,144 | **38,170** |
| `ratemyagent.co.nz` — **New Zealand** | 1 | 22,004 | **8,985** |
| `ratemyagent.com` — **United States** | 14 | 243,277 | **131,634** |
| | | | **178,789** |

```json
{
  "regions": ["AU"],
  "maxItems": 50,
  "includeRecentSales": true,
  "includeBio": true,
  "maxConcurrency": 8
}
```

Click **Try for free** and hit **Start** — that block is literally the input this Actor
ships with. It returns **50 Australian agents** for **$0.15** in under two minutes. Across
the three separate runs of exactly that input recorded below, email was 100%, rating 100%,
phone 98%, sold count 96–100% and suburb 94–96%.

***

### Read this before you run it

Six things that would otherwise turn into a refund request. The first one is not a
technical caveat — read it properly.

#### 1. `robots.txt` — the actual lines, unedited

This Actor reads RateMyAgent's own API host, **not** the public HTML pages. Both files
were fetched on **2026-08-16**, and both are quoted here in full.

**`https://api.ratemyagent.com.au/robots.txt`** — HTTP 200, **25 bytes**. That is the
entire file:

```
User-agent: *
Disallow: /
```

Identical, byte for byte, on `https://api.ratemyagent.com/robots.txt` (US) and
`https://api.ratemyagent.co.nz/robots.txt` (NZ) — all three 25 bytes, all three
`Disallow: /`.

For contrast, **`https://www.ratemyagent.com.au/robots.txt`** — HTTP 200, 1,141 bytes —
opens with:

```
user-agent: *
allow: /
disallow: /error/
disallow: /404
disallow: /500
disallow: /dashboard/
disallow: /promoter/
disallow: /account
disallow: /agency/
disallow: /agent/
disallow: /suburb/search/
disallow: /testimonial/
disallow: /arkadia

Sitemap: https://www.ratemyagent.com.au/sitemap.xml
```

So, stated plainly and without a justification wrapped around it: **the agent records in
this dataset come from `api.ratemyagent.*`, a host whose `robots.txt` disallows every path
for every crawler.** The only thing this Actor reads from the `allow: /` www host is the
`Sitemap:` file that same robots.txt points at. It does not log in, does not use an API
key, does not defeat any challenge and reads nothing behind authentication — but it is not
robots-compliant on the API host, and RateMyAgent's terms of use prohibit automated
collection regardless of technical access. Whether that is acceptable is your decision, in
your jurisdiction, for your use case. **Do not run this if the answer is no.**

#### 2. The public HTML profile pages are unreachable, and that is why the API is used

`www.ratemyagent.*/real-estate-agent/…` is behind **DataDome tag v5.9.0**. Measured
through the Apify proxy: **0 of 60 requests returned HTTP 200** — 52 in the original probe
across datacenter, residential, eight exit countries, four user-agents, eight host
variants and three real Chromium modes (headless-shell, new-headless and fully headed with
a warmed homepage session), plus a fresh **0 / 8** re-check on 2026-08-16. Every single one
was HTTP 403 with a `dd={…}` body of 777–781 bytes. There is no HTML route to this data,
and this Actor does not pretend there is.

#### 3. Geography fields differ by country, because RateMyAgent stores them differently

RateMyAgent prints an agent's market areas as a single string, and the format is not the
same in each directory:

| Region | Market string | `suburb` | `state` | `postcode` | `city` |
|---|---|---|---|---|---|
| **AU** | `Kingaroy, QLD, 4610` | 96% | 96% | 96% | 14% |
| **NZ** | `Ravensbourne, Otago` | 90% | 90% | **0%** | 26% |
| **US** | `33126, FL` | **8%** | 92% | 84% | 73% |

**New Zealand agents have no postcode** and **US agents have no suburb** — the US
directory keys markets on ZIP, and the town shows up in `city` instead. Those are source
gaps, not scraping failures. `primaryMarket` always carries the raw string and
`marketAreas` carries every one of them (a median of 8–10 per agent), so you can always
re-parse yourself.

#### 4. `rating` is 100% filled — including agents with zero reviews

Every profile carries a `rating`, and an agent nobody has reviewed carries `0`. A
`minRating` filter on its own therefore does **not** give you well-reviewed agents. Pair it
with **Minimum review count** if that is what you mean.

#### 5. Recent-listing prices are the agent's own free text

`recentListings[].priceText` is exactly what RateMyAgent printed —
`"$740,000 - $785,000"`, `"Offers Over $1,049,000"`, `"Auction"`, `"Contact Agent"`,
`"Price Adjusted"`. `priceMin` / `priceMax` are that string parsed into numbers when it
contains a real figure (≥ $10,000), and `null` when it does not. **The reliable numeric
price fields are `medianSalePrice` (94.6%), `avgSalePrice` (63.1%) and `totalSalesValue`
(94.6%)**, which come straight from RateMyAgent's own sales statistics, not from text.

#### 6. Everything except **Regions** is a post-fetch filter

Agent URLs carry no geography, rating or sales history, so state, suburb, rating, review
count, sold count, email/phone and claimed-profile filters all run **after** a profile has
been fetched. You are billed only for agents **returned** — a filtered-out profile costs
run time, not money — but a rare suburb can cost hundreds of fetches per row kept.

***

### What you get

One row per unique agent. `scrapedAt` is a full UTC timestamp; prices are plain numbers in
the local currency (AUD / NZD / USD — see `region`).

| Group | Fields | Example |
|---|---|---|
| **Identity** | `agentName`, `jobTitle`, `profileCode`, `profileUrl`, `profileSlug`, `region`, `country` | `Adrian Jian-Sheng Wu` · `Sales Executive` · `ce581` · `AU` |
| **Agency** | `agencyName`, `agencyUrl`, `officeName`, `officeUrl` | `Strathfield Partners` |
| **Contact** | `phone`, `email`, `contactFormEnabled`, `websiteUrl`, `facebookUrl`, `instagramUrl`, `linkedinUrl`, `youtubeUrl`, `twitterUrl`, `tiktokUrl` | `0410 296 171` · `adrianw@strathfieldpartners.com.au` |
| **Reputation** | `rating`, `reviewCount`, `isClaimed`, `featureLevel`, `isAwardWinner`, `isHighlyRecommended`, `isPriceExpert`, `isTrustedAgent`, `hasMlsConnection` | `4.9` · `77` · `Premium` |
| **Geography** | `suburb`, `city`, `state`, `postcode`, `primaryMarket`, `marketAreas[]`, `marketAreaCount` | `Homebush` · `NSW` · `2140` · 10 areas |
| **Sales record** | `soldCount`, `forSaleCount`, `avgDaysOnMarket`, `avgSalePrice`, `medianSalePrice`, `totalSalesValue` | `36` · `16` · `26` days · `$2,080,861` · `$74,911,000` |
| **Specialties** | `specialties[]`, `propertyTypesSold{}`, `medianPriceByPropertyType{}` | `["Apartment","House","Land"]` · `{Apartment: 26, House: 9, Land: 1}` |
| **Recent listings** | `activeListingCount`, `completedSaleCount`, `recentSoldPriceMin`, `recentSoldPriceMax`, `recentListings[]` | `16` · `36` · `$630,000` – `$4,500,000` |
| **Profile** | `bio`, `avatarUrl`, `coverImageUrl`, `scrapedAt` | |

The dataset ships with a saved **table view** — *Agents* — so you do not have to configure
columns.

***

### Field fill — measured on 149 agents

Every percentage below comes from **one run on this Actor**, `{"regions":["AU","NZ","US"],
"maxItems":150}` on **2026-08-16** (Apify run `CobnOy3K5kMhJoBaF`): 50 AU · 50 NZ · 49 US,
round-robin interleaved, `includeRecentSales` and `includeBio` both on. It asked for 150 and
delivered **149** — one profile stayed unreachable after four residential retries, which is
the normal ~99% shape of this source and exactly what the retry ladder is for. Sorted by
fill, so the sparse fields are impossible to miss.

| Field | Fill | Notes |
|---|---|---|
| `agentName`, `profileCode`, `profileUrl`, `profileSlug`, `region`, `country` | 100% | |
| **`email`** | **100%** | a real mailbox, not a contact form — 100% here in all three countries, but **98.8% on the 1,000-agent run**; budget for ~99% |
| `rating`, `reviewCount` | 100% | `rating` is `0` for un-reviewed agents, not `null` |
| `featureLevel` | 100% | `Default` · `Basic` · `Premium` |
| `contactFormEnabled`, `marketAreaCount`, `scrapedAt` | 100% | |
| **`phone`** | **99.3%** | AU 98% · NZ 100% · US 100% |
| `soldCount` | 97.3% | AU 100% · NZ 98% · US 94% |
| `activeListingCount`, `completedSaleCount` | 97.3% | with `includeRecentSales` on |
| `forSaleCount` | 96.6% | |
| `medianSalePrice`, `totalSalesValue` | 94.6% | |
| `state`, `primaryMarket`, `marketAreas` | 92.6% | ~7% of agents publish no market areas at all |
| `avatarUrl` | 92.6% | |
| `agencyName`, `agencyUrl` | 91.3% | |
| `officeName`, `officeUrl` | 89.9% | |
| **`websiteUrl`** | **76.5%** | AU 82% · NZ 86% · US 61% |
| `recentListings` | 75.8% | an agent with no live or recent listings gets an empty array |
| `jobTitle` | 70.5% | AU 84% · NZ 94% · **US 33%** |
| `specialties`, `propertyTypesSold` | 69.8% | |
| **`suburb`** | **65.1%** | AU 96% · NZ 90% · **US 8%** — see gotcha 3 |
| `avgDaysOnMarket`, `medianPriceByPropertyType` | 65.1% | |
| `avgSalePrice` | 63.1% | RateMyAgent only computes it once there are enough disclosed sales |
| **`postcode`** | **59.7%** | AU 96% · **NZ 0%** · US 84% — see gotcha 3 |
| **`bio`** | **59.1%** | AU 62% · NZ 80% · US 35% |
| `recentSoldPriceMin` / `recentSoldPriceMax` | 51.0% | only where the listing price was a real figure, not "Auction". The window is **RateMyAgent's own recent-campaigns window (~10 sales)**, not the agent's lifetime — an agent with 18 completed sales returns the most recent 10, so these are the min/max of that window |
| `coverImageUrl` | 43.6% | |
| **`facebookUrl`** | **40.9%** | |
| **`city`** | **37.6%** | AU 14% · NZ 26% · **US 73%** |
| `instagramUrl` | 20.8% | |
| `linkedinUrl` | 19.5% | |
| `youtubeUrl` · `twitterUrl` · `tiktokUrl` | 3.4% · 2.7% · 2.0% | |

Every `*Url` field is a real URL. RateMyAgent does not validate its own social fields, so a
small share of agents typed a bare handle (`uday_chandran`), a bare host (`compass.com`) or a
mistyped scheme (`https//www.instagram.com/…`, `https:wwwinstagram.com.au/…`) into one —
**14 of the 1,917 social/website values across 1,060 delivered rows**. All 14 are normalised to
`https://…` before they reach your dataset, so nothing lands in your CRM as a dead link, and no
value is ever dropped. Where the platform's path is ambiguous (LinkedIn `/in/` vs `/company/`,
YouTube `/@` vs `/channel/`) a bare handle is passed through untouched rather than guessed into
a wrong link.

`email` is likewise the agent's own free text. One agent in the same 1,048 addresses had typed
**two** addresses into the single field (`adam@… info@…`), which fails a CRM import; the first
valid address is returned rather than the raw pair.

Boolean flags are **rates**, not fills — the field is always present. On the same 149 rows:
`isClaimed` 100% true, `contactFormEnabled` 100%, `hasMlsConnection` 60.4%, `isPriceExpert`
20.1%, `isAwardWinner` 6.0%, `isHighlyRecommended` 3.4%, `isTrustedAgent` 2.7%.

**Every run prints its own measured fill in the log**, so you can check any slice you pull
against the table above rather than trusting it.

***

### How to run it

#### Whole directories

```json
{ "regions": ["AU", "NZ"], "maxItems": 5000 }
```

Reads each selected directory's `sitemap-sales-agents.xml`, queues its agents, and
**interleaves the regions round-robin** — so `["AU","NZ","US"]` with `maxItems: 150`
returns roughly 50 of each, not 150 Australians.

#### Paging across runs

```json
{ "regions": ["AU"], "maxItems": 5000, "skip": 0 }
{ "regions": ["AU"], "maxItems": 5000, "skip": 5000 }
```

Sitemap order is stable between runs. With no filters set, a run consumes **exactly**
`maxItems` candidates, so consecutive pages are disjoint — verified below. Advance `skip`
by `maxItems`, not by the row count you received.

#### Specific agents

```json
{
  "profileUrls": [
    "https://www.ratemyagent.com.au/real-estate-agent/troy-schultz-bz557/sales/overview",
    "https://www.ratemyagent.co.nz/real-estate-agent/sharon-larkins-bb682/sales/overview",
    "https://www.ratemyagent.com/real-estate-agent/elizabeth-ramirez-garza-b04l35/sales/overview",
    "bg295"
  ]
}
```

Refresh a list you already hold. Skips the sitemaps and overrides Regions and every filter.
Each URL's own domain decides the directory; a bare agent code uses the first region
selected. An entry that is not a RateMyAgent agent profile is skipped with a warning naming
it and the rest are still scraped; only a list with nothing usable in it stops the run, and
it stops cleanly with nothing charged.

#### Filtering to a lead list

```json
{
  "regions": ["AU"],
  "states": ["NSW"],
  "minReviewCount": 5,
  "maxItems": 12
}
```

That exact input returned **12 NSW agents from 36 profiles fetched** (run `RFVX1oZYDPWlrKymv`)
— about 3 fetches per row kept, and the 18 discarded agents were not billed. Budget for that
ratio: a geography filter multiplies **run time**, not price, so `maxItems: 500` on the same
filter is still $1.50 but will fetch roughly 1,500 profiles to find them.

- `states` — AU state codes (`NSW`, `VIC`, `QLD`, `WA`, `SA`, `TAS`, `ACT`, `NT`), NZ
  regions (`Auckland`, `Otago`, `Canterbury`, `Wellington`…), US state codes (`FL`, `TX`,
  `CA`…).
- `suburbs` — AU/NZ suburb names (`Bondi`, `Takapuna`) or US ZIPs (`33126`).
- `minRating` · `minReviewCount` · `minSoldCount` · `withEmailOnly` · `withPhoneOnly` ·
  `claimedOnly`.

`withEmailOnly` and `withPhoneOnly` measured ~100% fill, so they discard almost nothing —
they are there as a guarantee, not as a narrowing tool.

#### Lean, faster export

Set `includeRecentSales: false` and `includeBio: false`. Two API calls per agent instead of
four, a much smaller dataset, and you keep every contact, reputation, geography and
sales-statistics field. **Price per agent is identical** — this buys time, not money.

#### Scheduling and integrations

Save the input as a **Task**, then attach an Apify **Schedule**. The dataset is available
over the REST API and through the standard Apify integrations (Zapier, Make, n8n, webhooks,
MCP). Starting a run from the API:

```bash
curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~ratemyagent-scraper/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"regions":["AU"],"states":["VIC"],"maxItems":200}'
```

***

### Sample row

A real row from an actual run, captured 2026-08-16 (bio truncated here for length,
`recentListings` cut to 2 of 10).

```jsonc
{
  "agentName": "Adrian Jian-Sheng Wu",
  "jobTitle": "Sales Executive",
  "profileCode": "ce581",
  "profileUrl": "https://www.ratemyagent.com.au/real-estate-agent/adrian-jian-sheng-wu-ce581/sales/overview",
  "profileSlug": "adrian-jian-sheng-wu-ce581",
  "region": "AU",
  "country": "Australia",

  "agencyName": "Strathfield Partners",
  "agencyUrl": "https://www.ratemyagent.com.au/real-estate-agency/strathfield-partners-ao572/sales/overview",
  "officeName": "Strathfield Partners",
  "officeUrl": "https://www.ratemyagent.com.au/real-estate-agency/strathfield-partners-ao572/sales/overview",

  "phone": "0410 296 171",
  "email": "adrianw@strathfieldpartners.com.au",
  "contactFormEnabled": true,
  "websiteUrl": "https://www.strathfieldpartners.com.au/",
  "facebookUrl": "https://www.facebook.com/Adrian-Wu-491352231209294/",
  "instagramUrl": null, "linkedinUrl": null, "youtubeUrl": null, "twitterUrl": null, "tiktokUrl": null,

  "rating": 4.9,
  "reviewCount": 77,
  "isClaimed": true,
  "featureLevel": "Premium",
  "isAwardWinner": false, "isHighlyRecommended": false, "isPriceExpert": false, "isTrustedAgent": false,
  "hasMlsConnection": false,

  "suburb": "Homebush",
  "city": null,
  "state": "NSW",
  "postcode": "2140",
  "primaryMarket": "Homebush, NSW, 2140",
  "marketAreas": ["Homebush, NSW, 2140", "Strathfield, NSW, 2135", "Parramatta, NSW, 2150", "Newington, NSW, 2127", "Homebush West, NSW, 2140", "Campsie, NSW, 2194", "Carlingford, NSW, 2118", "Canterbury, NSW, 2193", "Lane Cove, NSW, 2066", "Hurstville, NSW, 2220"],
  "marketAreaCount": 10,

  "soldCount": 36,
  "forSaleCount": 16,
  "avgDaysOnMarket": 26,
  "avgSalePrice": 2080861.11,
  "medianSalePrice": 2080861.11,
  "totalSalesValue": 74911000,
  "specialties": ["Apartment", "House", "Land"],
  "propertyTypesSold": { "Apartment": 26, "House": 9, "Land": 1 },
  "medianPriceByPropertyType": { "Apartment": 874461.54, "House": 5569444.44, "Land": 2050000 },

  "avatarUrl": "https://cdn.ratemyagent.com.au/photo/2023-02-17-0321-1361-183289.png",
  "coverImageUrl": "https://cdn.ratemyagent.com.au/cover/2024-10-21-0923-2263-937689.png",
  "scrapedAt": "2026-08-16T18:54:31.238Z",
  "bio": "Committed to providing six star service to his clients, Adrian has a clear forward thinking vision for the future…",

  "activeListingCount": 16,
  "completedSaleCount": 36,
  "recentSoldPriceMin": 630000,
  "recentSoldPriceMax": 4500000,
  "recentListings": [
    {
      "status": "Active", "propertyType": "Apartment",
      "priceText": "Auction", "priceMin": null, "priceMax": null,
      "address": "56/22 Buchanan St", "suburb": "Balmain", "state": "NSW", "postcode": "2041",
      "bedrooms": 2, "bathrooms": 2, "carparks": 1, "resultDate": null,
      "listingUrl": "https://www.ratemyagent.com.au/real-estate-agency/strathfield-partners/property-listings/56-22-buchanan-st-balmain-ak2h05"
    },
    {
      "status": "Active", "propertyType": "Apartment",
      "priceText": "Just Listed", "priceMin": null, "priceMax": null,
      "address": "214/39 Cooper St", "suburb": "Strathfield", "state": "NSW", "postcode": "2135",
      "bedrooms": 2, "bathrooms": 2, "carparks": 1, "resultDate": null,
      "listingUrl": "https://www.ratemyagent.com.au/real-estate-agency/strathfield-partners/property-listings/214-39-cooper-st-strathfield-ak1mr2"
    }
  ]
}
```

Fields people misread:

- `medianSalePrice` and `avgSalePrice` are **whole-career figures RateMyAgent publishes**,
  not a 12-month median, and they can be equal on agents with few disclosed sales.
- `specialties` is derived from **what the agent actually sold**, ordered by count — it is
  not a self-declared badge. `propertyTypesSold` is the same data as raw counts.
- `soldCount` is RateMyAgent's `Sold` statistic; `completedSaleCount` is how many completed
  campaigns their listings feed carries. They usually match and occasionally do not.
- `agencyName` falls back to the office name when RateMyAgent files an agent under an
  office with no parent agency (common in the US directory).

***

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| **🎯 What to scrape** | | | |
| `regions` | multi-select | `["AU"]` | `AU` 38,170 · `NZ` 8,985 · `US` 131,634. The only pre-fetch filter. |
| `maxItems` | integer | `1000` (prefilled `50`) | Rows returned, and your hard cost cap. |
| `skip` | integer | `0` | Skip this many agents — page across runs. |
| `profileUrls` | string list | — | Paste agent URLs or bare codes; overrides Regions and every filter. |
| **📍 Geography** | | | |
| `states` | string list | — | AU state codes / NZ regions / US state codes. Post-fetch. |
| `suburbs` | string list | — | AU-NZ suburb names or US ZIPs. Post-fetch. |
| **⭐ Lead quality** | | | |
| `minRating` | select | `0` | `3.0+` · `4.0+` · `4.5+` · `4.8+`. Remember gotcha 4. |
| `minReviewCount` | integer | `0` | The filter that separates marketed agents from the long tail. |
| `minSoldCount` | integer | `0` | Costs one extra API call per agent tested. |
| `withEmailOnly` | boolean | `false` | ~100% fill, so it discards almost nothing. |
| `withPhoneOnly` | boolean | `false` | ~99% fill. |
| `claimedOnly` | boolean | `false` | Agents who took ownership of their profile. |
| **⚙️ Output + performance** | | | |
| `includeRecentSales` | boolean | `true` | Adds listing counts, sold price range and `recentListings`. |
| `includeBio` | boolean | `true` | Adds the agent's own bio (59.1% fill). |
| `maxConcurrency` | integer | `8` | Lower to 3–4 if you see "unreachable after retries" warnings. |
| `proxyConfiguration` | proxy | Apify **RESIDENTIAL** | Exit country is set per region automatically. Leave it alone. |

***

### Pricing

**$0.003 per agent returned — $3.00 per 1,000.** Pay-per-event, one event
(`agent-scraped`), nothing else.

| You want | Agents | Cost |
|---|---|---|
| A smoke test (the shipped default) | 50 | **$0.15** |
| A city's worth of agents | 500 | $1.50 |
| A state | 5,000 | $15.00 |
| **All of Australia** | **38,170** | **$114.51** |
| **All of New Zealand** | **8,985** | **$26.96** |
| **All of the United States** | **131,634** | **$394.90** |
| **All three, every agent** | **178,789** | **$536.37** |

- **You are charged for rows delivered.** An agent fetched and then removed by your filters
  is never charged. An agent that is queued twice is charged once — duplicates are dropped
  before billing.
- **Rows are charged as they are pushed** (`Actor.pushData(items, 'agent-scraped')`), so if
  you hit a budget cap you get whole rows and stop, never a half-billed dataset.
- **`maxItems` is your hard cost cap.** It counts unique, delivered agents.
- Turning `includeRecentSales` off halves the request count and costs exactly the same.

***

### Honest limits

- **The API host's `robots.txt` says `Disallow: /`.** Quoted verbatim at the top of this
  page. Read it before you run anything.
- **No licence numbers, and no review text.** RateMyAgent publishes agent reviews on the
  site, but a review is a different record from an agent and merging them would give you a
  dirty dataset. This Actor returns agents. `reviewCount` and `rating` are the summary.
- **NZ agents have no postcode; US agents have no suburb.** Source gaps, documented in
  gotcha 3 with the per-country numbers.
- **`avgSalePrice` is 63.1% and `bio` 59.1%.** Those are optional fields RateMyAgent fills
  in or does not; nothing here can conjure them.
- **`email` is ~99%, not 100%.** It was 100% on every sample up to 149 agents and 98.8% on
  the 1,000-agent run. Use `withEmailOnly` if a row without one is useless to you.
- **The sales record is RateMyAgent's, not the MLS's.** It reflects campaigns RateMyAgent
  knows about. `hasMlsConnection` (60.4% across AU+NZ+US; AU 2%, NZ 80%, US 100%) tells you
  whether an MLS feed is attached.
- **This is AU/NZ/US only.** RateMyAgent runs three directories and there is no fourth.
- **No login, no cookies, no CAPTCHA solving service, no browser automation.**

***

### How it works, and what it cost to make reliable

Enumeration reads the sitemaps the `www` `robots.txt` itself points at
(`/sitemap.xml` → `/sitemap-sales-agents.xml` → shards). Extraction reads RateMyAgent's own
JSON backend — up to four calls per agent (`/Details`, `/Markets`, `/Stats`,
`/Campaigns`) — ordered so the cheapest filters run first: an agent removed by a rating or
email filter costs **one** request, not four.

**Transport ladder, re-measured through the Apify proxy on 2026-08-16.** Each rung is
single-shot against real agent codes with a fresh, hyphen-free session per call — no
retries, so these are raw first-attempt rates:

| Rung | Result |
|---|---|
| `www` HTML profile page, RESIDENTIAL + `country-AU` | **0 / 8 → HTTP 403 DataDome, 781-byte `dd={…}` body** |
| API host, Apify auto / datacenter | 18 / 20 = **90.0%** |
| API host, RESIDENTIAL + `country-AU` | 20 / 20 = **100%** |

Datacenter drops roughly one call in ten, and a dropped call on this Actor means a missing
agent rather than a retry you can see, so **RESIDENTIAL with the exit country matched to the
region is the default.** Misses are absorbed by a four-attempt ladder that mints a brand-new
session each time rather than dropping to a cheaper rung. The one exception is the sitemap
host, which is not walled and is read over the cheap datacenter rung — a shard is ~2.2 MB and
reading it over residential would burn the run's proxy budget for nothing.

**Sustained load, this Actor end-to-end on the Apify platform (2026-08-16).** Every row is a
real run you can look up by id:

| Run | Input | Result |
|---|---|---|
| `CobnOy3K5kMhJoBaF` | AU + NZ + US, 150 | 150 fetched → **149 rows**, 1 unreachable, 323 s |
| `NF9dQj91daJdtfwFn` | AU, 50 (shipped default) | 50 fetched → **50 rows**, 0 unreachable, 102 s |
| `TkHukP1GB8blZ3Qau` · `l70Sm9hkV5aKKmWuC` | AU, 50 (shipped default) | 50 → **50 rows** each, 0 unreachable |
| `dr21FLov5pZ3mimxj` | AU, 25, `skip: 50` | 25 fetched → **25 rows**, 0 unreachable, 57 s |
| `NI9PWTcKheUBchdK2` | NZ + US, 30 | 30 fetched → **30 rows**, 0 unreachable, 69 s |
| `RFVX1oZYDPWlrKymv` | AU + NSW + 5 reviews, 12 | 36 fetched → **12 rows**, 0 unreachable, 107 s |
| `FLkgTJuK8UieR1s1Y` | bare `{}` (defaults to 1,000) | 1,000 fetched → **1,000 rows**, 0 unreachable, 1,716 s |
| **Combined** | | **1,391 profiles fetched, 1 unreachable = 99.93%** |

One unreachable profile in 1,391 is the honest number. It is not 100%, and the Actor is built
around that: a miss costs you a row, never the run.

**Behaviour at the run time limit.** Three deliberately unbounded runs
(`{"regions":["AU","NZ","US"],"maxItems":100000}`) capped at a 120-second run timeout returned
**34, 36 and 28 agents**, each finishing **Succeeded** with a status message saying it stopped
early — never timed out, and never discarded rows it had already fetched. The Actor reads the
platform's own deadline, stops starting new work before it, and pushes what it has.

***

### Duplicates — measured

| Walk | Agents returned | Unique | Duplicates |
|---|---|---|---|
| AU + NZ + US, 150 (`CobnOy3K5kMhJoBaF`) | 149 | **149** | **0.0%** |
| AU, 50, `skip: 0` (`NF9dQj91daJdtfwFn`) | 50 | **50** | **0.0%** |
| AU, 25, `skip: 50` (`dr21FLov5pZ3mimxj`) | 25 | **25** | **0.0%** |
| **The two contiguous AU pages combined** | **75** | **75** | **0.0% overlap** |
| bare `{}`, 1,000 rows (`FLkgTJuK8UieR1s1Y`) | 1,000 | **1,000** | **0.0%** |

The sitemap lists an agent under three URLs (`/overview`, `/properties`, `/reviews`) —
72,144 AU URLs collapse to 38,170 agents — so duplicates are dropped **at enumeration**,
before a single request is spent. A run-wide set keyed on `region:profileCode` then drops
anything that slips through **before it is pushed or billed**, and every run ends by checking
that the number of rows equals the number of distinct codes and printing the result in the
log. Across 1,366 delivered rows that check has never diverged.

Across runs, use `skip` — dedupe is per-run by design, and with no filters set the page
boundaries are exact (verified above: zero overlap between `skip: 0` and `skip: 50`).

***

### When something goes wrong

This Actor is built not to end a run in the **Failed** state. A failed run tells you nothing
useful, loses the rows already fetched, and counts against the Actor's health on the Store —
so every problem below instead finishes **Succeeded**, keeps whatever it delivered, and puts
the reason in the run's status message. **Nothing partial is ever billed: you are charged per
row delivered, so a run that delivers nothing charges nothing.**

- **0 rows because everything was filtered out** → succeeds, warns, charges nothing. A
  legitimately empty result is not a broken one.
- **0 rows because the profiles were unreachable** → succeeds with a status message naming
  the counts (fetched, unreachable after retries, retired, filtered out). Re-run: the
  residential pool was having a bad minute.
- **Hit the run time limit** → stops starting new work, pushes what it has, says so in the
  status message. Raise the run timeout or lower Max results.
- **Hit your Max total charge** → stops at a whole row boundary and tells you. Never a
  half-billed dataset.
- **Sitemap host down, or its layout changed** → that region is skipped with a warning; the
  other regions still run.
- **Bad entries in Agent profile URLs** → skipped individually with a warning, naming the
  first one. The valid entries still run.
- **A high block rate** → warns with the measured percentage; the rows you got are real.
- Fewer rows than requested because the directory ran out is a **warning, not an error**.

***

### Who buys this

- **PropTech and real-estate SaaS** (CRM, listing tools, digital marketing) selling to
  agents in AU, NZ and the US — `soldCount`, `medianSalePrice` and `featureLevel` rank the
  list by who can actually pay.
- **Recruiters and franchise groups** poaching high-performing agents: rating, review
  count, sold count and current agency, with an email on every row.
- **Photography, staging, styling and signage suppliers** targeting agents by suburb and
  by the property types they actually sell.
- **Mortgage brokers and conveyancers** building referral networks in specific postcodes.
- **Market analysts** — `medianPriceByPropertyType` and `avgDaysOnMarket` per agent per
  suburb is a view of a market that a listings scrape does not give you.

***

### Our other real-estate agent Actors

| Actor | Coverage | Why you would use it instead |
|---|---|---|
| [Zillow Agent Leads Scraper](https://apify.com/scrapersdelight/zillow-agent-leads-scraper) | US | Zillow's own agent directory with phone, email and licence number |
| [Realtor.com Agent Leads Scraper](https://apify.com/scrapersdelight/realtor-agent-leads-scraper) | US | Realtor.com profiles, recent sales and price bands |
| [FastExpert Agent Scraper](https://apify.com/scrapersdelight/fastexpert-agent-scraper) | US | Agent rankings with transaction history and emails |
| [HomeLight Scraper](https://apify.com/scrapersdelight/homelight-scraper) | US | Top-agent rankings plus licence data |

That family is **US-only**. RateMyAgent is the one that covers **Australia and New
Zealand** — 47,155 AU + NZ agents that none of the others can reach — and it carries the
review-based reputation signal (`rating`, `reviewCount`, `isHighlyRecommended`) that the
US listing portals do not publish. Buy it *alongside* them, not instead of them.

***

### FAQ

**Does this need an account, a login or cookies?**
No. No login, no cookie jar, no API key, no CAPTCHA solving service, no browser automation.

**Do I really get email addresses?**
Yes — 1,354 of 1,366 agents across every validation run (99.1%), and they are real mailboxes,
not contact-form links. It was 100% on the 149-agent three-country run and 98.8% on the
1,000-agent run, so plan on ~99% rather than all of them.

**Why doesn't it scrape the normal RateMyAgent pages?**
Because they cannot be scraped. The public profile pages are behind DataDome v5.9.0 and
returned HTTP 403 on **every** attempt across every proxy, country, user-agent and browser
mode tried — including a fresh 0/8 re-check on 2026-08-16, each one a 781-byte `dd={…}`
body. See gotcha 2.

**Is it robots-compliant?**
No, and this page says so plainly. The API host's `robots.txt` is 25 bytes and reads
`User-agent: *` / `Disallow: /`. It is quoted in full at the top. Read it and make your own
call.

**Can I get every agent in Australia?**
Yes — `{"regions":["AU"],"maxItems":40000}` queues all 38,170 for $114.51. Give it a run
timeout to match: 1,000 agents took 29 minutes, so budget several hours, or page it with
`skip` across several shorter runs.

**Two runs — will I get duplicates?**
Within a run, never: dedupe is on `region:profileCode`, and every run checks and logs that
rows equal distinct codes. Across runs with no filters set, `skip: N` after `maxItems: N` is
exact — verified at 0.0% overlap between `skip: 0` and `skip: 50`.

**Do I get charged for rows a filter removed?**
No. Filtered-out agents are never pushed and never billed.

**Will a run ever succeed with zero rows?**
Yes, in a few honest cases — filters that matched nothing, a run time limit hit before the
first agent, or a bad minute on the residential pool. Each one says which in the run's status
message, and each one charges nothing. See "When something goes wrong".

**Do I need a residential proxy?**
Yes — it is the default and you should leave it alone. Re-measured 2026-08-16, single-shot:
datacenter 18/20 (90.0%), RESIDENTIAL with a country-matched exit 20/20 (100%). One dropped
call in ten is a missing agent, which is why the cheap rung is only used for the sitemaps.

***

### Legal & fair use

This Actor reads RateMyAgent's public sitemaps and its own JSON backend. It does not log
in, does not use an API key, does not defeat any anti-bot challenge, and collects nothing
behind authentication.

**The API host it reads publishes a `robots.txt` of `User-agent: * / Disallow: /`** —
quoted verbatim near the top of this page. This Actor is therefore **not robots-compliant
on that host**, and RateMyAgent's terms of use prohibit automated collection regardless of
technical access.

Records contain **personal data** — agent names, direct mobile numbers and personal email
addresses. **You are responsible for complying with RateMyAgent's terms of service and with
how you use the data**, including the Australian Privacy Act and the Spam Act, the New
Zealand Privacy Act and Unsolicited Electronic Messages Act, and US CAN-SPAM and state
privacy law.

RateMyAgent® is a trademark of its owner. This Actor is not affiliated with, endorsed by,
or connected to RateMyAgent.

***

### Feedback

Found a missing field or want a new filter? Open an issue on the **Issues** tab, and if the
Actor earns it, a review on the **Reviews** tab helps other buyers find it.

# Actor input Schema

## `regions` (type: `array`):

Which RateMyAgent directories to crawl. AU = ratemyagent.com.au, NZ = ratemyagent.co.nz, US = ratemyagent.com. Pick more than one and they are crawled in the order listed. This is the ONLY pre-fetch filter — every other filter below runs after a profile is fetched.

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

How many agent records to return, and your hard cost cap: 50 = $0.15, 1,000 = $3.00, 10,000 = $30. One record is one agent. RateMyAgent publishes 178,789 agents across the three countries. The prefilled 50 is a smoke test and takes under two minutes — raise it once the rows look right. Pace yourself on the run timeout: 1,000 agents took about 29 minutes.

## `skip` (type: `integer`):

Skip this many agents before returning any. Set it to the number you already have to continue where the last run stopped — e.g. run 1 with Max results 5,000, then run 2 with Skip 5,000. Sitemap order is stable between runs.

## `profileUrls` (type: `array`):

Paste exact RateMyAgent agent URLs (https://www.ratemyagent.com.au/real-estate-agent/troy-schultz-bz557/sales/overview) or bare agent codes (bz557) to refresh a list you already have. This skips the sitemaps entirely and overrides Regions and every filter. The region is taken from each URL's domain; bare codes use the first region selected above. An entry that is not a RateMyAgent agent profile is skipped with a warning naming it, and the rest are still scraped; only a list with nothing usable in it stops the run (cleanly, with nothing charged).

## `states` (type: `array`):

Keep only agents who list at least one market area in these states/regions (case-insensitive, exact match on the part RateMyAgent prints). Leave empty for every state.

## `suburbs` (type: `array`):

Keep only agents who list at least one market area matching these suburb names (AU/NZ, e.g. Bondi, Toorak, Takapuna) or ZIP codes (US, e.g. 33126). Case-insensitive exact match on one comma-separated part of the market string. A rare suburb can cost hundreds of fetches per row kept. Leave empty for every suburb.

## `minRating` (type: `string`):

Keep only agents at or above this RateMyAgent star rating. Rating is present on 100% of profiles, but an agent with no reviews carries 0.0 — pair this with Minimum reviews if you want genuinely reviewed agents.

## `minReviewCount` (type: `integer`):

Keep only agents with at least this many client reviews. Review count is on 100% of profiles; many agents sit at 0, so this is the filter that separates active, marketed agents from the long tail.

## `minSoldCount` (type: `integer`):

Keep only agents whose RateMyAgent sales record shows at least this many completed sales. Read from a second API call, so it costs one extra request per agent tested. Sold count is present on ~97% of agents.

## `withEmailOnly` (type: `boolean`):

Keep only agents with an email address on file. Measured 99.1% fill across 1,366 agents (100% on samples up to 149, 98.8% on a 1,000-agent run), so this filter discards very little — it is here as a guarantee, not as a narrowing tool.

## `withPhoneOnly` (type: `boolean`):

Keep only agents with a phone number on file. Also measured 99.3% fill across 1,366 agents (AU 98%, NZ 100%, US 100%).

## `claimedOnly` (type: `boolean`):

Keep only agents who have claimed their RateMyAgent profile (they logged in and took ownership of it). Claimed agents are far likelier to answer an outbound email.

## `includeRecentSales` (type: `boolean`):

Adds activeListingCount, completedSaleCount, recentSoldPriceMin/Max and a recentListings array (up to 10 recent listings, each with status Active/Sold, address, suburb, price text, parsed price, beds/baths/cars and result date). Costs one extra API call per agent. Turn OFF for a lean, faster contact-only export.

## `includeBio` (type: `boolean`):

Adds the agent's self-written bio (present on 59% of agents — AU 62%, NZ 80%, US 35%). Turn OFF for a much smaller dataset.

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

Parallel agents in flight. 8 is the tested default. Lower it to 3-4 if you see a lot of 'unreachable after retries' warnings; raising it above 10 does not make RateMyAgent faster.

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

Leave this alone unless you know why you are changing it. Re-measured 2026-08-16, single-shot: a datacenter exit answered 18/20 (90%) and a residential exit matched to the country 20/20 (100%). One dropped call in ten is a missing agent, so this Actor defaults to Apify RESIDENTIAL with the exit country set per region (AU/NZ/US).

## Actor input object example

```json
{
  "regions": [
    "AU"
  ],
  "maxItems": 50,
  "skip": 0,
  "minRating": "0",
  "minReviewCount": 0,
  "minSoldCount": 0,
  "withEmailOnly": false,
  "withPhoneOnly": false,
  "claimedOnly": false,
  "includeRecentSales": true,
  "includeBio": true,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per RateMyAgent agent: name, job title, agency/office, suburb/state/postcode, star rating, review count, sold and for-sale counts, average and median sale price, property-type specialties, recent sold listings, phone, email, socials, bio and photo.

# 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 = {
    "regions": [
        "AU"
    ],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/ratemyagent-scraper").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 = {
    "regions": ["AU"],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/ratemyagent-scraper").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 '{
  "regions": [
    "AU"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call scrapersdelight/ratemyagent-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/ratemyagent-scraper"
        }
    }
}

```

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/xn0c2xaUSGxvBDUGj/builds/uTDE0guU7uQvovPdO/openapi.json
