# Homes.co.nz $1💰 URL | Search | Property Scraper (`abotapi/homes-co-nz-scraper`) Actor

From $1/1K. Scrape Homes.co.nz listings via search or URL. Supports multiple locations or links per run with forward pagination. Extract enriched data, including property details, agents, branch info, valuations, media, open homes, and filters for status, price, beds, and baths.

- **URL**: https://apify.com/abotapi/homes-co-nz-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Developer tools, Automation
- **Stats:** 14 total users, 8 monthly users, 99.2% 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?

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

## Homes.co.nz Scraper

Collect rich residential listing data from `homes.co.nz` using structured site data.

### What it does

- Supports `search` mode from plain location names such as `Wellington`.
- Supports `url` mode for detail URLs and map URLs.
- Accepts multiple locations or multiple URLs in one run.
- Advances forward across structured result pages without relying on DOM pagination.
- Pulls structured listing data directly from the site's underlying data sources.
- Enriches each listing with property card data, agent and branch details, title metadata, valuations, media, and open home schedules.
- Applies structured filters for listing status, bedrooms, bathrooms, and price range.
- Supports output sorting by publication time or numeric price when available.

### Why this actor is different

The actor is structured-data first:

- Detail pages are resolved from the page's server-rendered state and then enriched with listing data.
- Map pages are resolved from the page's server-rendered state and then expanded into individual listings.
- Property enrichment adds valuation, title, branch, and agent data.

That keeps extraction faster and more stable than page-structure parsing.

### Input

- `mode`: `search` or `url`
- `locations`: one or more location names for search mode
- `urls`: one or more Homes detail or map URLs for URL mode
- `listingStatus`: `for_sale`, `for_rent`, `just_sold`, or `off_market`
- `minBedrooms`, `minBathrooms`, `minPrice`, `maxPrice`: structured query filters
- `sortBy`: default ordering, newest, oldest, highest price, or lowest price
- `maxListings`: maximum number of listings to output (default 20; the sole soft cap on a run — 0 = unlimited)
- `maxPages`: output pages to emit per search target. Leave empty (0) to walk every result page — bounded only by `maxListings` and the site's own candidate ceiling. Set a number only for an explicit page cap.
- `pageSize`: listings to emit per output page
- `resumeFromRunId`: optional. Paste a previous run ID or dataset ID to continue a large walk-all pull — listings already saved by that run are loaded before scraping starts and skipped, so this run only appends new listings. Independent of `incrementalMode` below.
- `fetchPropertyCards`: whether to include extra property enrichment
- `includeRawApiResponses`: include raw structured payloads in output for debugging

### Resume & recurring updates

- `resumeFromRunId` (see Input above) continues **one interrupted crawl**.
- `incrementalMode` (default `false`) is for **recurring/scheduled monitoring** of the same search or URLs: the actor remembers the previous run itself (a key-value baseline keyed on the tracked search, not a run ID you paste) and tags each listing with:
  - `changeType`: `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED`, or `EXPIRED`
  - `changedFields`: which top-level fields differ from the previous run (empty for NEW/UNCHANGED/REAPPEARED/EXPIRED)
  - `firstSeenAt` / `lastSeenAt`: UTC timestamps for this state key
- `stateKey` (optional): override the auto-derived tracked-search identity. Auto-derivation hashes `mode` + `locations`/`urls` + `listingStatus`/`minBedrooms`/`minBathrooms`/`minPrice`/`maxPrice` + `fetchPropertyCards` (toggling this changes the comparable field set, so it must start a new baseline). It deliberately **excludes** `maxListings`, `maxPages`, `pageSize`, `sortBy` (output caps/ordering, not scope), proxy/debug/notification fields, and the resume/incremental controls themselves.
- `emitUnchanged` (default `false`): when off, a listing identical to the last run is not pushed again — a recurring run only bills for what changed. Turning it on returns (and bills) a row for every unchanged listing too.
- `emitExpired` (default `false`): when on, and only after a run completes a full pass of the tracked search (a capped/partial/resumed run never does this), a listing that dropped out since the last run is pushed once more with `changeType: EXPIRED`. This bills an extra row per dropped listing.
- The identity key across runs is the same listing id the in-run dedup set already uses (`listingId`, falling back to `id` — same underlying value).
- Combining `incrementalMode` with `resumeFromRunId` is only supported for the **first** incremental run for a given state key (bootstraps the resumed ids as known-present placeholders); once a real incremental baseline exists, combining the two fails fast rather than guessing.
- A field transition where either side is missing/null is never treated as a change — only a genuine `value → different value` transition counts. This also protects against a failed detail fetch: it never enters the tracked baseline (no false `UPDATED`/billing on a transient error), and a leaner-but-successful fetch that happens to omit a field carries the prior value forward instead of looking like a change.

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

### Output highlights

Each item contains the standard listing fields plus richer structured sections such as:

- `openHomes`
- `images`
- `media`
- `agents`
- `branch`
- `propertyDetails`
- `valuations`
- `estimates`
- `titleRecord`
- `sourceEndpoints`
- `seedContext`

