# Domain.com.au - Buy, Rent, Sold & Price History (`abotapi/domain-com-au-scraper`) Actor

Extract enriched Domain.com.au property listings across buy, rent, and sold, with AI-enhanced content and deep structured data including descriptions, features, photos, GPS, agent contacts, suburb insights, school catchments, and complete sold and leased price history timelines.

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

## Pricing

from $1.50 / 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

## Domain.com.au Scraper: Property Listings, History and Insights

Domain.com.au Scraper turns Domain.com.au into a clean property data feed. Search by suburb and filters, or paste Domain search and listing URLs, to pull buy, rent and sold listings with full agent and agency details. Turn on optional add-ons for a property's price history timeline, a valuation and suburb insights, or extra indoor and outdoor listing attributes, then export to JSON, CSV or Excel, or read the results straight through the API.

### Why This Scraper?

- **Two ways to search.** Filter by suburb, listing type and property criteria, or paste Domain search or listing URLs directly.
- **Buy, rent and sold listings from one input,** each with full agent and agency contact details, not just a listing summary.
- **Optional price history timeline** for a property's past sold, listed and leased events.
- **Optional property insights:** a sale and rental valuation estimate, suburb market trends, planning overlays, NBN connection type, school zoning and nearby childcare.
- **Optional extended listing attributes:** the indoor and outdoor feature split and a few listing-card fields Domain itself doesn't publish.
- **Open for inspection and auction filters** surface time-sensitive listings coming up in the next 7 days.
- **Built for schedules.** Incremental mode returns only new and changed listings on recurring runs, and Resume continues a large pull that got interrupted.

### Use Cases

- **Buyers and renters:** track new listings and price changes in a suburb without refreshing Domain by hand.
- **Real estate agencies and PropTech:** build a lead or comparables database with full agent, agency and pricing detail.
- **Investors and valuers:** pull price history and valuation estimates to build a suburb level model.
- **Market researchers and media:** track inventory, days on market and price trends across suburbs over time.
- **Portfolio and compliance monitoring:** use incremental mode to catch relisted, repriced or delisted properties on a schedule.

### Data You Get

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

| Field | Example |
| --- | --- |
| `id` | `"0000000000"` |
| `url` | Domain listing link |
| `listingType` | `"buy"` (also `"rent"`, `"sold"`) |
| `displayAddress` / `addressDisplayType` | `"1 Example Street, Sample Suburb NSW 2000"` / `"FULL_ADDRESS"` |
| `suburb` / `state` / `postcode` | `"Sample Suburb"` / `"NSW"` / `"2000"` |
| `latitude` / `longitude` | `-33.8688` / `151.2093` |
| `price` / `priceGuide` | `"Contact Agent"` |
| `bedrooms` / `bathrooms` / `parkingSpaces` | `2` / `2` / `1` |
| `landSize` / `landSizeUnit` | `415` / `"m2"` |
| `buildingSize` / `buildingSizeUnit` | `95` / `"m2"` |
| `propertyType` | `"Apartment Unit Flat"` |
| `headline` | `"Example listing headline."` |
| `description` | `"Example multi paragraph description."` |
| `structuredFeatures` | `["Air Conditioning", "Built-In Wardrobes", "Pool"]` |
| `status` / `saleMethod` / `isAuction` | `"NEW"` / `"auction"` / `true` |
| `inspection` | schedule with start, end, time and day of week, or `null` |
| `firstListedDate` | `"2026-01-01T00:00:00"` |
| `agencyName` / `agencyId` / `agencyProfileUrl` / `agencyAddress` / `agencyBrandColour` | `"Example Real Estate"` and its office address |
| `agents` | name, phone, mobile, email, photo, profile URL |
| `images` | the hero image first, then every gallery photo of the listing (photo URLs) |
| `tags` | `["apartment_unit_flat", "new"]` |
| `priceHistory` | sold, listed and leased timeline (when Fetch property price history is on) |
| `valueEstimates` / `marketInsights` / `pcaInsights` | valuation estimate, suburb trends, planning, NBN and school data (when Property insights is on) |
| `extendedListing` | indoor and outdoor feature split and more (when Extended listing attributes is on) |

When incremental mode is on, every record also carries `changeType` (`NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED` or `EXPIRED`), `changedFields` for an `UPDATED` record, and `firstSeenAt` / `lastSeenAt` timestamps. An `EXPIRED` record is the last known snapshot of a listing that is no longer found, not a fresh scrape, so its price and enrichment fields reflect whenever it was last seen rather than the current run.

