# Domain.com.au Property Scraper (Australia) (`scrapyx/domain-properties-scraper`) Actor

Scrapes houses and apartments for rent or sale from Domain.com.au, Australia's #1 property portal. Search any suburb; returns price, full address, bedrooms, bathrooms, property type and photos from a single search call.

- **URL**: https://apify.com/scrapyx/domain-properties-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 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 (Australia)

Scrapes houses and apartments for rent or sale from
**[Domain.com.au](https://www.domain.com.au)** — Australia's #1 property
portal (larger than realestate.com.au).

Public data only. No login, no cookies, no browser.

### The one thing you need to know before using this

**The search page is behind Akamai Bot Manager**, which serves a disguised
HTTP 200 "behavioral content" challenge page to most TLS fingerprints — it
looks like a normal response by status code alone. Of every profile tried
this session (`chrome124`, `chrome120`, `edge101`, `firefox133`,
`safari17_0`, and three `primp` profiles), only **`chrome99_android`**
returned genuine content. This actor pins to that profile as the pool
leader rather than using the portfolio's usual default, and checks response
bodies for challenge markers before trusting a 200 (same defensive pattern
as every other actor here, just against a target where it actually
triggers).

### What you get

Two record types share one dataset, told apart by `recordType`.

#### `PROPERTY` — one row per listing

Search rows (`listing`) already carry everything: price, full street
address with coordinates, bedrooms, bathrooms, property type and every
photo. No separate detail fetch exists in this actor — none was found to be
necessary this session (search already has it all, same architecture
decision as `bienici-properties-scraper` elsewhere in this portfolio).

#### `SEARCH_SUMMARY` — one row per location searched

Domain's own `totalListings`/`totalPages` plus how many pages this run
actually fetched.

### Input

| Field | What it does |
| --- | --- |
| **Rent / sale** | which side of the market (note: the real path segment for buying is `sale`, not `buy`) |
| **Locations** | Domain slugs: `melbourne-vic-3000`, `bondi-nsw-2026`… |
| **Max properties / max pages per location** | pagination caps |

#### Example

```json
{
  "transactionType": "rent",
  "locations": ["melbourne-vic-3000", "bondi-nsw-2026"],
  "maxItems": 100
}
```

### Notes on reliability

- **Active WAF (Akamai)** — see above. Residential proxy is the default on
  cloud runs on top of the profile pin; a fresh IP does not fix a bad
  fingerprint, but a bad IP can still add a second failure mode.
- **Unknown locations fail cleanly** as `not_found` — a clean HTTP 404,
  never a silently-wrong nationwide baseline.
- **A page past the end is a clean HTTP 400** (`page=999` on a 50-page
  search, verified live), reported as `past_page_range` — not a silent
  empty/wrong page.
- **`buy` 404s** — the correct path segment is `sale`; confirmed live so
  the actor doesn't offer the wrong one.

### Output envelope

Every record carries `_input`, `_source` and `_scrapedAt`. Upstream field
names pass through **verbatim** under `listing` — no renaming. `_source` is
`S1-nextdata-search` on every row.

See [`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) for the full reverse-engineering
trail, including the complete TLS profile ladder results.

# Actor input Schema

## `transactionType` (type: `string`):

Which side of the market to search. Note the real path segment for buying is 'sale', not 'buy' -- the label Domain's own nav shows.

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

Domain.com.au location slugs -- one search per entry, each with its own SEARCH\_SUMMARY row. Format is suburb-state-postcode: 'melbourne-vic-3000', 'bondi-nsw-2026', 'brisbane-qld-4000'. A human spelling is slugified automatically. An unknown location returns a clean 404 reported as not\_found, never silently-wrong results (verified live).

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

Stop paginating a location after this many properties. Set to 0 for unlimited (still bounded by Max pages and Domain's own totalPages).

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

Hard cap on pagination depth, independent of maxItems. Domain reports its own totalPages per query (a page past that answers a clean HTTP 400, verified live) -- this is the actor's own additional safety valve for very large searches.

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

Upper bound on requests in flight at once, across locations. Kept low by default (3) -- this target is behind an active bot-mitigation gate (see below), and this actor only has one verified-working TLS profile.

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

Domain.com.au's search page is behind Akamai Bot Manager, which serves a disguised HTTP 200 'behavioral content' challenge to most TLS fingerprints -- confirmed live, only chrome99\_android bypassed it in this session's ladder (chrome124/120/edge101/firefox133/safari17\_0 and every primp profile tried all hit the challenge). Residential proxy is the default for cloud runs on top of that.

## Actor input object example

```json
{
  "transactionType": "rent",
  "locations": [
    "melbourne-vic-3000"
  ],
  "maxItems": 100,
  "maxPages": 10,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "locations": [
        "melbourne-vic-3000"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/domain-properties-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 = { "locations": ["melbourne-vic-3000"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/domain-properties-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 '{
  "locations": [
    "melbourne-vic-3000"
  ]
}' |
apify call scrapyx/domain-properties-scraper --silent --output-dataset

```

## MCP server setup

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