# Real Estate AU Agents Scraper - Profiles, Reviews & Stats (`abotapi/au-property-agents-scraper`) Actor

Scrape Australian real estate agent and agency profiles by suburb or profile URLs. Returns names, roles, agencies, performance stats, median sold price, days advertised, properties sold, ratings, full reviews, and contact details.

- **URL**: https://apify.com/abotapi/au-property-agents-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 24 total users, 12 monthly users, 85.2% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 record scrapeds

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

## realestate.com.au Agent & Agency Scraper: Profiles, Performance Stats & Reviews

This scraper turns realestate.com.au into a structured directory of Australian property agents and agencies: who they are, where they work, how they perform, and what their clients say. Search by suburb and state, or paste profile and search links you already opened in a browser, then export every record as JSON, CSV or Excel, or read it straight through the API.

### Why This Scraper?

- **Two record kinds, one actor.** Individual agent profiles and whole agency profiles (with the full team roster), each returned in a consistent, flat shape ready for a CRM or lead list.
- **Performance you can rank on.** Median sold price, median days advertised, and properties sold as lead agent or secondary agent.
- **Reputation built in.** Average rating, total review count, an at-a-glance summary, and the full list of individual reviews (rating, text, reviewer role, date, and the property each review refers to).
- **Contact details where published.** Agent mobile and office numbers, agency phone, postal address, and the agency's own website.
- **Two ways to find data.** Search by suburb and state, or paste profile and search links you already opened in a browser, in the same run.
- **Resilient by design.** A request that gets refused is retried automatically on alternate connections; the run only fails outright once every option has been exhausted, so a quiet suburb is never confused with a run that could not connect.
- **Optional one-click export.** Send records, up to a per-run cap, into Notion, Linear, Airtable, or Apify through MCP connectors, without changing what lands in the dataset.

### Use Cases

- **Real estate CRMs and lead lists:** pull agent and agency contact details, ratings, and coverage areas into your own database.
- **Agent and agency benchmarking:** compare median sold price, days advertised, and properties sold across a suburb or region.
- **Reputation monitoring:** track review volume, ratings, and sentiment for a shortlist of agents or agencies over time.
- **Recruiting and vendor selection:** shortlist top-performing agents or agencies before an appraisal or a hiring decision.
- **Market research and directories:** build a suburb-by-suburb map of who is active, with a full team roster for each agency.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `recordType` | `"agent"` (also `"agency"`) |
| `agentId` | `"00000001"` |
| `name` / `firstName` | `"Jane Doe"` / `"Jane"` |
| `jobTitle` | `"Sales Executive"` |
| `yearsExperience` / `startYearInIndustry` | `12` / `2012` |
| `description` | full biography text (fetched with profile details) |
| `mostActiveLocation` | `{ "suburb": "Sample Suburb", "state": "NSW" }` |
| `rating.average` / `rating.totalReviews` | `5` / `82` |
| `reviewSummary` | `"Consistently praised for clear communication and strong results."` |
| `stats.medianSoldPrice.overall` | `875000` |
| `stats.medianDaysAdvertised.overall` | `43` |
| `stats.propertiesSold.asLeadAgent` / `.asSecondaryAgent` | `34` / `41` |
| `phone.mobile` / `phone.office` | `"0400000000"` / `"0200000000"` |
| `specialities` | `["Residential Sales", "First Home Buyers"]` |
| `awards` | list of named awards, when published |
| `agencyId` / `agency.name` | `"AAA000"` / `"Sample Agency"` |
| `reviews[0].rating` / `.text` / `.reviewerRole` / `.publishDate` | `5` / `"Prompt, clear, and helpful throughout the sale."` / `"seller"` / `"2026-01-01"` |
| `profileImageUrl` | agent photo URL |
| `url` | `"https://www.realestate.com.au/agent/jane-doe-00000001"` |
| `source` | `"realestate.com.au"` |
| `scrapedAt` | `"2026-01-01T00:00:00.000Z"` |

