# REIWA Scraper - WA Property, Agents & Market Data (`abotapi/reiwa-com-au-scraper`) Actor

Scrape REIWA.com.au across sale, rental, sold, commercial, business and rural properties. Search with filters or URLs and extract structured listings, agents, agencies and market insights across Western Australia.

- **URL**: https://apify.com/abotapi/reiwa-com-au-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 6 total users, 2 monthly users, 96.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

## REIWA Scraper: WA Property Listings, Agents and Market Insights

REIWA Scraper turns REIWA.com.au (Real Estate Institute of Western Australia) into structured data. Collect for sale, for rent, sold, commercial, business and rural listings, agent and agency profiles, and market insight articles across Western Australia. Search by section, suburb, price and bedroom filters, or paste REIWA URLs directly. Turn on detail enrichment for full descriptions and extra photos, then export to JSON, CSV or Excel, pull the dataset through the API, or pipe results into apps you already use.

### Why This Scraper?

- **Nine REIWA sections, one actor.** For sale, for rent, sold, find agents, commercial for sale, commercial for lease, business, rural and insights, all through the same input.
- **Two ways in.** Build a search from a section, suburbs or regions, price and bedroom filters, or paste REIWA result pages, single listings, agent or agency profiles, and insight articles.
- **Agent and agency contact detail.** Agent names, profile links, agency names, phone numbers where visible, and profile performance figures such as listings sold and median days on market.
- **Optional detail enrichment.** Turn on Fetch details to add full descriptions, extra photos, page metadata and richer visible contact fields, billed only for the rows it successfully enriches.
- **Full-catalogue pagination.** Leave Max pages empty and the run keeps walking result pages until the site's own last-page signal, relying on Max items as the cap.
- **Filters that work on both modes.** Keywords, property type, price and bedroom filters run as a post-fetch step on search-mode and URL-mode rows alike, though a row with no numeric price or bedroom count is always kept.
- **Resume and recurring updates.** Point a new run at a previous run's ID (or dataset ID) to skip rows already collected, or turn on Incremental mode so each scheduled run returns only new and changed rows, stamped with `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`.

### Use Cases

- **Buyers and renters:** track new listings in chosen suburbs, filtered by price range and bedroom count.
- **Lead generation:** build contact lists of agents and agencies active in a region, with phone numbers and profile links.
- **Market research:** pair sold listings with insight articles to study price trends across WA.
- **Portfolio tracking:** monitor commercial, business and rural listings for acquisition or leasing opportunities.
- **CRM and workflow automation:** pipe new rows into Notion, Linear or Airtable through MCP connectors as they're collected.

### Data You Get

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

| Field | Example |
| --- | --- |
| `id` | `"5551234"` |
| `type` | `"for-sale"` (also `"for-rent"`, `"sold"`, `"commercial"`, `"business"`, `"rural"`, `"agent"`, `"agency"`, `"insight"`) |
| `title` | `"12 Example Street, Sampleford"` |
| `address` | `"12 Example Street, Sampleford"` |
| `suburb` | `"Sampleford"` |
| `url` | `"https://reiwa.com.au/12-example-street-sampleford-5551234/"` |
| `sourceUrl` | `"https://reiwa.com.au/for-sale/perth~region/"` |
| `price` | `"$650,000"` |
| `priceValue` | `650000` |
| `bedrooms` / `bathrooms` / `parkingSpaces` | `4` / `2` / `2` |
| `propertyType` | `"House"` |
| `agencyName` / `agencyUrl` | `"Sample Realty Group"` / `"https://reiwa.com.au/real-estate-agency/sample-realty-group/"` |
| `agentNames` / `agentUrls` | `["Jordan Sample"]` / `["https://reiwa.com.au/real-estate-agent/jordan-sample/"]` |
| `phoneNumbers` | `["0491570156"]` |
| `dateText` | `"Added 4 days ago"` |
| `imageUrls` | `["https://imagecdn.reiwa.com.au/listing/34/5551234-01.jpg"]` |
| `description` | listing description text (Fetch details on, or a pasted single listing URL; usually a short summary, not the full listing text) |
| `pageTitle` | the listing page's `<title>` text (Fetch details on, or a pasted single listing URL) |
| `latitude`, `longitude` | listing coordinates (Fetch details on, or a pasted single listing URL) |
| `postcode`, `listingStatus`, `landAreaSqm`, `soldDate` | postcode, status label such as Sold, land size in square metres, `YYYY-MM-DD` sold date (same condition, when the listing shows them) |
| `features` | listing feature lines such as `Garages: 2` (same condition) |
| `detailStatus` | `"ok"` or `"skipped"` (when Fetch details is on) |
| `scrapedAt` | `"2026-01-01T00:00:00.000Z"` |