In `incrementalMode`, each item also carries `changeType`, `changedFields`, `firstSeenAt`, and `lastSeenAt` — see [Resume & recurring updates](#resume--recurring-updates) above.

### Notes

- This actor is tuned for structured site data first.
- If a Homes URL changes shape, the fallback is still the page's embedded state rather than page selectors.

# Actor input Schema

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

Search mode resolves location names and expands them into listing results. URL mode accepts Homes detail URLs or map URLs directly.

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

One or more location names to resolve through Homes and collect listings from.

## `listingStatus` (type: `string`):

Choose which Homes listing bucket to collect.

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

Only include listings with at least this many bedrooms when the structured feed exposes that filter.

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

Only include listings with at least this many bathrooms when the structured feed exposes that filter.

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

Lower price bound to send to the structured Homes query when supported for the chosen listing status.

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

Upper price bound to send to the structured Homes query when supported for the chosen listing status.

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

Applies to the output ordering of collected listings. Price sorting only affects records where a numeric price can be derived from the structured listing data.

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

One or more homes.co.nz detail URLs or map URLs. Multi-URL supported. Detail URLs return a single enriched listing. Map URLs expand into structured result pages and walk forward — when used this way, the search filters and sort above also apply.

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

Hard ceiling on listings output across the whole run — pagination stops as soon as this is hit, in both Search and URL modes. Use 0 for no explicit cap.

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

How many output pages to emit for each search target or map URL. Leave empty to walk every result page: the walk is then bounded only by Max Listings and the site's own candidate ceiling. Set a number only for an explicit page cap below that natural stop.

## `pageSize` (type: `integer`):

Number of listings to emit on each output page.

## `fetchPropertyCards` (type: `boolean`):

Include extra valuation, title, branch, and agent data when available. Adds one additional request per listing — disable to cut cost when you only need core fields.

## `includeRawApiResponses` (type: `boolean`):

Include the raw structured API payloads in each output item for debugging.

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

Paste a previous run ID or dataset ID to continue a large walk-all pull: listings already saved by that run are loaded before scraping starts and skipped, so this run only appends new listings. Leave empty for a normal fresh run. Independent of Incremental mode below — see that field for recurring/scheduled monitoring instead.

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

For a scraper you schedule repeatedly against the SAME search/URLs: the actor remembers the previous run itself (no need to paste a run ID) and tags each listing changeType NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED, with changedFields / firstSeenAt / lastSeenAt. Off by default (normal one-off scrape). See 'Resume from a prior run' above for continuing ONE interrupted run instead - that is a different feature.

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

Only used when Incremental mode is on. Leave empty to auto-derive the tracked search's identity from mode + locations/URLs + filters + fetchPropertyCards (recommended). Set your own text only if you need two runs with different other settings (e.g. different maxListings) to share one baseline, or to keep two similar searches from colliding.

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

Only used when Incremental mode is on. When off (default), a listing identical to the last run is not pushed again, so a recurring run only bills for what changed. Turn on to get a full refreshed dataset every run - this returns and BILLS an extra row for every unchanged listing.

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

Only used when Incremental mode is on, and only after a run completes a full pass of the tracked search (a capped/partial run never emits these). When on, a listing that was present last run but not found this run is pushed once more with changeType EXPIRED - this returns and BILLS an extra row for every listing that dropped out.

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

Proxy configuration for Homes requests. Apify Proxy is recommended for production runs.

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

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

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

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

## Actor input object example

```json
{
  "mode": "search",
  "locations": [
    "Wellington",
    "Auckland Central"
  ],
  "listingStatus": "for_sale",
  "sortBy": "default",
  "urls": [
    "https://homes.co.nz/address/wellington/te-aro/25-26-marion-street/o02G2",
    "https://homes.co.nz/map/wellington?lat=-41.2865&lng=174.7762&zoom=13"
  ],
  "maxListings": 20,
  "maxPages": 0,
  "pageSize": 20,
  "fetchPropertyCards": true,
  "includeRawApiResponses": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `listings` (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": [
        "Wellington",
        "Auckland Central"
    ],
    "urls": [
        "https://homes.co.nz/address/wellington/te-aro/25-26-marion-street/o02G2",
        "https://homes.co.nz/map/wellington?lat=-41.2865&lng=174.7762&zoom=13"
    ],
    "maxPages": 0,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/homes-co-nz-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": [
        "Wellington",
        "Auckland Central",
    ],
    "urls": [
        "https://homes.co.nz/address/wellington/te-aro/25-26-marion-street/o02G2",
        "https://homes.co.nz/map/wellington?lat=-41.2865&lng=174.7762&zoom=13",
    ],
    "maxPages": 0,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/homes-co-nz-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",
  "locations": [
    "Wellington",
    "Auckland Central"
  ],
  "urls": [
    "https://homes.co.nz/address/wellington/te-aro/25-26-marion-street/o02G2",
    "https://homes.co.nz/map/wellington?lat=-41.2865&lng=174.7762&zoom=13"
  ],
  "maxPages": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/homes-co-nz-scraper --silent --output-dataset

```

## MCP server setup

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