Agency records (`recordType: "agency"`) carry `agencyId`, `name`, a structured `address` (street, suburb, state, postcode), `phone.office`, `website`, `rating.average`/`rating.totalReviews`, a `performance` object, the full team roster in `agents[]` (each with `agentId`, `name`, `jobTitle`, `profileUrl`, `profileImageUrl`, and `rating`), `agentCount`, `social` links, `logoUrl`, `coverPhotoUrl`, `primaryColor`, the `isCommercial`/`isResidential` flags, and separate seller and buyer review lists under `reviews.seller[]` / `reviews.buyer[]`.

### How to Use

1. Pick a **mode**: `location` (suburb + state) or `url` (paste profile or search links you already opened in a browser).
2. For location mode, choose **Search for** (agent or agency) and list one or more suburb/state combinations. For URL mode, paste profile and/or search links instead; the location fields are ignored.
3. Set **Maximum records** to control run size and cost (default 20, hard ceiling 1,000,000; 0 means no limit up to that ceiling), and use **Fetch full profile details** and **Maximum reviews per record** to control how much detail each record carries.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search for agents in a suburb:**

```json
{
  "mode": "location",
  "searchType": "agent",
  "locations": [{ "suburb": "Sydney CBD", "state": "NSW" }],
  "fetchDetails": true,
  "maxItems": 20
}
```

**Search for agencies in a suburb:**

```json
{
  "mode": "location",
  "searchType": "agency",
  "locations": [{ "suburb": "Melbourne", "state": "VIC" }],
  "maxItems": 20
}
```

**Scrape specific profiles by URL:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.realestate.com.au/agent/jane-doe-00000001",
    "https://www.realestate.com.au/agency/sample-agency-AAA000"
  ]
}
```

**Continue a previous run without duplicates:**

```json
{
  "mode": "location",
  "searchType": "agent",
  "locations": [{ "suburb": "Sydney CBD", "state": "NSW" }],
  "maxItems": 50,
  "resumeFromRunId": "aBcDeFgHiJkLmNoPq"
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/au-property-agents-scraper").call(run_input={
    "mode": "location",
    "searchType": "agent",
    "locations": [{"suburb": "Sydney CBD", "state": "NSW"}],
    "maxItems": 20,
})
for record in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(record["name"], record.get("agency", {}).get("name"))
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/au-property-agents-scraper').call({
  mode: 'location',
  searchType: 'agent',
  locations: [{ suburb: 'Sydney CBD', state: 'NSW' }],
  maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### How `maxItems` and `maxPages` shape a run

Location mode walks search result pages in order (roughly 24 agents or 12 agencies per page) until it reaches **Maximum records**, runs out of new results, or hits the site's own last page. **Maximum search result pages** adds an optional ceiling on how many pages are walked per location; leave it empty and **Maximum records** stays the only real limit. Once a search has found new records, a later page made up entirely of already-known records ends that search early. Leading pages already covered by a resumed run are walked through rather than treated as the end, so a resume seed does not cut a search short.

#### URL mode

Supported URL shapes (the `urls` default and prefill hold one example of each, so URL mode with the field untouched scrapes one example per shape instead of failing):

| Shape | Example |
| --- | --- |
| Agent search (location) | `https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000` |
| Agency search (location) | `https://www.realestate.com.au/find-agency/sydney-cbd-nsw-2000/` |
| Paginated search (`?page=N` sets the starting page) | `https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?page=2` |
| Search without surrounding suburbs | `https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?includeSurrounding=false` |
| Agent profile | `https://www.realestate.com.au/agent/bryn-fowler-132261` |
| Agency profile | `https://www.realestate.com.au/agency/sydney-cove-property-the-rocks-PUHSYD` |

Any other URL shape is skipped with a warning that names the URL (`Skipping unsupported URL ...`). A search that matches no suburb logs `No agents found for ... (nothing matched this search)` or `No agencies found for ...` instead of returning silently, and a run with no usable locations or URLs stops with `No work items`.

#### Reviews

Reviews come with the full profile (`fetchDetails` on for agents; agencies always). **Maximum reviews per record** (`maxReviews`, default 50) caps how many reviews are kept on each record; `0` omits them. Agent records carry a `reviews[]` list; agency records carry separate `reviews.seller[]` and `reviews.buyer[]` lists.

#### Output views