Agent and agency profile rows (the find-agents section, or pasted agent and agency URLs) carry the same `title`, `url`, `agencyName` and `phoneNumbers` shape plus `profileStats`, an object of visible profile figures such as listings sold, median days on market and median sale price. Insight rows (the insights section) use the article URL as `id` and carry `title`, `url`, `dateText` and a short `summary` instead of price or agent fields.

**Output views.** The dataset has five views: **Results** (every kind), **Overview** (compact listing columns), **Listings (detail)** (coordinates, postcode, status, land area, sold date, features), **Agents & agencies** and **Insights & articles**. The `type` field tells you the kind of each row. A run summary is saved to the key-value store as `OUTPUT` (rows saved, rows per type, detail-enriched count, rows per search).

### How to Use

1. Pick a **mode**: Search by filters or Scrape from URLs.
2. For search mode, choose a **REIWA section** and one or more **suburbs or regions**, then narrow with price, bedroom and property type filters.
3. Turn on **Fetch details** if you want full descriptions, extra photos and richer contact fields (billed per row it succeeds on).
4. Set **Max items** (and optionally **Max pages**), then click **Start**.

**Sale listings in Perth with a price and bedroom filter:**

```json
{
  "mode": "search",
  "channel": "for-sale",
  "locations": ["perth~region"],
  "minPrice": 500000,
  "maxPrice": 750000,
  "minBedrooms": 3,
  "maxItems": 30
}
```

**Rental listings with details enabled:**

```json
{
  "mode": "search",
  "channel": "for-rent",
  "locations": ["baldivis"],
  "minPrice": 450,
  "maxPrice": 650,
  "fetchDetails": true,
  "maxItems": 20
}
```

**Find agents in a region:**

```json
{
  "mode": "search",
  "channel": "agents",
  "locations": ["south-west~region"],
  "maxItems": 25
}
```

**Paste REIWA search URLs (multiple pages of the same search):**

```json
{
  "mode": "url",
  "urls": [
    "https://reiwa.com.au/for-sale/perth~region/",
    "https://reiwa.com.au/for-sale/perth~region/?page=2"
  ],
  "maxItems": 15
}
```

**Supported URL shapes (URL mode):**

| Page | Example path |
|---|---|
| For sale search (region, suburb, property type, filtered or sorted, paginated) | `/for-sale/perth~region/`, `/for-sale/baldivis/houses/`, `/for-sale/baldivis/?minPrice=400000&bedrooms=3&sortBy=price-asc`, `?page=2` |
| Rentals | `/rental-properties/perth/` (a `/rent/<location>/` link is mapped to this) |
| Sold (including `?page=2`) | `/sold/baldivis/` |
| Commercial for sale / for lease | `/commercial/for-sale/perth/`, `/commercial/for-lease/perth/` |
| Business for sale | `/business/for-sale/perth/` |
| Rural | `/rural/for-sale/south-west~region/` |
| Agents directory | `/real-estate-agents/baldivis/` |
| Agent profile | `/real-estate-agent/<name>-<id>/` |
| Agency profile | `/real-estate-agency/<name>-<id>/` |
| Listing detail | `/<street>-<suburb>-<id>/` (typed from the page: sale, rent or sold) |
| Insights list / article | `/the-wa-market/resources/?types=news&page=1`, `/news/<slug>/` |

