# 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
- **Stats:** 14 total users, 6 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Domain.com.au Property Scraper

Pull every listing on **Domain.com.au**, Australia's #2 property portal, with the full field surface across buy, rent, and sold modes.

> ⭐ **Highlights: Property History & Property Insights.** Beyond the standard listing fields, this scraper can enrich every record with **Property History** (the full sold/listed/leased price timeline per unit) and **Property Insights** (AVM value estimates, suburb market trends, and property-level intelligence such as planning overlays, school zoning, nearby childcare, NBN, and council). Together they turn a plain listing into a full valuation-and-history dossier. See [Enriched output](#enriched-output-when-cross-source-toggles-are-on).

### What you get per listing

Example record (shape only, values are illustrative placeholders):

```json
{
  "id": "0000000000",
  "url": "https://www.domain.com.au/1-example-street-suburb-nsw-2000-0000000000",
  "listingType": "buy",
  "displayAddress": "1 Example Street, Sample Suburb NSW 2000",
  "addressDisplayType": "FULL_ADDRESS",
  "suburb": "Sample Suburb", "state": "NSW", "postcode": "2000",
  "latitude": 0.0, "longitude": 0.0,
  "price": "Contact Agent",
  "bedrooms": 2, "bathrooms": 2, "parkingSpaces": 1,
  "landSize": 415, "landSizeUnit": "m²",
  "buildingSize": 95, "buildingSizeUnit": "m²",
  "propertyType": "Apartment Unit Flat",
  "headline": "Example listing headline.",
  "description": "Example multi-paragraph description.",
  "structuredFeatures": ["Air Conditioning", "Built-In Wardrobes", "Pool", "Security Access"],
  "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",
  "agencyId": "QWdlbmN5OjAwMDAw",
  "agencyProfileUrl": "https://www.domain.com.au/agencies/00000/",
  "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://rimh2.domainstatic.com.au/.../contact_000000.JPG",
    "profileUrl": "https://www.domain.com.au/real-estate-agent/0000000/"
  }],
  "tags": ["apartment_unit_flat", "new"]
}
```

35+ fields populated per listing, including:

- Agent name, phone, mobile, email, headshot URL
- Agency name, ID, profile URL
- Full address (street, suburb, state, postcode) + lat/lng
- Beds, baths, parking, land size
- Property type
- Headline and full description (multi-paragraph)
- Structured features list (typically 15 to 20 per listing)
- Status (NEW / UNDER\_OFFER / SOLD / LEASED)
- First-listed date, sold/leased dates where applicable
- Hero image URL
- Optional cross-source enrichment for property insights and extended listing fields when `includePropertyInsights` or `includeExtendedListing` is enabled.

### Two modes (input)

#### Search mode

Filter by suburb plus listing type.

```jsonc
{
  "mode": "search",
  "listingType": "buy",
  "locations": ["sydney-nsw-2000", "melbourne-vic-3000"],
  "minBedrooms": 2,
  "maxListings": 100,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AU" }
}
```

#### URL mode

Paste Domain listing URLs, search URLs, or a mix.

**Open-for-inspection URLs are supported.** Paste a suburb URL whose path ends in `/inspection-times/` (e.g. `…/rent/<suburb-slug>/inspection-times/`) to scrape **only** listings with a scheduled inspection in the next 7 days. Each result's `inspection` field carries the full schedule (start/end time, day of week). Pagination walks the whole filtered set automatically.

```jsonc
{
  "mode": "url",
  "urls": [
    "https://www.domain.com.au/level-12-303-castlereagh-street-sydney-nsw-2000-2013554678",
    "https://www.domain.com.au/1-alfred-street-sydney-nsw-2000-2019522695"
  ],
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AU" }
}
```

### Proxy requirement

Apify Residential proxy, country AU or NZ. Residential exits are required for reliable reachability and for the optional cross-source enrichment paths.

### Input fields

- `mode` is `search` or `url`
- `listingType` is `buy` (default), `rent`, or `sold`
- `locations` is a list of `suburb-state-postcode` slugs (search mode); accepts lowercase hyphens
- `urls` is a list of Domain listing URLs (URL mode)
- `propertyTypes` (optional filter; example `["apartment-unit-flat", "house"]`)
- `minBedrooms`, `maxBedrooms`, `minBathrooms`, `minPrice`, `maxPrice` (optional filters)
- `excludeUnderOffer` drops listings with status UNDER\_OFFER
- `inspectionsOnly` (search mode) returns only listings with a scheduled inspection in the next 7 days; each result gets an `inspection` schedule. In URL mode, paste an `/inspection-times/` URL instead.
- `auctionsOnly` (search mode) returns only listings going to auction in the next 7 days. Best paired with `listingType: "buy"`. Domain has no auction URL to paste, so this toggle is the only way to filter auctions.
- `maxListings` is the hard output cap (default 20; set to 0 for no limit)
- `maxPages` is a coarse output cap, ~30 listings per page (default 0 = no limit; `maxListings` wins when both are set)
- `resumeFromRunId` (optional) continues a large pull across separate runs: paste a previous run id (or its dataset id) and this run skips listings it already saved, appending only new ones
- `fetchPropertyHistory` adds the sold/leased price-history timeline (needs residential AU)
- `includePropertyInsights` adds suburb-level insights (median, days on market, demographics)
- `includeExtendedListing` adds the cross-source indoor/outdoor feature split
- `mcpConnectors` (optional) pipes results into your apps — see below
- `notionParentPageUrl` (optional, Notion only) the page to create listing pages under
- `maxNotifyListings` (optional) caps how many listings are written per connector (default 50)

### 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 record — not the full JSON. Each item becomes one entry with a **title** (the listing's name / address) and its key fields flattened to plain text (price, beds/baths, agent, URL, …). Nested objects are collapsed to their main value (e.g. an address object → its full-address text) and long lists are trimmed to the first few names. The **complete, full-fidelity record always stays in the Apify dataset** — the connector copy is a readable digest for browsing in your app.

- **Notion** → one page per item (title + a summary body), created under the page you set in `notionParentPageUrl`.
- **Linear / Airtable / other** → one record/issue per item with the same title + fields.

How to enable:

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 — the export only runs when a connector is selected.

### Tags

Each listing carries category tags you can filter or group on:

- Property type (`apartment_unit_flat`, `house`, `townhouse`, ...)
- Status (`new`, `under_offer`, `sold`, `leased`)
- Sale method (`auction` when the price string indicates one)

### Enriched output (when cross-source toggles are on)

The three optional toggles attach extra fields to each listing. Each is independent and can be enabled on its own.

> ⭐ **Highlight: Property History & Property Insights.** These are the two flagship enrichments. **Property History** (`fetchPropertyHistory`) gives you the full sold/listed/leased price timeline per unit, and **Property Insights** (`includePropertyInsights`) adds AVM value estimates, suburb market trends, and property-level intelligence (planning overlays, school zoning, nearby childcare, NBN, council). Together they turn a plain listing into a full valuation-and-history dossier.

#### ⭐ Property History: `fetchPropertyHistory: true`, adds `priceHistory[]`

A timeline of past sale, listing, and lease events for the SPECIFIC unit (apartment listings need a unit number like `12/45 …` in the address to match; bare-building addresses are skipped to avoid returning aggregate building data).

```json
"priceHistory": [
  { "event": "listed_for_sale",  "year": 2026, "price": "Auction Guide $1,000,000", "date": "1 Jan 2026", "agent": "Example Real Estate", "details": "Listed 1 Jan 2026 by Example Real Estate" },
  { "event": "sold",             "year": 2018, "price": "$870,000",                  "date": "1 Jan 2018", "agent": "Example Real Estate", "details": "Sold 1 Jan 2018 by Example Real Estate" },
  { "event": "listed_for_sale",  "year": 2018, "price": "$880,000",                  "date": "1 Dec 2017" },
  { "event": "sold",             "year": 2012, "price": "$640,000",                  "date": "1 Jan 2012" }
]
```

Typical 0-10 events per unit. Empty when the cross-source index has no record for this specific unit.

#### ⭐ Property Insights: `includePropertyInsights: true`, adds `valueEstimates`, `marketInsights`, `pcaInsights`

Three separate blocks for AVM valuation, suburb market trends, and property-level intelligence:

```json
"valueEstimates": {
  "sale":   { "value": 1050000, "display": "$1.05M",   "confidence": "MEDIUM", "range_min": 950000,  "range_max": 1150000 },
  "rental": { "value":    900,  "display": "$900/wk",  "confidence": "MEDIUM", "range_min":    800,  "range_max":   1000 }
},

"marketInsights": {
  "median_price":      "$1,200,000",
  "price_growth_12mo": "+5.2%",
  "median_rent":       "$950",
  "rent_growth_12mo":  "+8.1%"
},

"pcaInsights": {
  "planningOverlays": { "bushfire": false, "flood": false, "heritage": true },
  "council":          "Sample Council",
  "landDescription":  "Lot 1 in Strata Plan 0000",
  "internet":         { "connectionType": "FTTP", "quality": "Excellent", "description": "NBN Fibre to the Premises" },
  "schoolZoning":     [
    { "name": "Sample Public School",     "isZoned": true, "phase": "primary"   },
    { "name": "Sample Secondary College", "isZoned": true, "phase": "secondary" }
  ],
  "nearbyChildcare":  [
    { "name": "Sample Early Learning Centre", "address": "1 Example St, Suburb, NSW 2000" }
  ],
  "builtYear":      2005,
  "mobileCoverage": "5G",
  "mediaCounts":    { "photos": 18, "floorPlans": 1, "videos": 0 }
}
```

#### `includeExtendedListing: true`, adds `extendedListing`

Indoor/outdoor feature split and a handful of listing-card fields Domain itself doesn't expose:

```json
"extendedListing": {
  "indoorFeatures":             ["Air conditioning", "Built-in robes", "Dishwasher", "Floorboards", "Gas heating"],
  "outdoorFeatures":            ["Balcony / Deck", "Secure parking", "Shed"],
  "studies":                    1,
  "buildingSize":               95,
  "buildingSizeUnit":           "m²",
  // note: buildingSize / buildingSizeUnit are also returned at the top level
  // of every record now, so this block only fills them in as a fallback.
  "priceFrom":                  950000,
  "priceTo":                    1050000,
  "hasFloorplan":               true,
  "hasVideo":                   false,
  "has3DTour":                  true,
  "hasStatementOfInformation":  false,
  "constructionStatus":         "existing",
  "dateUpdated":                "2026-01-15T09:00:00",
  "auctionDate":                "2026-02-15T11:00:00",
  "soldPrice":                  null,
  "daysOnMarket":               14,
  "bond":                       null,
  "availableDate":              null
}
```

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

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

Only used when mode = search. One search runs per location. Accepts 'Suburb State Postcode' or a Domain slug, e.g. 'Sydney NSW 2000', 'Bondi Beach NSW 2026', 'richmond-vic-3121'.

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

buy = for sale, rent = for rent, sold = sold listings.

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

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

Optional. Leave empty for all. One type is applied as a path filter; multiple are combined.

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

Optional minimum bedrooms.

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

Optional maximum bedrooms.

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

Optional minimum bathrooms.

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

Optional minimum price. For rent this is the weekly rent.

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

Optional maximum price. For rent this is the weekly rent.

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

When on, hides listings marked under offer / under contract.

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

## `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. Filter fields above are ignored. Pagination starts at the page in the URL and walks forward.

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

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

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

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

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

## `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",
  "locations": [
    "Sydney NSW 2000"
  ],
  "listingType": "buy",
  "sortBy": "default",
  "propertyTypes": [],
  "excludeUnderOffer": false,
  "inspectionsOnly": false,
  "auctionsOnly": false,
  "fetchPropertyHistory": true,
  "includePropertyInsights": false,
  "includeExtendedListing": false,
  "maxListings": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AU"
  },
  "maxPages": 0,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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",
    "locations": [
        "Sydney NSW 2000"
    ],
    "listingType": "buy",
    "sortBy": "default",
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "AU"
    }
};

// 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",
    "locations": ["Sydney NSW 2000"],
    "listingType": "buy",
    "sortBy": "default",
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "AU",
    },
}

# 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("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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",
  "locations": [
    "Sydney NSW 2000"
  ],
  "listingType": "buy",
  "sortBy": "default",
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AU"
  }
}' |
apify call abotapi/domain-com-au-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/domain-com-au-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/KxbgqfxgAgMD4ndqz/builds/9wF4fE01ThIgrOrsD/openapi.json