The dataset has these views (Output tab and `?view=` on the API): **Results** (all records), **Overview** (compact), **Agents** (performance, specialities, awards; profiles that list awards or specialities as free text fill `awardsText` / `specialitiesText`; `recentListings` holds the agent's recent sold listings with address, type, beds/baths/parking, price and sold date, and `lastSoldDate` the most recent of those dates: search results fill them from the agent's search card (up to 5 listings), and with profile details on (or a pasted agent profile URL) they come from the agent's profile page (up to 10 listings); with profile details on, agent records also carry `currentListings`, the agent's current counts as the site shows them (`forSale`, `forRent`, `sold` and `total` = for sale + for rent), and `currentForSaleListings`, up to 3 of their current for-sale listings. The current-listing fields come only from the profile page, so they are left out when profile details are off or the page does not show them), **Agencies** (address, team size, performance) and **Reviews** (reviews per record) and **Changes (incremental)** (change type per record, incremental mode only). Each run also saves a small `OUTPUT` summary (records pushed, by record type, detail-enriched count, whether the cap was reached).

#### Resume

Paste a previous run ID or dataset ID from this actor into `resumeFromRunId` to skip every agent or agency already collected there; the run then returns only new records and does not re-charge for them. Resume is a one-time delta that continues ONE specific earlier run; for a schedule that should report what changed, use Incremental mode below instead. Leave it empty for a normal run.

#### Incremental mode (recurring monitoring)

Turn on **Incremental changes for scheduled runs** (`incrementalMode`) and schedule the actor against the same search. The first run returns every matching agent or agency as `NEW`. Each later run is compared with the saved state for that search and returns only what changed: `NEW` (first seen), `UPDATED` (a tracked field changed; `changedFields` names it, e.g. `rating`, `phone`, `reviews`), and `REAPPEARED` (came back after being gone). Every returned row also carries `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`; the **Changes (incremental)** view lists them.