Filters you put in the URL (price, bedrooms, sort) are applied by the site and are never overwritten by the run-level filters. `www.` and `m.` hosts are normalised to `reiwa.com.au`, tracking parameters are dropped, and a bare numeric id such as `/5049038/` or a non-REIWA link is skipped with a warning in the log.

**Default URLs (behaviour change):** the **URLs** field now defaults to one example of every page type above (16 URLs, covering all 18 URL shapes in the table). A URL-mode run started with that field untouched scrapes all of them, up to **Max items**, and the dataset items it produces are billed as usual. Replace or clear the list to scrape only your own pages.

**Plain place names in search mode:** locations such as `Perth`, `South West` or `Baldivis` are checked against the site (region first, then suburb) and the run stops with a clear error if no match is found. Slugs like `south-west~region` are used as typed.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/reiwa-com-au-scraper").call(run_input={
    "mode": "search",
    "channel": "for-sale",
    "locations": ["perth~region"],
    "maxItems": 20,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["title"], row.get("price"))
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/reiwa-com-au-scraper').call({
  mode: 'search',
  channel: 'for-sale',
  locations: ['perth~region'],
  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.

#### Result filters apply to both modes

Keywords, property types, min/max price and min bedrooms run as a filter on the rows a page returns, whether that page came from a built search or a pasted URL. Locations and sort order only affect how a search-mode URL is built, so they have no effect in URL mode. Property type filters have no effect on the rural section.

#### Full pages and Resume

Leave **Max pages** empty (or `0`) to walk every result page for a search or URL; the run stops on the site's own short or empty last page, or once **Max items** is reached. **Resume** (`resumeFromRunId`) seeds the run's row-tracking set from a previous run or dataset before scraping starts: already-collected leading pages are walked through rather than ending the search early, and already-collected rows on them are skipped; once the walk reaches a page with no new rows at all, that search stops as usual.

#### Incremental mode for scheduled runs

Turn on **Incremental mode** (`incrementalMode`) and the run remembers what it delivered for the same search scope (mode, section, locations or URLs, filters, sort and Fetch details; caps such as Max items never change the scope). On the next run every row is classified: **NEW** (first time seen), **UPDATED** (a compared field changed, with `changedFields` naming it), **UNCHANGED** (not pushed unless you turn on `emitUnchanged`), **REAPPEARED** (was tombstoned, now back) or **EXPIRED** (the previous run delivered it and a complete scan no longer returns it). UPDATED compares every returned field except `scrapedAt`, the relative `dateText`, `detailStatus`, `imageUrls`, `sourceUrl` and anything you list under **Ignore fields**; list order is ignored. Set `changeScope` to `priceAndStock` to compare only price, price value, listing status and sold date. EXPIRED means the search no longer returned the row; it does not prove the property was sold or withdrawn. EXPIRED rows are only produced when the scan ran to the end: not capped by Max items or Max pages, not resumed, and not started at a later page. Rows that are not pushed (unchanged ones) are not billed, and a detail-enrichment charge applies only to full rows pushed with `detailStatus: "ok"`; compact UPDATED rows and EXPIRED tombstones carry no detail charge. Incremental mode off returns the full result set exactly as before.

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