### How to Use

1. Pick a mode: **search** (filter by suburb and criteria) or **url** (paste Domain search or listing URLs).
2. For search mode, fill in **Locations** and the filters you need. For url mode, paste one or more Domain URLs.
3. Turn on **Fetch property price history**, **Property insights** or **Extended listing attributes** if you want the enrichment fields, and set **Max listings** (or Max pages) to control run size.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search a suburb for houses to buy:**

```json
{
  "mode": "search",
  "locations": ["Sydney NSW 2000"],
  "listingType": "buy",
  "propertyTypes": ["house"],
  "minBedrooms": 2,
  "maxListings": 50
}
```

**Paste search and listing URLs:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.domain.com.au/sale/melbourne-vic-3000/",
    "https://www.domain.com.au/1-example-street-sample-suburb-nsw-2000-0000000000"
  ]
}
```

**Only listings open for inspection this week:**

```json
{
  "mode": "search",
  "locations": ["Bondi Beach NSW 2026"],
  "listingType": "rent",
  "inspectionsOnly": true
}
```

**Sold listings with price history and property insights:**

```json
{
  "mode": "search",
  "locations": ["Richmond VIC 3121"],
  "listingType": "sold",
  "fetchPropertyHistory": true,
  "includePropertyInsights": true,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AU" }
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/domain-com-au-scraper").call(run_input={
    "mode": "search",
    "locations": ["Sydney NSW 2000"],
    "listingType": "buy",
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["displayAddress"], listing["price"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/domain-com-au-scraper').call({
  mode: 'search',
  locations: ['Sydney NSW 2000'],
  listingType: 'buy',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

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

#### How Max listings and Max pages cap a run

**Max listings** is the hard cap on total listings returned (maximum 1,000,000), and `0` means no limit up to the built-in ceiling of 1000 pages per search. **Max pages** is a coarser cap of roughly 30 listings per page; when both are set, Max listings wins. Filters (bedrooms, bathrooms, price, property type, exclude under offer) are applied after a listing is fetched, so a heavily filtered search can return fewer listings than the cap even though more exist. If Min price or Max price is set, a listing with no parseable price (auction, contact agent, by negotiation) cannot be confirmed in range and is excluded rather than kept.

#### Resume vs Incremental mode

These are two different features under the same input section:

- **Resume** (`resumeFromRunId`) continues one specific interrupted run. Paste its run or dataset id and this run skips listings already saved there, so it only appends new ones.
- **Incremental mode** (`incrementalMode`) is for scheduling the same search again and again, for example daily, and getting only what changed. Each listing is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED`, or `EXPIRED` (only after a run that scanned the whole search, and only with `emitExpired` on). `stateKey` names or shares the stored state; leave it blank and the actor derives one from your search scope. Turning both on together is supported once, as a bootstrap: the first run seeds the incremental baseline from the resumed dataset. Those seeded listings come back once more as `UPDATED` with a full `changedFields` list on the next run while their snapshot is recorded, and after that you run with incrementalMode alone on a schedule and only real changes come through.

**What incremental mode compares.** Every listing field is compared (price, price guide, status, sale method, inspection and auction times, bedrooms, bathrooms, parking, land size, description, features, agency, agents, schools and the enrichment blocks), and a change to any of them makes the listing `UPDATED` with the field named in `changedFields`. Excluded by default, but still returned on every record: the scrape timestamp, the suburb-wide market insights block, the whole `images` list (photo URLs on signed image servers rotate between runs, so a changed URL is not a listing change), the listing-card badge text `tagText` (it ages from New to Updated on Domain's side), and the order of the agents list. Add your own with **Ignore fields when detecting changes** (`ignoreFieldsForChanges`, dots for nested fields such as `agents.photo`); changing that list does not trigger a wave of `UPDATED` rows. With Max listings set and the default order, Domain does not return the same top listings on every run, so the ones that rotate in show up as `NEW`; pick a Sort order (for example oldest first) or leave Max listings unlimited for stable monitoring. If an add-on such as price history was unavailable on the first run, the run that first gets it marks those listings `UPDATED` with `priceHistory` in `changedFields`. A run only produces `EXPIRED` rows when it scanned the whole search: a pasted URL that starts past page 1, a capped run, and inspection or auction time searches never do.

#### URL mode shapes

Behaviour change: in url mode, leaving `urls` untouched now scrapes one example of each shape below (previously nothing). Use `maxListings` to cap the run.

| # | Shape | Example | Support |
|---|---|---|---|
| 1 | Sale search | `/sale/melbourne-vic-3000/` | yes |
| 2 | Sale search, paginated | `/sale/melbourne-vic-3000/?page=2` | yes, starts at the pasted page (Domain's 20 listings per page) and continues forward; partial scope, no EXPIRED marking |
| 3 | Rent search | `/rent/sydney-nsw-2000/` | yes |
| 4 | Rent search, paginated | `/rent/melbourne-vic-3000/?page=3` | yes, starts at page 3 (partial scope) |
| 5 | Sold listings | `/sold-listings/richmond-vic-3121/` | yes |
| 6 | Sold listings, sorted | `/sold-listings/melbourne-vic-3000/?sort=solddate-desc` | yes (order set by the Sort input) |
| 7 | Query-form search | `/sale/?suburb=bondi-beach-nsw-2026&ptype=house&bedrooms=2-any&sort=price-asc` | yes (suburb, property types, bedrooms and sort read from the URL) |
| 8 | Multi-suburb query form | `/sale/?suburb=richmond-vic-3121,carlton-vic-3053` | yes, one scope per suburb |
| 9 | Filtered search | `/sale/melbourne-vic-3000/?ptype=...&price=500000-1000000&excludeunderoffer=1` | suburb, property types, bedrooms, bathrooms, price band, exclude-under-offer and sort are read from the URL and applied to that URL only (they win over the matching input fields for it); a value that cannot be read is named in a warning |
| 10 | Sorted search | `/sale/melbourne-vic-3000/?sort=price-asc` | yes, the sort is read from the URL (`price-asc`, `price-desc`, `dateupdated-*`, `datelisted-*`, `suburb-asc`) and orders that URL's results only; other URLs in the same run keep their own order |
| 11 | Inspection times path | `/sale/sydney-nsw-2000/inspection-times/` | yes |
| 12 | Inspection filter query | `/sale/melbourne-vic-3000/?inspectiontimes=1` | yes |
| 13 | Listing detail | `/2405-623-collins-street-melbourne-vic-3000-2020262713` | yes |
| 14 | Numeric id only | `/2020262713` | yes |
| 15 | New homes landing | `/new-homes/` | not supported, logs a warning and skips; if every pasted URL is unsupported the run ends with a message naming them and the supported shapes |
| 16 | Auction results | `/auction-results/melbourne/` | not supported, logs a warning and skips (same end-of-run message when it is the only kind of URL given) |
| 17 | New-home project page | `/project/6781/623-collins-melbourne-vic/` | not supported (project pages have no per-listing feed), logs a warning and skips |
| 18 | Real-estate agents directory | `/real-estate-agents/` | not supported (agent directory, not property listings), logs a warning and skips |

In search mode, locations accept plain suburb names such as `Richmond` or `Bondi Beach`. A plain name is looked up in Domain's own suburb search and the top match is used; when several suburbs share the name (`Richmond` is also in NSW, TAS, SA and QLD) the run uses the top one and logs a warning naming the others, so add the state or postcode (`Richmond NSW 2753`) to choose. A location that matches no suburb is logged as an error and skipped; if none of your locations match, the run fails instead of returning an empty dataset.

#### Output views

The dataset has four views in the Console: **Overview** (address, price, beds, baths, agency), **Full Details** (every field including enrichment blocks), **Changes (incremental)** (`changeType`, `changedFields`, first and last seen) and **Agents (one row per agent)** (one row per listing agent with name, phone, mobile, email and agency). The same records are available as JSON, CSV or Excel. A run also writes a short run summary to the `OUTPUT` record of the key-value store.

Besides the core fields, each record carries listing-card extras such as `listingUrl`, `dateListed`, `soldPrice`, `floorPlanImages`, `videoUrls`, `agencyUrl`, `agencyLogoUrl`, `agentNames`, `nextInspectionOpen` / `nextInspectionClose` and `auctionDate`. All additions are extra fields; no existing field was renamed or removed.

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

Optionally pipe the scraped listings 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 listing, not the full JSON. Each item becomes one entry with a title (the listing's address or headline) and its key fields flattened to plain text: address, price, type, bedrooms and bathrooms, status, agency, agent and a link back to the listing. 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 listing pages 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 the query from the filters below; `url` scrapes pasted Domain URLs. |
| `locations` | array | none (prefill: `["Sydney NSW 2000"]`) | Suburbs to search, one search per location (search mode only). Accepts "Suburb State Postcode" or a Domain slug. |
| `sortBy` | string | `default` | Order results by price or date. Search mode only; does not resort a pasted URL's results. |
| `urls` | array | one example of each supported shape | Domain search or listing URLs to scrape (url mode only). |
| `listingType` | string | `buy` | `buy`, `rent`, or `sold`. Search mode: which listings to search. Any mode: `sold` also narrows a pasted URL's results to sold or leased listings. |
| `propertyTypes` | array | `[]` (all types) | Restrict to one or more property types. Applies in search mode and to a pasted URL. |
| `minBedrooms` | integer | none (no minimum) | Applies in search mode and to a pasted URL. |
| `maxBedrooms` | integer | none (no maximum) | Applies in search mode and to a pasted URL. |
| `minBathrooms` | integer | none (no minimum) | Applies in search mode and to a pasted URL. |
| `minPrice` | integer | none (no minimum) | AUD; weekly rent for rent listings. Applies in search mode and to a pasted URL. |
| `maxPrice` | integer | none (no maximum) | AUD; weekly rent for rent listings. Applies in search mode and to a pasted URL. |
| `excludeUnderOffer` | boolean | `false` | Hide listings marked under offer. |
| `inspectionsOnly` | boolean | `false` | Search mode only. Only listings with a scheduled inspection in the next 7 days. Paste an inspection times URL in url mode instead. |
| `auctionsOnly` | boolean | `false` | Search mode only. Only listings going to auction in the next 7 days. Domain has no auction URL to paste, so this is the only way to filter auctions. |
| `fetchPropertyHistory` | boolean | `true` | Adds the sold, listed and leased price history timeline. Needs Residential proxy, country AU. |
| `includePropertyInsights` | boolean | `false` | Adds a valuation estimate, suburb market trends, planning overlays, NBN, school zoning and more. Needs Residential proxy, country AU. |
| `includeExtendedListing` | boolean | `false` | Adds the indoor and outdoor feature split and a few attributes Domain doesn't publish. Needs Residential proxy, country AU. |
| `maxListings` | integer | `20` | Hard cap on total listings across all searches. `0` = no limit. |
| `maxPages` | integer | `0` (no limit) | Coarse cap, about 30 listings per page. `maxListings` wins when both are set. |
| `proxy` | object | Residential proxy, country AU | Connection settings. Domain.com.au accepts Australian and New Zealand residential connections. |
| `resumeFromRunId` | string | none | Continue one interrupted run: paste its run id or dataset id. |
| `incrementalMode` | boolean | `false` | Return only new and changed listings on scheduled runs. |
| `stateKey` | string | none (auto derived from your search scope) | Name or share an incremental mode monitoring campaign. |
| `ignoreFieldsForChanges` | string\[] | none | Fields (dots for nested, e.g. `agents.photo`) that never count as a change in incremental mode. They are still returned on every record. |
| `emitUnchanged` | boolean | `false` | Also return, and bill, listings unchanged since the last run. |
| `emitExpired` | boolean | `false` | Also return, and bill, listings no longer found, once a run has fully scanned the tracked search. |
| `mcpConnectors` | array | none | Optional: send a summary of each listing to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | none | Notion connector only: page under which listing pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on listings written to each connector per run. |

### Output Example

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

```json
{
  "id": "0000000000",
  "url": "https://www.domain.com.au/1-example-street-sample-suburb-nsw-2000-0000000000",
  "listingType": "buy",
  "displayAddress": "1 Example Street, Sample Suburb NSW 2000",
  "addressDisplayType": "FULL_ADDRESS",
  "street": "1 Example Street",
  "suburb": "Sample Suburb",
  "state": "NSW",
  "postcode": "2000",
  "latitude": -33.8688,
  "longitude": 151.2093,
  "price": "Contact Agent",
  "priceGuide": "Contact Agent",
  "bedrooms": 2,
  "bathrooms": 2,
  "parkingSpaces": 1,
  "landSize": 415,
  "landSizeUnit": "m2",
  "buildingSize": 95,
  "buildingSizeUnit": "m2",
  "propertyType": "Apartment Unit Flat",
  "headline": "Example listing headline.",
  "description": "Example multi paragraph description.",
  "structuredFeatures": ["Air Conditioning", "Built-In Wardrobes", "Pool", "Security Access"],
  "saleMethod": null,
  "isAuction": false,
  "status": "NEW",
  "inspection": {
    "byAppointmentOnly": false,
    "inspections": [
      { "start": "2026-01-03T13:45:00", "end": "2026-01-03T14:00:00", "time": "1:45pm", "dayOfWeek": "Saturday" }
    ]
  },
  "firstListedDate": "2026-01-01T00:00:00",
  "agencyName": "Example Real Estate",
  "agencyProfileUrl": "https://www.domain.com.au/real-estate-agencies/00000/",
  "agencyId": "QWdlbmN5OjAwMDAw",
  "agencyAddress": {
    "street": "Example Street",
    "streetNumber": "10",
    "suburb": "Sample Suburb",
    "state": "NSW",
    "postcode": "2000"
  },
  "agencyBrandColour": "#000000",
  "agents": [
    {
      "id": "QWdlbnQ6MDAwMDAwMA==",
      "name": "Jane Smith",
      "phone": "0400 000 000",
      "mobile": "0400 000 000",
      "email": "jane@example.com.au",
      "photo": "https://example.com/agent-photo.jpg",
      "profileUrl": "https://www.domain.com.au/real-estate-agent/0000000/"
    }
  ],
  "images": ["https://example.com/hero-image.jpg"],
  "tags": ["apartment_unit_flat", "new"],
  "sourceUrl": "https://www.domain.com.au/1-example-street-sample-suburb-nsw-2000-0000000000",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan Requirement

Domain.com.au does not serve plain scripted clients, so the actor opens a browser grade session before it reads any page. That session needs a paid Apify plan with proxy access: the default connection is the Apify Residential proxy with country AU (NZ also works). Fetch property price history, Property insights and Extended listing attributes need the Residential connection as well; without it those options are skipped and a warning is logged.

On an Apify free plan the Residential connection is refused. The run then logs "Apify free plans do not include residential proxies" and continues on a backup connection, which may still not be able to read pages. If it cannot load any page the run fails with a clear message instead of finishing empty:

- Connection refused for the account: "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".
- Free plan, backup could not load pages: "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 Domain pages."
- Any other no-page run: "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."

The Pricing tab on the actor page shows the current rates.

### FAQ

#### How much does it cost?

You pay per listing returned, plus a `detail-enrichment` charge of one unit per listing, billed right after that listing is saved (so a run stopped early has paid only for what it delivered). It applies to a returned listing only when Fetch property price history or Extended listing attributes actually attached data to it; fetchPropertyHistory is on by default, so most default runs include it. Unchanged, suppressed or failed listings are never charged for enrichment. The **Pricing** tab shows the current rates, and **Max listings** caps the cost of any run.

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

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

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Later runs then return only new, updated and reappeared listings, and unchanged ones are not returned or billed unless you turn on **Emit unchanged**.

#### What is the difference between Resume and Incremental mode?

**Resume** (`resumeFromRunId`) continues one specific run that got interrupted, it is a one-off. **Incremental mode** is for running the same search again and again on a schedule and getting only the changes each time; it remembers its own state between runs. See "Resume vs Incremental mode" above for the full detail, including how to bootstrap one from the other.

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

If the actor cannot establish a connection to Domain at the start of the run after several automatic retries, the run fails with a clear error (see Plan Requirement for the exact messages) instead of silently returning nothing. Once connected, a listing or results page that still cannot be read after retries is skipped and logged, so a run can finish with fewer listings than exist; check the run log if the count looks low. A `resumeFromRunId` that doesn't match a run or dataset you can access also fails fast, and turning on **Incremental mode** together with `resumeFromRunId` fails if that state key already has tracked listings from an earlier run, since combining them again would be ambiguous; remove one, or set a different **State key**.

#### 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 listing summaries 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/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/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></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 full detail data. Extract...</td><td>🤖 <a href="https://apify.com/abotapi/property-au-scraper"><b>AU Residential Property</b></a><br>From $0.9/1K. Scrape Australian property listings with rich detail-page enrichment. Each...</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/domain-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 = build the query from filters below. url = paste Domain.com.au search or listing URLs and let the actor walk them.

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

Order results server-side. 'Default' keeps Domain's native order. For sold listings, 'Newest/Oldest' sorts by sale date; for buy/rent it sorts by date listed. Applies in Search mode. Does not sort a pasted URL's results by this key — leave it at Default in URL mode.

## `inspectionsOnly` (type: `boolean`):

When on, return only listings with a scheduled inspection in the next 7 days. In URL mode, paste an inspection-times URL (path ending in /inspection-times/) instead.

## `auctionsOnly` (type: `boolean`):

When on, return only listings going to auction in the next 7 days. Best used with listing type 'For sale'. Domain has no auction URL to paste, so this toggle is the only way to filter auctions.

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

Only used when mode = search. One search runs per location. Accepts a plain suburb name ('Richmond', 'Bondi Beach'), 'Suburb State Postcode' or a Domain slug, e.g. 'Sydney NSW 2000', 'Bondi Beach NSW 2026', 'richmond-vic-3121'. A plain name uses the top match and logs a warning when other suburbs share the name; a name that matches no suburb is skipped with an error. Add the state or postcode to disambiguate same-named suburbs.

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

Only used when mode = url. Paste Domain search URLs (e.g. https://www.domain.com.au/sale/melbourne-vic-3000/) or individual listing URLs. Multi-URL supported. Listing type and the filters above (property types, bedrooms, bathrooms, price, exclude-under-offer) apply to these results; filters written in a pasted search URL (ptype, bedrooms, bathrooms, price, excludeunderoffer, sort) win over the matching input field for that URL only. The inspection/auction toggles do not apply. Pagination starts at the page in the URL and walks forward. Leaving this field untouched scrapes one example of each supported URL shape (use maxListings to cap). new-homes, new-home project, real-estate-agents directory and auction-results pages are not supported and log a warning.

## `listingType` (type: `string`):

buy = for sale, rent = for rent, sold = sold listings. Applies in Search mode (selects which listings to search); also narrows a pasted URL's results to sold/leased listings when set to Sold.

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

Optional. Leave empty for all; selecting more than one keeps a listing that matches any of them. Applies in Search mode and to a pasted URL.

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

Optional minimum bedrooms. Applies in Search mode and to a pasted URL.

## `maxBedrooms` (type: `integer`):

Optional maximum bedrooms. Applies in Search mode and to a pasted URL.

## `minBathrooms` (type: `integer`):

Optional minimum bathrooms. Applies in Search mode and to a pasted URL.

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

Optional minimum price. For rent this is the weekly rent. Applies in Search mode and to a pasted URL.

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

Optional maximum price. For rent this is the weekly rent. Applies in Search mode and to a pasted URL.

## `excludeUnderOffer` (type: `boolean`):

When on, hides listings marked under offer / under contract. Applies in Search mode and to a pasted URL.

## `fetchPropertyHistory` (type: `boolean`):

Also fetch each property's profile page for the full sold/leased price history timeline and rental estimate. Requires Fetch listing details. Adds one extra request per listing.

## `includePropertyInsights` (type: `boolean`):

Attach attributes Domain.com.au does not publish: a sale & rental valuation estimate (AVM with confidence range), suburb market growth (12-month price/rent change), planning overlays (bushfire / flood / heritage), local council, NBN connection type, school zoning, nearby childcare centres, built year, mobile coverage and media counts. Resolved per listing from its address. Off by default; adds a short delay per listing and needs a Residential proxy (country AU).

## `includeExtendedListing` (type: `boolean`):

Attach an extended set of attributes Domain itself does not publish, matched per address from the property's active listing elsewhere on the market: the indoor and outdoor feature split (e.g. Air Conditioning, Built-in Wardrobes, Pool, Courtyard), floor plan / video / 3D-tour availability flags, building size, study count, numeric price range, plus rent/sold/auction details. De-duplicated against the fields already on each listing so nothing is repeated. Off by default; adds extra lookups per suburb and needs a Residential proxy (country AU). Coverage is partial: only properties also listed elsewhere are matched.

## `maxListings` (type: `integer`):

Hard cap on total listings across all searches. Default 20. Set to 0 for no limit (bounded by Max pages, or by the real inventory size when Max pages is also 0). Maximum 1000000.

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

Coarse output cap. Each page is ~30 listings. 0 means no limit (walk every page Domain reports for the search). Use Max listings for a finer cap; if both are set, Max listings wins. A hard limit of 1000 pages per search always applies.

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

Domain.com.au accepts Australian (and New Zealand) residential connections. Keep Residential + country AU (NZ also works).

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

Paste a previous run ID (or its dataset ID) to continue a large pull across separate runs. Listings already saved by that run are skipped, so this run only appends new ones. Leave empty for a normal, fresh run. For recurring monitoring of the same search, use Incremental mode below instead.

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

Turn this on for daily/weekly recurring monitoring of the same search. The first run returns all matching listings as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. What is compared: every listing field, including price, price guide, status, sale method, inspection and auction times, bedrooms/bathrooms/parking/land size, description, features, agency, agents, schools and the enrichment blocks (price history, value estimates, property insights, cross-listing data). Any change to one of these makes the listing UPDATED and names the field in changedFields. What is excluded: the scrape timestamp, the suburb-wide market insights block, the whole images list (photo URLs are signed and rotate between runs, so a changed URL is not a listing change), the listing-card badge text tagText and the order of the agents list; these are still returned on every record. You can exclude more fields with Ignore fields below. Turn on "Emit unchanged" or "Emit expired" only when you also want those listings returned (and billed). State is kept separately per location/URL/listingType/filter/enrichment setup; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run 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 locations/URLs, listing type, filters, and enrichment toggles — different searches then never mix state with each other.

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

Off by default. Turn on to also return listings 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 specifically want the full snapshot every run.

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

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED — e.g. sold, leased, or delisted. Only produced once a run has fully scanned the tracked search — not when Max listings/Max pages capped it, or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

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

Optional. Field names that should NOT count as a change in incremental mode, e.g. description or images. Use dots for nested fields, e.g. agents.photo or inspection.openTime. Ignored fields are still returned on every record and still shown in the output; they only stop a listing being marked UPDATED (and billed) when just they changed. Changing this list never causes a wave of UPDATED rows. Only applies when Incremental mode is on.

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

Optionally send the scraped listings into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → API & Integrations, then select it here. Notion gets a rich page-per-listing export; other connectors receive a best-effort write/digest. Leave empty to skip — this never changes the dataset output. 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 listing pages are created. Required to enable the Notion export; ignored by other connectors.

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

Cap on how many listings are written to each selected connector per run (protects against very large writes). Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "sortBy": "default",
  "inspectionsOnly": false,
  "auctionsOnly": false,
  "locations": [
    "Sydney NSW 2000"
  ],
  "urls": [
    "https://www.domain.com.au/sale/melbourne-vic-3000/",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?page=2",
    "https://www.domain.com.au/rent/sydney-nsw-2000/",
    "https://www.domain.com.au/rent/melbourne-vic-3000/?page=3",
    "https://www.domain.com.au/sold-listings/richmond-vic-3121/",
    "https://www.domain.com.au/sold-listings/melbourne-vic-3000/?sort=solddate-desc",
    "https://www.domain.com.au/sale/?suburb=bondi-beach-nsw-2026&ptype=house&bedrooms=2-any&sort=price-asc",
    "https://www.domain.com.au/sale/?suburb=richmond-vic-3121,carlton-vic-3053",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?ptype=apartment-unit-flat&bedrooms=2-any&price=500000-1000000&excludeunderoffer=1",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?sort=price-asc",
    "https://www.domain.com.au/sale/sydney-nsw-2000/inspection-times/",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?inspectiontimes=1",
    "https://www.domain.com.au/2405-623-collins-street-melbourne-vic-3000-2020262713",
    "https://www.domain.com.au/2020262713",
    "https://www.domain.com.au/new-homes/",
    "https://www.domain.com.au/auction-results/melbourne/",
    "https://www.domain.com.au/project/6781/623-collins-melbourne-vic/",
    "https://www.domain.com.au/real-estate-agents/"
  ],
  "listingType": "buy",
  "propertyTypes": [],
  "excludeUnderOffer": false,
  "fetchPropertyHistory": true,
  "includePropertyInsights": false,
  "includeExtendedListing": false,
  "maxListings": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AU"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `details` (type: `string`):

No description

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

No description

## `agents` (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",
    "sortBy": "default",
    "locations": [
        "Sydney NSW 2000"
    ],
    "urls": [
        "https://www.domain.com.au/sale/melbourne-vic-3000/",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?page=2",
        "https://www.domain.com.au/rent/sydney-nsw-2000/",
        "https://www.domain.com.au/rent/melbourne-vic-3000/?page=3",
        "https://www.domain.com.au/sold-listings/richmond-vic-3121/",
        "https://www.domain.com.au/sold-listings/melbourne-vic-3000/?sort=solddate-desc",
        "https://www.domain.com.au/sale/?suburb=bondi-beach-nsw-2026&ptype=house&bedrooms=2-any&sort=price-asc",
        "https://www.domain.com.au/sale/?suburb=richmond-vic-3121,carlton-vic-3053",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?ptype=apartment-unit-flat&bedrooms=2-any&price=500000-1000000&excludeunderoffer=1",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?sort=price-asc",
        "https://www.domain.com.au/sale/sydney-nsw-2000/inspection-times/",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?inspectiontimes=1",
        "https://www.domain.com.au/2405-623-collins-street-melbourne-vic-3000-2020262713",
        "https://www.domain.com.au/2020262713",
        "https://www.domain.com.au/new-homes/",
        "https://www.domain.com.au/auction-results/melbourne/",
        "https://www.domain.com.au/project/6781/623-collins-melbourne-vic/",
        "https://www.domain.com.au/real-estate-agents/"
    ],
    "listingType": "buy",
    "proxy": {
        "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/domain-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",
    "sortBy": "default",
    "locations": ["Sydney NSW 2000"],
    "urls": [
        "https://www.domain.com.au/sale/melbourne-vic-3000/",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?page=2",
        "https://www.domain.com.au/rent/sydney-nsw-2000/",
        "https://www.domain.com.au/rent/melbourne-vic-3000/?page=3",
        "https://www.domain.com.au/sold-listings/richmond-vic-3121/",
        "https://www.domain.com.au/sold-listings/melbourne-vic-3000/?sort=solddate-desc",
        "https://www.domain.com.au/sale/?suburb=bondi-beach-nsw-2026&ptype=house&bedrooms=2-any&sort=price-asc",
        "https://www.domain.com.au/sale/?suburb=richmond-vic-3121,carlton-vic-3053",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?ptype=apartment-unit-flat&bedrooms=2-any&price=500000-1000000&excludeunderoffer=1",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?sort=price-asc",
        "https://www.domain.com.au/sale/sydney-nsw-2000/inspection-times/",
        "https://www.domain.com.au/sale/melbourne-vic-3000/?inspectiontimes=1",
        "https://www.domain.com.au/2405-623-collins-street-melbourne-vic-3000-2020262713",
        "https://www.domain.com.au/2020262713",
        "https://www.domain.com.au/new-homes/",
        "https://www.domain.com.au/auction-results/melbourne/",
        "https://www.domain.com.au/project/6781/623-collins-melbourne-vic/",
        "https://www.domain.com.au/real-estate-agents/",
    ],
    "listingType": "buy",
    "proxy": {
        "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/domain-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",
  "sortBy": "default",
  "locations": [
    "Sydney NSW 2000"
  ],
  "urls": [
    "https://www.domain.com.au/sale/melbourne-vic-3000/",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?page=2",
    "https://www.domain.com.au/rent/sydney-nsw-2000/",
    "https://www.domain.com.au/rent/melbourne-vic-3000/?page=3",
    "https://www.domain.com.au/sold-listings/richmond-vic-3121/",
    "https://www.domain.com.au/sold-listings/melbourne-vic-3000/?sort=solddate-desc",
    "https://www.domain.com.au/sale/?suburb=bondi-beach-nsw-2026&ptype=house&bedrooms=2-any&sort=price-asc",
    "https://www.domain.com.au/sale/?suburb=richmond-vic-3121,carlton-vic-3053",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?ptype=apartment-unit-flat&bedrooms=2-any&price=500000-1000000&excludeunderoffer=1",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?sort=price-asc",
    "https://www.domain.com.au/sale/sydney-nsw-2000/inspection-times/",
    "https://www.domain.com.au/sale/melbourne-vic-3000/?inspectiontimes=1",
    "https://www.domain.com.au/2405-623-collins-street-melbourne-vic-3000-2020262713",
    "https://www.domain.com.au/2020262713",
    "https://www.domain.com.au/new-homes/",
    "https://www.domain.com.au/auction-results/melbourne/",
    "https://www.domain.com.au/project/6781/623-collins-melbourne-vic/",
    "https://www.domain.com.au/real-estate-agents/"
  ],
  "listingType": "buy",
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AU"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/domain-com-au-scraper --silent --output-dataset

```

## MCP server setup

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