- **Unchanged records** are not returned and not billed (no record event and no profile-enrichment event). Turn on **Emit unchanged records** (`emitUnchanged`) to get them marked `UNCHANGED` (billed like any returned record).
- **Expired records** (in the previous run, gone now) are returned as `EXPIRED` only if you turn on **Emit expired records** (`emitExpired`), and only after a run that scanned the whole search: not when **Maximum records** or **Maximum search result pages** capped the run, when a pasted URL starts after page 1, when a saved checkpoint continued the run, or when Resume was used. Expired rows are billed like any returned record.
- **What counts as a change:** every output field except `scrapedAt`, `yearsExperience` (it rises on the calendar), and the numbered image host. Add more with **Ignore fields when detecting changes** (`ignoreFieldsForChanges`, dotted names such as `rating.totalReviews`); ignored fields stay in the output, they are just not compared.
- **State** is kept per search (mode, search type, locations or URLs, surrounding suburbs, Fetch full profile details, Maximum reviews per record), so different searches never mix and each review cap has its own baseline (the reviews list is compared, so changing the cap would otherwise flag most records as UPDATED). Set **State key** (`stateKey`) to name a campaign or deliberately share one; a named key is not split by the review cap. Changing **Maximum records** or the proxy does not start a new baseline.
- **Maximum records and unchanged records:** unchanged records do not count toward the cap, so every profile on a page is still opened and compared; the run stops once the cap of returned records is reached, and a run stopped by the cap never produces EXPIRED rows.
- An agent whose profile could not be loaded in an incremental run is skipped for that run (not returned, not compared, not billed) and retried next time.
- Incremental mode and **Resume from run ID** do not combine on a search that already has saved state; the run fails with a message telling you to remove one of them.
- With `fetchDetails` on, every agent's profile is still opened to check for changes, so an incremental run saves rows and charges, not requests.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped records into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset.**

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where records should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `location` | `location` (suburb + state) or `url` (paste links). |
| `searchType` | string | `agent` | `agent` or `agency`. Location mode only. |
| `locations` | array | none (prefill: Sydney CBD, NSW) | Suburb, state, and postcode per entry; all three are optional individually. |
| `includeSurrounding` | boolean | `true` | Include agents or agencies from surrounding suburbs. |
| `urls` | array | one example of each supported URL shape (see URL mode) | Profile or search URLs (URL mode). |
| `fetchDetails` | boolean | `true` | Open each agent's profile for contact details, full stats, and reviews. Agencies always include full details. |
| `maxReviews` | integer | `50` | Cap on reviews per record. `0` omits reviews. |
| `maxItems` | integer | `20` | Stop after this many records. `0` for unlimited. |
| `maxPages` | integer | none (walks every result page) | Optional bound on search result pages per location; `maxItems` is the real limit. |
| `proxyConfiguration` | object | none (prefill: Apify Proxy, RESIDENTIAL group, country AU) | Connection settings for this run. |
| `resumeFromRunId` | string | none | Paste a previous run ID or dataset ID from this actor to skip already-collected agents/agencies. |
| `incrementalMode` | boolean | `false` | Scheduled monitoring: return only NEW / UPDATED / REAPPEARED records versus the previous run of the same search. |
| `stateKey` | string | none (derived from the search) | Name a monitoring campaign, or deliberately share state across differently-configured runs. |
| `emitUnchanged` | boolean | `false` | Also return unchanged records, marked UNCHANGED (billed). |
| `emitExpired` | boolean | `false` | Also return records that disappeared, marked EXPIRED, after a fully scanned search (billed). |
| `ignoreFieldsForChanges` | array | `[]` | Extra field names (dotted) that must not count as a change. |
| `mcpConnectors` | array | none | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | none | Notion connector only: page under which records are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "recordType": "agent",
  "agentId": "00000001",
  "name": "Jane Doe",
  "firstName": "Jane",
  "jobTitle": "Sales Executive",
  "yearsExperience": 12,
  "startYearInIndustry": 2012,
  "description": "Full agent biography text appears here when full profile details are fetched.",
  "mostActiveLocation": { "suburb": "Sample Suburb", "state": "NSW", "postcode": "2000" },
  "rating": { "average": 5, "totalReviews": 82 },
  "reviewSummary": "Consistently praised for clear communication and strong results.",
  "stats": {
    "medianSoldPrice": { "overall": 875000 },
    "medianDaysAdvertised": { "overall": 43 },
    "propertiesSold": { "overall": 75, "asLeadAgent": 34, "asSecondaryAgent": 41 }
  },
  "phone": { "mobile": "0400000000", "office": "0200000000" },
  "specialities": ["Residential Sales", "First Home Buyers"],
  "agencyId": "AAA000",
  "agency": { "id": "AAA000", "name": "Sample Agency", "profileUrl": "https://www.realestate.com.au/agency/sample-agency-AAA000" },
  "reviews": [
    { "rating": 5, "text": "Prompt, clear, and helpful throughout the sale.", "reviewerRole": "seller", "publishDate": "2026-01-01" }
  ],
  "profileImageUrl": "https://www.realestate.com.au/images/agent-00000001.jpg",
  "url": "https://www.realestate.com.au/agent/jane-doe-00000001",
  "source": "realestate.com.au",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan Requirement

The site challenges every automated request, whatever connection it comes from: a plain request through a datacenter, residential or direct connection is refused, so changing the connection tier does not unblock it. The actor solves that challenge itself and reads pages through the Apify Proxy; its default connection setting is Apify Proxy, the RESIDENTIAL group, country AU. **Apify free plans do not include residential proxies.** On a free plan the run logs:

> The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable. For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs.

and carries on with the backup connection. Contact numbers, profile and record links, the source label, the agency website, and images are included only when the run is served over the residential connection; on any other connection those fields are left out of the affected records (the run logs a warning saying so). A run fails instead of finishing empty with one of these messages:

- `The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid.`
- `No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Australian property agent pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again.`
- `Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later.`

A run also fails if a `resumeFromRunId` you supplied could not be read.

### FAQ

#### How much does it cost?

You pay per record returned, plus one profile-enrichment event per record when **Fetch full profile details** is on (see the table below). Use **Maximum records** and **Maximum reviews per record** to cap the cost of any run.

Billing is pay per event, as currently registered on the **Pricing** tab (FREE plan prices; paid plans are lower):