Optionally pipe the scraped results 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 row, not the full JSON. Each row 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 items 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 | `search` | `search` builds REIWA pages from the filters below; `url` scrapes the REIWA links you paste. |
| `channel` | string | `for-sale` | REIWA section for search mode: for sale, for rent, sold, find agents, commercial for sale, commercial for lease, business, rural or insights. URL mode detects the section from each URL. |
| `locations` | array | `["perth~region"]` | Suburbs or regions such as perth, perth~region, baldivis, or south-west~region. One search runs per location; leave empty for all available results where the section supports it. The find-agents section uses plain names, so perth~region is searched as perth there. |
| `sortBy` | string | `default` | Ordering for result pages that support sorting. |
| `urls` | array | 16 example URLs, one per supported page type (see the URL mode section) | Only used when `mode` is `url`. Paste search or list pages, single listings, agent or agency profiles, or insight articles. Multiple URLs are supported. |
| `keywords` | string | `""` (empty) | Optional text matched against returned titles, addresses, agencies, and article summaries, plus a pasted single listing page's own description; Fetch-details-enriched descriptions are not searched. Runs as a post-fetch filter on both search and URL modes. |
| `propertyTypes` | array | `[]` (none) | Optional listing type filters (houses, apartments, units, villas, townhouses, land, rural). Applies to search and URL modes when supported by the source; has no effect on the rural section. |
| `minPrice` | integer | none | Optional minimum price. For rentals this is weekly rent. |
| `maxPrice` | integer | none | Optional maximum price. For rentals this is weekly rent. |
| `minBedrooms` | integer | none | Optional minimum bedrooms. |
| `fetchDetails` | boolean | `false` | Adds full descriptions, extra photos, page metadata and richer contact or profile fields per row. Billed as a detail-enrichment surcharge for each row it succeeds on. |
| `maxItems` | integer | `20` | Hard cap on rows saved across the whole run. |
| `maxPages` | integer | none (prefill: `0`) | Optional bound on result pages walked per search or URL. `0` or empty walks every result page, relying on Max items as the cap. |
| `resumeFromRunId` | string | none | Paste a previous run ID (or dataset ID) from this actor. Rows already collected there are skipped, so this run only returns new ones. |
| `incrementalMode` | boolean | `false` | Remember the rows delivered for this search scope and push only NEW, UPDATED, REAPPEARED and EXPIRED rows on later runs. Off returns the full result set as before. |
| `stateKey` | string | none | Optional name for the incremental baseline. Empty keys the baseline on a hash of the search scope automatically. |
| `emitUnchanged` | boolean | `false` | With Incremental mode on, also push rows that did not change (`changeType: "UNCHANGED"`). |
| `emitExpired` | boolean | `true` | With Incremental mode on, push an EXPIRED tombstone for rows the previous run delivered and this complete scan no longer returns. |
| `changeScope` | string | `all` | `all` compares every returned field; `priceAndStock` compares only price, price value, listing status and sold date. |
| `compactUpdates` | boolean | `false` | UPDATED rows carry only id, title, link, current price fields and `priceChanges` instead of the full record. |
| `expireAfterMissedScans` | integer | `1` | Tombstone a row only after it was missing from this many consecutive complete scans. |
| `ignoreFieldsForChanges` | array | `[]` | Fields that never count as a change (top-level names or dotted paths). Defaults already ignore `scrapedAt`, `dateText`, `detailStatus`, `imageUrls` and `sourceUrl`. |
| `proxy` | object | Apify Proxy enabled | Connection settings. Keep Apify Proxy enabled for stable runs; use a country setting only if you need a specific geography. |
| `mcpConnectors` | array | none | Optional: send a summary of each row into apps you already use, via MCP connectors authorized under Apify Integrations. |
| `notionParentPageUrl` | string | none | Notion connector only: page under which item pages are created. Ignored by other connectors. |
| `maxNotifyListings` | integer | `50` | Cap on rows written to each selected connector per run. Does not affect the dataset. |

### Output Example

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

```json
{
  "id": "5551234",
  "type": "for-sale",
  "title": "12 Example Street, Sampleford",
  "address": "12 Example Street, Sampleford",
  "suburb": "Sampleford",
  "url": "https://reiwa.com.au/12-example-street-sampleford-5551234/",
  "sourceUrl": "https://reiwa.com.au/for-sale/perth~region/",
  "price": "$650,000",
  "priceValue": 650000,
  "bedrooms": 4,
  "bathrooms": 2,
  "parkingSpaces": 2,
  "propertyType": "House",
  "agencyName": "Sample Realty Group",
  "agencyUrl": "https://reiwa.com.au/real-estate-agency/sample-realty-group/",
  "agentNames": ["Jordan Sample"],
  "agentUrls": ["https://reiwa.com.au/real-estate-agent/jordan-sample/"],
  "phoneNumbers": ["0491570156"],
  "dateText": "Added 4 days ago",
  "imageUrls": ["https://imagecdn.reiwa.com.au/listing/34/5551234-01.jpg"],
  "description": "Sample listing description text goes here.",
  "pageTitle": "12 Example Street, Sampleford - For Sale | REIWA",
  "detailStatus": "ok",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan Requirement

The default proxy setting works out of the box on any plan that includes Apify Proxy: REIWA pages load through the standard datacenter connection, so no residential proxy is needed. Each page request has a 15 second attempt limit and up to 5 attempts, and each page has a 25 second overall limit. When the selected connection is unavailable the run switches to a backup connection and logs: "Served over the backup connection (the default connection was unavailable)." If it does, a warning explains that "Apify free plans do not include residential proxies" and that some pages may still be unavailable.

When nothing can be loaded the run fails with a plain message instead of returning an empty dataset:

- **Proxy refused:** "The proxy connection was refused for this account, so no page could be read." A paid plan is required, or supply valid proxy URLs of your own.
- **Backup also failed:** "No results: residential proxies are unavailable on this run ... and the backup connection could not load the requested REIWA pages."
- **Connection problem:** "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."

### FAQ

#### How much does it cost?

You pay per row saved, plus a one-time run start fee. When you turn Fetch details on, the **detail-enrichment** event is billed once per row whose detail page was fetched and merged successfully (rows marked `detailStatus: "skipped"` are never billed). It is charged once, when the run finishes cleanly, for the total number of enriched rows. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run. Single pages you paste (a listing, an agency or agent profile, an insight article) are already their own detail page, so they are never fetched again and never billed detail-enrichment; the same goes for agent, agency and article rows found on list pages.

#### Is it legal to scrape REIWA?

This actor collects only publicly available real estate data. You are responsible for how you use it: follow REIWA's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution, particularly for agent or agency contact details.

#### Do keyword and price filters work in URL mode too?

Yes. Keywords, property types, price and bedroom filters run on the rows returned by a page regardless of how that page was reached, so they apply to a pasted URL the same way they apply to a built search.

#### Can I get only new listings on a recurring schedule?

Yes. Turn on **Incremental mode**: the first run returns everything as NEW, later runs on the same scope return only NEW, UPDATED, REAPPEARED and EXPIRED rows (see the incremental section above). **Resume** (`resumeFromRunId`) is different: it skips rows collected in one earlier run and is meant for continuing an interrupted run.

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

An empty dataset can mean the search or filters matched nothing on the find-agents or insights sections, or, for a listing search, that the section returned a single page-level row rather than any listings at all. A page that does not exist (a mistyped pasted URL included) is skipped with a "Page not found" warning, and a search whose page can't be loaded is skipped with a "Could not load" warning, so the other searches in the same run still finish. A search whose first page returns no results logs a warning, and a run that saves nothing says so in the log. The run fails only when no page could be loaded at all ("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."). Rows already saved before a failure stay in the dataset and are billed for. Check the run log, then run it again in a few minutes.

#### 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/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><td>🏠 <a href="https://apify.com/abotapi/domain-com-au-scraper"><b>Domain.com.au</b></a><br>Extract enriched Domain.com.au property listings across buy, rent, and sold, with...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/view-com-au-scraper"><b>View.com.au Scraper</b></a><br>Scrape View.com.au properties for sale, rent and recently sold across Australia. Search...</td><td>🏠 <a href="https://apify.com/abotapi/au-property-agents-scraper"><b>Real Estate AU Agents Scraper</b></a><br>Scrape Australian real estate agent and agency profiles by suburb or profile URLs...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/realcommercial-au-scraper"><b>Commercial Property AU Scraper</b></a><br>Scrape Australian commercial property listings with detailed property data. Extract...</td><td>🏠 <a href="https://apify.com/abotapi/591-com-tw-scraper"><b>591.com.tw Scraper</b></a><br>Extract property listings from 591房屋交易網 591.com.tw, Taiwan’s largest real estate...</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/reiwa-com-au-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`):