| Event | What is billed | FREE plan price |
| --- | --- | --- |
| Record scraped | once per agent or agency record returned to the dataset | $0.0018 |
| Profile enrichment (`detail-enrichment`) | once per record whose full profile is fetched, charged right after that record is saved, so a run that stops early is only billed for records already delivered; not charged when **Fetch full profile details** is off; in incremental mode, not charged for unchanged records that are not returned | $0.0008 |
| Actor start | once per GB of run memory (minimum one) | $0.06 |

Use **Maximum records** and **Maximum reviews per record** to cap the cost of a run.

#### Is it legal to scrape realestate.com.au?

This actor collects only publicly available agent and agency data. You are responsible for how you use it: follow realestate.com.au's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Names, roles, and performance figures are generally facts, but photos, logos, and review text may be subject to third-party rights.

#### Can I run this repeatedly without getting duplicate agents or agencies?

Yes. Schedule the actor from the **Schedules** tab with **Incremental changes for scheduled runs** turned on: each run returns only the agents and agencies that are new or changed since the last run of the same search, with `changeType` and `changedFields`. To continue one interrupted run instead, paste its ID into `resumeFromRunId`.

#### Why are some fields empty in my results?

Contact numbers, profile links, the agency website, and images are only populated when the run is served over the residential connection (see Plan Requirement). On an Apify free plan, or with a custom connection setting, those fields are left out; use a paid plan with the default setting and re-run.

#### Why did my run fail instead of returning an empty dataset?

If every request is refused across every connection the actor tries, the run stops with a clear message so "no agents in this suburb" is never confused with "nothing could be read." Re-run it in a few minutes. If only some locations or URLs could not be read, you still get what was found for the rest.

#### What is the difference between the two search types?

`searchType: agent` returns individual agent profiles; `searchType: agency` returns whole agency profiles, each with its full team roster in `agents[]`. This choice only applies in location mode; URL mode is classified automatically from the link you paste.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear, or Airtable.

### 🔗 Want more real estate data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏠 <a href="https://apify.com/abotapi/realestate-au-scraper"><b>Real Estate AU</b></a><br>Extract detailed Australian real estate property listings with 30+ structured fields...</td><td>🏠 <a href="https://apify.com/abotapi/realcommercial-au-scraper"><b>Commercial Property AU Scraper</b></a><br>Scrape Australian commercial property listings with full detail data. Extract...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/realestate-co-nz-scraper"><b>RealestateCo NZ</b></a><br>From $1/1K. Search and extract all real estate properties for sale, rent, and sold across...</td><td>🏠 <a href="https://apify.com/abotapi/homely-com-au-scraper"><b>Homely.com.au</b></a><br>Scrape Homely.com.au properties for sale, rent, and sold, plus agent finder, suburb...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/reiwa-com-au-scraper"><b>REIWA Scraper</b></a><br>Scrape REIWA.com.au across sale, rental, sold, commercial, business and rural properties...</td><td>🚗 <a href="https://apify.com/abotapi/dubizzle-com-scraper"><b>Dubizzle.com Scraper</b></a><br>From $1/1K. Scrape dubizzle.com across the UAE for property, motors, and agent listings...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/au-property-agents-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Location mode = pick suburb + state below and choose agents or agencies. URL mode = paste profile or search URLs you already opened in a browser.

## `searchType` (type: `string`):

Whether to return individual agents or whole agencies. Location mode only.

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

Locations to search. Suburb, state, and postcode are all optional - provide whichever you have.

## `includeSurrounding` (type: `boolean`):

Include agents or agencies from surrounding suburbs in the results.

## `urls` (type: `array`):

Paste agent profile URLs, agency profile URLs, or location search URLs. Search URLs paginate forward; the page number in the URL sets the starting page. In URL mode, the location fields above are ignored.

## `fetchDetails` (type: `boolean`):

Open each agent's profile to add contact details, full performance stats, and the complete reviews list. Off = faster, returns the summary fields available on the search results card only. Agencies always include full profile details. Adds about 1s per agent.

## `maxReviews` (type: `integer`):

Cap on the number of reviews attached to each agent or agency. Set to 0 to omit reviews.

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

Stop after scraping this many agents or agencies. Set to 0 for no limit (hard ceiling 1,000,000 records).

## `maxPages` (type: `integer`):