search builds REIWA result pages from the fields below. url uses the REIWA URLs you paste.

## `channel` (type: `string`):

Choose the result type for search mode. URL mode detects the section from each URL.

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

Search locations such as perth, perth~region, baldivis, or south-west~region. One search runs per location. Leave empty for all available results where the selected section supports it. The find-agents section uses plain names, so perth~region is searched as perth there.

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

Ordering for result pages that support sorting.

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

Only used when mode = url. Paste search/list pages (for sale, rent, sold, commercial, business, rural), agent directories, agent and agency profiles, individual listings, insight lists or insight articles. Multiple URLs are supported. Leave the list as is to scrape one example of each page type.

## `keywords` (type: `string`):

Optional text matched against returned titles, addresses, agencies, descriptions, or article summaries. Applies to search and URL modes.

## `propertyTypes` (type: `array`):

Optional listing type filters for listing sections. Applies to search and URL modes when supported by the source URL; has no effect when the section is rural.

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

Optional minimum price. For rentals this is weekly rent.

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

Optional maximum price. For rentals this is weekly rent.

## `minBedrooms` (type: `integer`):

Optional minimum bedrooms.

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

Adds full descriptions, extra photos, page metadata, and richer contact/profile fields when available. Billed as a detail-enrichment surcharge.

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

Hard cap across the whole run. The actor stops once this many rows are saved.

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

Optional bound on result pages walked per search/URL. Leave empty to walk every result page. Use Max items as the primary cap.

## `proxy` (type: `object`):

Keep Apify Proxy enabled for stable runs. Use a country setting only if you need a specific geography.

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

Optional: paste a previous run ID (or dataset ID) from this actor. Rows already collected there are skipped, so this run only returns new ones. Leave empty for a normal run. For recurring diff runs (new/changed only on a schedule) you don't need this - turn on Incremental mode below instead.

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

ON: the actor remembers the rows it delivered for this exact search scope (keyed automatically, or use State key below) and on each scheduled run classifies every row as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED - pushing only new and changed rows plus EXPIRED tombstones, and stamping each row with changeType, changedFields, firstSeenAt and lastSeenAt. UPDATED is decided by comparing the whole record: price, price value, bedrooms, bathrooms, parking, agency, agents, phone numbers, description, status and every other returned field. It does NOT compare scrapedAt, the relative date label (dateText), the detail status, the photo list, the result page it was found on (sourceUrl), or any field you list under Ignore fields. Use Change scope to compare only price and status. OFF (default): every run returns the full result set like before.

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

Name for the incremental baseline when Incremental mode is ON. Leave empty to key the baseline on a hash of the search scope automatically (recommended). Use the same key across runs to compare like with like; a different key starts a new baseline.

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

With Incremental mode ON, also push rows that did not change (stamped changeType=UNCHANGED). Default OFF - unchanged rows are only remembered, not pushed.

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

With Incremental mode ON, push a tombstone row (changeType=EXPIRED, last known field values) for rows from the previous run that this run's search no longer returns - only when the search ran to completion (not capped by Max items or Max pages, not resumed, not started at a later page). Default ON.

## `changeScope` (type: `string`):

With Incremental mode ON. 'all' (default): any change in the compared fields makes a row UPDATED. 'priceAndStock': only price, price value, listing status and sold date changes make a row UPDATED; descriptions, agents, photos and the other fields never do.

## `compactUpdates` (type: `boolean`):