Optional bound on search result pages processed per location (each page has roughly 24 agents, or 12 agencies). Leave empty to walk every result page — the record cap above is the real limit.

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

Configure the proxy used for this run. The default works for most runs; some optional fields require the default selection.

## `resumeFromRunId` (type: `string`):

Optional: paste a previous run ID (or dataset ID) from this actor to continue a large pull. Agents/agencies already collected there are skipped, so this run only returns new ones. Use it after an interrupted run. For recurring daily monitoring of the same search, use Incremental mode below instead. Leave empty for a normal run.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns every matching agent or agency as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED records. A record is UPDATED when a tracked field changed (name, job title, agency, phone, address, website, rating, review count, stats, performance, reviews, specialities, awards, description, social links, images); changedFields names which. Never compared, but still in the output: scrapedAt, yearsExperience and the numbered image host. Add more with Ignore fields below. Turn on "Emit unchanged" or "Emit expired" only when you also want those records returned (and billed). State is kept separately for each location/URL, Fetch-full-profile and Maximum-reviews setup; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from run ID above.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search mode, locations or URLs, and Fetch-full-profile setting, so different searches never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return agents and agencies that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return agents and agencies that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search - not when Maximum records or Maximum search result pages capped it, when a pasted URL starts after page 1, or when Resume was used. This returns, and bills, extra rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names that must NOT count as a change in incremental mode, so a record is not flagged UPDATED (and billed) because of them. Nested fields use dots, e.g. rating.totalReviews or agency.logoUrl; a name inside a list applies to every item. The fields stay in the output, they are just not compared. Already ignored by default: scrapedAt, yearsExperience.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. Notion gets a page per item; other connectors receive a best-effort write/digest. Leave empty to skip — this never changes the dataset output.

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "location",
  "searchType": "agent",
  "locations": [
    {
      "suburb": "Sydney CBD",
      "state": "NSW"
    }
  ],
  "includeSurrounding": true,
  "urls": [
    "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000",
    "https://www.realestate.com.au/find-agency/sydney-cbd-nsw-2000/",
    "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?page=2",
    "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?includeSurrounding=false",
    "https://www.realestate.com.au/agent/bryn-fowler-132261",
    "https://www.realestate.com.au/agency/sydney-cove-property-the-rocks-PUHSYD"
  ],
  "fetchDetails": true,
  "maxReviews": 50,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AU"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `agents` (type: `string`):

No description

## `agencies` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `summary` (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 = {
    "mode": "location",
    "locations": [
        {
            "suburb": "Sydney CBD",
            "state": "NSW"
        }
    ],
    "urls": [
        "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000",
        "https://www.realestate.com.au/find-agency/sydney-cbd-nsw-2000/",
        "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?page=2",
        "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?includeSurrounding=false",
        "https://www.realestate.com.au/agent/bryn-fowler-132261",
        "https://www.realestate.com.au/agency/sydney-cove-property-the-rocks-PUHSYD"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "AU"
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/au-property-agents-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 = {
    "mode": "location",
    "locations": [{
            "suburb": "Sydney CBD",
            "state": "NSW",
        }],
    "urls": [
        "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000",
        "https://www.realestate.com.au/find-agency/sydney-cbd-nsw-2000/",
        "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?page=2",
        "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?includeSurrounding=false",
        "https://www.realestate.com.au/agent/bryn-fowler-132261",
        "https://www.realestate.com.au/agency/sydney-cove-property-the-rocks-PUHSYD",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "AU",
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/au-property-agents-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 '{
  "mode": "location",
  "locations": [
    {
      "suburb": "Sydney CBD",
      "state": "NSW"
    }
  ],
  "urls": [
    "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000",
    "https://www.realestate.com.au/find-agency/sydney-cbd-nsw-2000/",
    "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?page=2",
    "https://www.realestate.com.au/find-agent/sydney-cbd-nsw-2000?includeSurrounding=false",
    "https://www.realestate.com.au/agent/bryn-fowler-132261",
    "https://www.realestate.com.au/agency/sydney-cove-property-the-rocks-PUHSYD"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AU"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/au-property-agents-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/au-property-agents-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/gvbz6lK77QpgvHtAV/builds/z85iszeMrKhRt3Gpk/openapi.json