With Incremental mode ON, UPDATED rows carry only id, title, link, current price fields and priceChanges {field: {old, new}} instead of the full record (no detail payload, so no detail charge rides on a compact row). NEW rows stay full. Default OFF.

## `expireAfterMissedScans` (type: `integer`):

With Incremental mode and Push EXPIRED tombstones ON: a row is tombstoned only after it was missing from this many CONSECUTIVE complete scans (default 1 = the first complete scan that misses it). EXPIRED means the search no longer returned the row; it does not prove the property was sold or withdrawn, since a result-page reshuffle and a withdrawal look the same without a detail probe.

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

With Incremental mode ON, dataset fields listed here never count as a change (they are excluded from changeType/changedFields while staying in the output). Top-level names (e.g. agentNames) or dotted paths (e.g. profileStats.listings). Built-in defaults already cover the scrape timestamp, the relative date label (dateText), the detail status, the photo list (imageUrls) and the result page link (sourceUrl).

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

Send the scraped results into apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify Settings, then select it here. The complete record stays in the dataset. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

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

URL or id of the Notion page under which item pages are created. Required for Notion export and ignored by other connectors.

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

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

## Actor input object example

```json
{
  "mode": "search",
  "channel": "for-sale",
  "locations": [
    "perth~region"
  ],
  "sortBy": "default",
  "urls": [
    "https://reiwa.com.au/for-sale/perth~region/",
    "https://reiwa.com.au/for-sale/perth~region/?page=2",
    "https://reiwa.com.au/for-sale/baldivis/",
    "https://reiwa.com.au/for-sale/baldivis/houses/",
    "https://reiwa.com.au/for-sale/baldivis/?minPrice=400000&maxPrice=700000&bedrooms=3&sortBy=price-asc",
    "https://reiwa.com.au/rental-properties/perth/",
    "https://reiwa.com.au/sold/baldivis/",
    "https://reiwa.com.au/sold/baldivis/?page=2",
    "https://reiwa.com.au/commercial/for-sale/perth/",
    "https://reiwa.com.au/commercial/for-lease/perth/",
    "https://reiwa.com.au/business/for-sale/perth/",
    "https://reiwa.com.au/rural/for-sale/south-west~region/",
    "https://reiwa.com.au/real-estate-agents/baldivis/",
    "https://reiwa.com.au/real-estate-agent/adam-dineley-13546-0-567762/",
    "https://reiwa.com.au/real-estate-agency/opal-realty-14506-0/",
    "https://reiwa.com.au/1-canter-lane-baldivis-5049038/",
    "https://reiwa.com.au/the-wa-market/resources/?types=news&page=1",
    "https://reiwa.com.au/news/perth-vacancy-rate-drops-to-lowest-level-in-nearly-two-years/"
  ],
  "keywords": "",
  "propertyTypes": [],
  "fetchDetails": false,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": true,
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `listings` (type: `string`):

No description

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

No description

## `insights` (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": "search",
    "channel": "for-sale",
    "locations": [
        "perth~region"
    ],
    "urls": [
        "https://reiwa.com.au/for-sale/perth~region/",
        "https://reiwa.com.au/for-sale/perth~region/?page=2",
        "https://reiwa.com.au/for-sale/baldivis/",
        "https://reiwa.com.au/for-sale/baldivis/houses/",
        "https://reiwa.com.au/for-sale/baldivis/?minPrice=400000&maxPrice=700000&bedrooms=3&sortBy=price-asc",
        "https://reiwa.com.au/rental-properties/perth/",
        "https://reiwa.com.au/sold/baldivis/",
        "https://reiwa.com.au/sold/baldivis/?page=2",
        "https://reiwa.com.au/commercial/for-sale/perth/",
        "https://reiwa.com.au/commercial/for-lease/perth/",
        "https://reiwa.com.au/business/for-sale/perth/",
        "https://reiwa.com.au/rural/for-sale/south-west~region/",
        "https://reiwa.com.au/real-estate-agents/baldivis/",
        "https://reiwa.com.au/real-estate-agent/adam-dineley-13546-0-567762/",
        "https://reiwa.com.au/real-estate-agency/opal-realty-14506-0/",
        "https://reiwa.com.au/1-canter-lane-baldivis-5049038/",
        "https://reiwa.com.au/the-wa-market/resources/?types=news&page=1",
        "https://reiwa.com.au/news/perth-vacancy-rate-drops-to-lowest-level-in-nearly-two-years/"
    ],
    "maxItems": 20,
    "maxPages": 0,
    "proxy": {
        "useApifyProxy": true
    },
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/reiwa-com-au-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": "search",
    "channel": "for-sale",
    "locations": ["perth~region"],
    "urls": [
        "https://reiwa.com.au/for-sale/perth~region/",
        "https://reiwa.com.au/for-sale/perth~region/?page=2",
        "https://reiwa.com.au/for-sale/baldivis/",
        "https://reiwa.com.au/for-sale/baldivis/houses/",
        "https://reiwa.com.au/for-sale/baldivis/?minPrice=400000&maxPrice=700000&bedrooms=3&sortBy=price-asc",
        "https://reiwa.com.au/rental-properties/perth/",
        "https://reiwa.com.au/sold/baldivis/",
        "https://reiwa.com.au/sold/baldivis/?page=2",
        "https://reiwa.com.au/commercial/for-sale/perth/",
        "https://reiwa.com.au/commercial/for-lease/perth/",
        "https://reiwa.com.au/business/for-sale/perth/",
        "https://reiwa.com.au/rural/for-sale/south-west~region/",
        "https://reiwa.com.au/real-estate-agents/baldivis/",
        "https://reiwa.com.au/real-estate-agent/adam-dineley-13546-0-567762/",
        "https://reiwa.com.au/real-estate-agency/opal-realty-14506-0/",
        "https://reiwa.com.au/1-canter-lane-baldivis-5049038/",
        "https://reiwa.com.au/the-wa-market/resources/?types=news&page=1",
        "https://reiwa.com.au/news/perth-vacancy-rate-drops-to-lowest-level-in-nearly-two-years/",
    ],
    "maxItems": 20,
    "maxPages": 0,
    "proxy": { "useApifyProxy": True },
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/reiwa-com-au-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": "search",
  "channel": "for-sale",
  "locations": [
    "perth~region"
  ],
  "urls": [
    "https://reiwa.com.au/for-sale/perth~region/",
    "https://reiwa.com.au/for-sale/perth~region/?page=2",
    "https://reiwa.com.au/for-sale/baldivis/",
    "https://reiwa.com.au/for-sale/baldivis/houses/",
    "https://reiwa.com.au/for-sale/baldivis/?minPrice=400000&maxPrice=700000&bedrooms=3&sortBy=price-asc",
    "https://reiwa.com.au/rental-properties/perth/",
    "https://reiwa.com.au/sold/baldivis/",
    "https://reiwa.com.au/sold/baldivis/?page=2",
    "https://reiwa.com.au/commercial/for-sale/perth/",
    "https://reiwa.com.au/commercial/for-lease/perth/",
    "https://reiwa.com.au/business/for-sale/perth/",
    "https://reiwa.com.au/rural/for-sale/south-west~region/",
    "https://reiwa.com.au/real-estate-agents/baldivis/",
    "https://reiwa.com.au/real-estate-agent/adam-dineley-13546-0-567762/",
    "https://reiwa.com.au/real-estate-agency/opal-realty-14506-0/",
    "https://reiwa.com.au/1-canter-lane-baldivis-5049038/",
    "https://reiwa.com.au/the-wa-market/resources/?types=news&page=1",
    "https://reiwa.com.au/news/perth-vacancy-rate-drops-to-lowest-level-in-nearly-two-years/"
  ],
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/reiwa-com-au-scraper --silent --output-dataset

```

## MCP server setup

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