# Realtor.ca - Property Listings, Prices, Agents & Open Houses (`abotapi/realtor-ca-scraper`) Actor

From $1/1K. Scrape Realtor.ca property listings with prices, descriptions, images, agent and brokerage details, coordinates, open house times, building features, and more.

- **URL**: https://apify.com/abotapi/realtor-ca-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Automation, Developer tools
- **Features:** Limited permissions
- **Stats:** 54 total users, 3 monthly users, 94.4% runs succeeded, 4 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Realtor.ca Scraper: Canadian Property Listings, Prices & Agent Details

Realtor.ca Scraper turns Realtor.ca, Canada's national property listing site, into a clean property data feed. Search any Canadian city and province, or paste a search URL you already refined on the site, and get full listing details: price, full address with coordinates, bedrooms and bathrooms, building features, every listing photo, and the listing agent's contact and brokerage details. Covers homes for sale, for rent, and recently sold, across residential, commercial, condo, multi-family, vacant land and more, then export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two ways to search.** Pick a city and province with filters, or paste a Realtor.ca search URL already refined in a browser.
- **Every property category.** Residential, commercial, condo, multi-family, vacant land, recreational, agriculture and parking listings, across all 13 Canadian provinces and territories.
- **Full listing depth.** Price, full address with coordinates, bedrooms, bathrooms, parking, land and building size, building features, and every listing photo.
- **Agent and brokerage details on every listing.** Name, phone, position, profile link, brokerage name, logo, website and phone.
- **Built for schedules.** Incremental mode returns only new and changed listings on recurring runs, and a genuine connection failure fails loudly instead of returning an empty dataset.
- **Two independent ways to avoid rescraping.** Continue one interrupted run from its run or dataset ID, or track a recurring search with Incremental mode.

### Use Cases

- **Real estate agents and investors:** track new listings, price changes and sold comparables in target neighborhoods.
- **Property portals and apps:** feed listing search, pricing and photos into your own site or app.
- **Market research and analytics:** build historical pricing datasets by city, province and property type.
- **Relocation and rental services:** monitor rental listings and open houses across cities.
- **Lead generation:** collect agent and brokerage contact details tied to active listings.

### Data You Get

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

| Field | Example |
| --- | --- |
| `propertyId` | `"12345678"` |
| `mlsNumber` | `"C1234567"` |
| `url` | Full listing URL on Realtor.ca |
| `listingType` | `"buy"` (also `"rent"`, `"sold"`) |
| `propertyType` | `"condo"` (also `"apartment"`, `"house"`, `"townhouse"`, `"duplex"`, `"triplex"`, `"fourplex"`, `"multi-family"`, `"modular"`, `"vacant-land"`, `"parking"`, `"commercial"`, `"farm"`, `"other"`) |
| `buildingType` | `"Apartment"` (raw building type as published) |
| `ownershipType` | `"Condominium/Strata"` |
| Listing description text | Not included. realtor.ca returns the remarks field empty in search results and only serves it on the listing-detail endpoint, which refuses requests (HTTP 403). |
| `dateListed` / `timeOnRealtor` | `"2026-03-19T10:00:00.000Z"` / `"2 hours ago"` |
| `communityName`, `statusId` | Extra listing attributes, present only when realtor.ca publishes them for that listing (omitted otherwise, never estimated) |
| `address` | `full`, `street`, `city`, `province`, `postalCode` |
| `coordinates` | `latitude`, `longitude` |
| `price` | `display` (formatted string), `value` (numeric CAD) |
| `features` | `bedrooms`, `bathrooms`, `parkingSpaces`, `parkingType`, `landSize`, `buildingSize`, `stories` |
| `propertyFeatures` | Building amenities and construction details, e.g. `["Central air conditioning", "Style: Bungalow"]` |
| `tags` | Listing tags shown on the site, e.g. `["Open House"]` |
| `media` | `imageCount`, `images` (high resolution photo URLs) |
| `agents` | One entry per listing agent: name, phone, position, profile link, brokerage name, brokerage ID (`organizationId`), logo, website, phone |
| `openHouse` | Upcoming open house entries: `startTime`, `endTime`, `comments` |
| `scrapedAt` | `"2026-03-19T12:00:00.000Z"` |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | Only present when Incremental mode is on: `"NEW"`, `"UPDATED"`, `"UNCHANGED"`, `"REAPPEARED"` or `"EXPIRED"`, the fields that changed, and when the listing was first and last seen |

### How to Use

1. Pick a **mode**: Location (city and province, plus filters) or URL (paste one or more Realtor.ca search URLs).
2. In Location mode, add one or more cities and set the filters you need: listing type, property category, building type, price, bedrooms, bathrooms, open house. In URL mode, paste the URL(s) instead; the filter fields above are ignored because the URL already carries them, but set Listing Type to match the URL since it labels each row's `listingType`.
3. Set **Maximum Listings** and **Maximum Pages per Location** to control run size and cost.
4. Click **Start**. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Houses for sale in Toronto, with price and bed/bath filters:**

```json
{
  "mode": "location",
  "locations": [{ "city": "Toronto", "province": "ON" }],
  "listingType": "buy",
  "propertySearchCategory": "residential",
  "buildingTypeFilter": "house",
  "priceMin": 500000,
  "priceMax": 1500000,
  "bedroomsMin": 3,
  "bathroomsMin": 2,
  "maxListings": 100
}
```

**Condos for rent across two cities:**

```json
{
  "mode": "location",
  "locations": [
    { "city": "Vancouver", "province": "BC" },
    { "city": "Calgary", "province": "AB" }
  ],
  "listingType": "rent",
  "propertySearchCategory": "condo",
  "maxListings": 50
}
```

**Recently sold listings in the last 90 days:**

```json
{
  "mode": "location",
  "locations": [{ "city": "Montreal", "province": "QC" }],
  "listingType": "sold",
  "soldWithinDays": "90days",
  "maxListings": 200
}
```

**Paste a search URL refined on the site:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.realtor.ca/map#ZoomLevel=11&Center=43.718245%2C-79.376682&LatitudeMax=43.86034&LongitudeMax=-79.12303&LatitudeMin=43.57584&LongitudeMin=-79.63033&view=list&Sort=6-D&PGeoIds=g30_dpz89rm7&GeoName=Toronto%2C%20ON&PropertyTypeGroupID=1&TransactionTypeId=2&PropertySearchTypeId=1&Currency=CAD&IncludeHiddenListings=false",
    "https://www.realtor.ca/on/toronto/real-estate"
  ]
}
```

**Supported URL shapes**

| Shape | Example | Behaviour |
|---|---|---|
| Map search URL | `https://www.realtor.ca/map#ZoomLevel=11&LatitudeMax=...` | Searches the exact area and filters in the URL. |
| City page | `https://www.realtor.ca/on/toronto/real-estate` | Searches the built-in area for that city (the same cities as Location mode); a city not in the list is skipped with a warning. |
| Single listing page (`/real-estate/<id>/<slug>`) | not supported | Skipped with a warning; use a search URL. The listing-detail endpoint refused (HTTP 403) every request from a cleared session when tested, so a single page cannot be fetched reliably. |

City rent pages (`realtor.ca/<province>/<city>/apartments-for-rent`) are supported for the same built-in cities and search rentals; their rows come back with `listingType` set to `rent` and the rent in `price` (for example `$3,400/Monthly`, with the cadence in `price.period`). Single-listing pages and province landing pages (no listing cards) are not searched. Any URL that is not one of the supported shapes is skipped with a warning in the run log; it never falls back to a default search area. URL mode with the URLs field left empty searches two default areas in order, a Toronto map search and then the Vancouver city page (the Console form is prefilled with those two plus a Toronto rent page), and stops as soon as **Max listings** is reached. With the default Max listings (20) the Toronto map search alone fills the limit, so raise Max listings (or edit the URLs) to reach the Vancouver page.

#### Dataset views

The dataset has five ready-made tables: **Overview** (price, address, key features), **Agents & Brokerages**, **Media & Open House**, **Changes (incremental)** and **Details** (community, status, ownership and building type). Turn on **Flat output** for one-level records.

#### Cities with built-in coordinates

Location mode has a built-in, precise search area for 33 major Canadian cities (Toronto, Vancouver, Montreal, Calgary, Edmonton, Ottawa, Winnipeg, Halifax and more). Any other city is searched using a default area in southern Ontario plus the city and province name as a search term, so cities outside southern Ontario may return few or unrelated listings. For those, and for an exact area anywhere in Canada, draw it on the site and use URL mode instead.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/realtor-ca-scraper").call(run_input={
    "mode": "location",
    "locations": [{"city": "Toronto", "province": "ON"}],
    "listingType": "buy",
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["address"]["full"], listing["price"]["display"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/realtor-ca-scraper').call({
  mode: 'location',
  locations: [{ city: 'Toronto', province: 'ON' }],
  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.

#### Resume, Incremental mode and Checkpoint

Three independent controls that are easy to mix up:

- **Resume from run/dataset ID** continues ONE interrupted crawl: paste its run or dataset ID and this run skips every listing it already collected, so you don't pay twice.
- **Incremental mode** remembers a recurring search between separate scheduled runs. 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 scans the whole search, and only with `emitExpired` on). `stateKey` names or shares the stored baseline. Use **Ignore fields when detecting changes** to name fields that should never trigger `UPDATED`; the relative "time on realtor.ca" text is ignored by default.
- **Resume from Checkpoint** continues THIS SAME run after a platform migration or restart. It is on by default and does not change billing or output shape.

### 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 listing, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where 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 | `location` | Search mode: `location` (city and province, plus filters) or `url` (paste Realtor.ca search URLs). |
| `locations` | array | (none; prefilled with `Toronto, ON`) | Cities to search in Location mode. Each entry needs a city and a two-letter province code, e.g. `ON`. |
| `listingType` | string | `buy` | `buy` (for sale), `rent`, or `sold`. |
| `propertySearchCategory` | string | `residential` | Property category: `residential`, `commercial`, `condo`, `multi-family`, `vacant-land`, `recreational`, `agriculture`, or `parking`. |
| `buildingTypeFilter` | string | (none) | Optional building type within the selected category: `house`, `row-townhouse`, or `apartment`. Location mode only. |
| `priceMin` | integer | (none) | Minimum price in CAD. Location mode only. |
| `priceMax` | integer | (none) | Maximum price in CAD. Location mode only. |
| `bedroomsMin` | integer | (none) | Minimum bedrooms, 0 to 10. Location mode only. |
| `bedroomsMax` | integer | (none) | Maximum bedrooms, 0 to 10. Location mode only; applies only together with `bedroomsMin`. |
| `bathroomsMin` | integer | (none) | Minimum bathrooms, 0 to 10. Location mode only. |
| `sortBy` | string | `6-D` (Newest) | Sort order: Newest, Oldest, Lowest Price, or Highest Price. |
| `soldWithinDays` | string | `1year` | Date range for `sold` listings: `7days`, `14days`, `30days`, `90days`, `6months`, or `1year`. |
| `openHouseOnly` | boolean | `false` | Only return listings with an upcoming open house. Location mode only. |
| `keywords` | string | (none) | Not currently applied to Location mode searches, which always search by city and province. To search by keyword, add it on Realtor.ca and paste the URL in URL mode. |
| `urls` | array | one map search URL (Toronto) and one city page URL (Vancouver) | Realtor.ca map search URLs or city pages, used in URL mode. |
| `maxListings` | integer | `20` | Stop after this many listings (`0` = unlimited). |
| `maxPages` | integer | `0` | Maximum search result pages per location, about 12 listings each (`0` = every page, with a hard cap of 1000 pages per location). |
| `proxyConfiguration` | object | Apify residential proxy, country CA | Connection settings. |
| `resumeFromRunId` | string | (none) | Paste a previous run or dataset ID to continue one interrupted crawl, skipping listings it already collected. |
| `incrementalMode` | boolean | `false` | Track a recurring search across scheduled runs and classify each listing as `NEW`, `UPDATED`, `REAPPEARED`, `EXPIRED` or `UNCHANGED`. |
| `stateKey` | string | (none) | Optional label for the Incremental mode baseline; auto-derived from your search when left empty. |
| `emitUnchanged` | boolean | `false` | Incremental mode: also return (and bill) listings with no changes. |
| `simplifyOutput` | boolean | `false` | Return flat records (`addressCity`, `priceValue`, `featuresBedrooms`, first agent as `agentName`/`agentPhone`) instead of nested objects. Same listings and billing. |
| `emitExpired` | boolean | `false` | Incremental mode: also return (and bill) listings that disappeared, once, after a complete scan. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode: field names that never make a listing `UPDATED` (matched at any nesting depth, still returned in the output). `timeOnRealtor`, its mirrored entries in `tags` and `scrapedAt` are always ignored because they change every run. |
| `resumeFromCheckpoint` | boolean | `true` | Resume this same run from its last checkpoint after a platform migration or restart. |
| `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 items written to each connector per run. |

### Output Example

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

```json
{
  "propertyId": "12345678",
  "mlsNumber": "C1234567",
  "url": "https://www.realtor.ca/listing/12345678/100-sample-street-toronto",
  "listingType": "buy",
  "propertyType": "condo",
  "buildingType": "Apartment",
  "ownershipType": "Condominium/Strata",
  "dateListed": "2026-03-19T10:00:00.000Z",
  "timeOnRealtor": "2 hours ago",
  "tags": ["Open House"],
  "address": {
    "full": "100 SAMPLE STREET|Toronto, Ontario M5A 1A1",
    "street": "100 SAMPLE STREET",
    "city": "Toronto",
    "province": "Ontario",
    "postalCode": "M5A1A1"
  },
  "coordinates": {
    "latitude": 43.65,
    "longitude": -79.38
  },
  "price": {
    "display": "$850,000",
    "value": 850000
  },
  "features": {
    "bedrooms": 2,
    "bathrooms": 2,
    "parkingSpaces": 1,
    "parkingType": "Underground, Garage",
    "buildingSize": "90 m2"
  },
  "propertyFeatures": [
    "Storage locker",
    "Floor Area: 900+ sqft",
    "Parking: Underground, Garage"
  ],
  "media": {
    "imageCount": 1,
    "images": ["https://cdn.realtor.ca/listings/sample/highres/1/sample_1.jpg"]
  },
  "agents": [
    {
      "name": "Sample Agent",
      "phone": "(416) 555-0100",
      "position": "Broker of Record",
      "profileUrl": "https://www.realtor.ca/agent/100001/sample-agent",
      "organization": "Sample Realty Inc.",
      "organizationId": "100200",
      "organizationDesignation": "Brokerage",
      "organizationWebsite": "http://www.example-realty.ca/",
      "organizationPhone": "(416) 555-0200"
    }
  ],
  "scrapedAt": "2026-03-19T12:00:00.000Z"
}
```

### Plan Requirement

This site refuses datacenter connections and plain scripted requests, so the actor needs Apify **residential proxies** with country **Canada** (the default). Datacenter proxies are not expected to work: the run log says "Using a datacenter proxy. This site usually refuses datacenter connections. Use the default RESIDENTIAL proxy with country CA for reliable runs." Residential proxies are not included in the Apify free plan. Leave **Proxy configuration** at its default. The actor retries and rotates connections when one is refused, and stops early (keeping what it collected) with "Too many consecutive upstream refusals, stopping this run early" when the site keeps refusing.

### FAQ

#### How much does it cost?

You pay per listing returned. The **Pricing** tab on the actor page shows the current rate. Use **Maximum Listings** to cap the size, and cost, of any run.

#### Is it legal to scrape Realtor.ca?

This actor collects only publicly available property listing data. You are responsible for how you use it: follow Realtor.ca's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Agent and brokerage names are included because that information is published alongside each listing.

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

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run classifies every listing as `NEW`, `UPDATED`, `REAPPEARED`, `EXPIRED` or `UNCHANGED` against the last committed run for that search, and unchanged listings are not billed unless you turn on **Emit unchanged listings**.

#### What is the difference between Resume from run/dataset ID, Incremental mode, and Resume from Checkpoint?

They solve three different problems. **Resume from run/dataset ID** continues one specific interrupted run so you don't rescrape listings it already collected. **Incremental mode** tracks the same search across separate scheduled runs and reports what changed. **Resume from Checkpoint** (on by default) simply lets one run recover its own progress after a platform migration or restart. Use any combination, or none.

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

Apart from invalid input or a resume ID that can't be read, the run fails only when nothing could be collected and the site could not be reached at all: either connection setup was rejected on every available proxy tier, or a search's result page never came back readable. In that case it exits with an error and a clear status message; wait a few minutes and try again, or switch **Connection** to residential with country CA. If the site was reachable but genuinely has no listings matching your search and filters, the run finishes successfully with an empty dataset and a "No results found" status message instead.

#### 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 Realtor data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏠 <a href="https://apify.com/abotapi/realtor-com-scraper"><b>Realtor.com</b></a><br>From $0.9/1K. Extract property listings from realtor.com. Get comprehensive data...</td><td>🧩 <a href="https://apify.com/abotapi/homes-com-scraper"><b>Homes.com Scraper</b></a><br>Scrape US property records from Homes.com. Pick a city, state or ZIP and a channel (for...</td></tr>
<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/trulia-scraper"><b>Trulia Scraper</b></a><br>Scrape Trulia.com property listings by location, filters, or URL. Extract prices, beds...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/homes-co-nz-scraper"><b>Homes.co.nz Scraper</b></a><br>Scrape Homes.co.nz listings by location or URL with pagination. Extract property details...</td><td>🏠 <a href="https://apify.com/abotapi/crexi-commercial-listings-scraper"><b>Crexi Scraper</b></a><br>Scrape crexi.com commercial listings: asking price, cap rate, square footage, units, year...</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/realtor-ca-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`):

Location mode = pick city + province + filters below. URL mode = paste any realtor.ca search URL already refined in a browser (recommended for complex filters like map area, building type, exact bed/bath ranges).

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

Locations to search. Province is optional - leave it blank to use the city's default. Example: Toronto / ON.

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

Type of property listings to scrape.

## `propertySearchCategory` (type: `string`):

Property search category. Determines the type of properties returned.

## `buildingTypeFilter` (type: `string`):

Filter by specific building type (optional, works within the selected Property Category)

## `priceMin` (type: `integer`):

Minimum property price (CAD)

## `priceMax` (type: `integer`):

Maximum property price (CAD)

## `bedroomsMin` (type: `integer`):

Minimum number of bedrooms

## `bedroomsMax` (type: `integer`):

Maximum number of bedrooms

## `bathroomsMin` (type: `integer`):

Minimum number of bathrooms

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

Sort order for search results. Default is 'Newest' (date listed, newest first).

## `soldWithinDays` (type: `string`):

Date range for sold listings. Only applies when Listing Type is 'Sold'.

## `openHouseOnly` (type: `boolean`):

Only return listings with upcoming open houses

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

Additional search keywords (e.g. 'pool', 'waterfront', 'garage')

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

Realtor.ca URLs to scrape, one search per URL. Supported: a map search URL (open realtor.ca, apply filters or draw the area, copy the address bar) and a city page (realtor.ca/<province>/<city>/real-estate or /apartments-for-rent, for the cities in the built-in list). Single-listing pages and other URLs are skipped with a warning. Searched in order; the run stops once Max listings is reached, so with the default limit only the first URL may be scraped.

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

Stop after scraping this many listings. Set to 0 for unlimited.

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

Maximum search result pages to process per location. Each page has ~12 results. Set to 0 for the default (every page the search has, up to a hard cap of 1000 pages per location).

## `simplifyOutput` (type: `boolean`):

When ON, each result is returned as a flat record: address, price, features, coordinates and media become single columns (addressCity, priceValue, featuresBedrooms, ...) and the first agent is lifted to agentName / agentPhone / agentOrganization. Same listings and same billing; only the shape changes. Off by default (nested record).

## `resumeFromCheckpoint` (type: `boolean`):

Continue this SAME run after a platform migration or restart, reloading its own in-progress state. Different from 'Resume from run/dataset ID' above, which continues a separate, already-finished run.

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

Residential proxy with country CA (the default) is recommended: this site usually refuses datacenter connections. Residential proxy requires a paid Apify plan.

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

Paste a previous run ID or dataset ID to skip listings it already collected and only scrape new ones - useful for continuing an interrupted crawl. Leave empty for a normal fresh run. For recurring/scheduled monitoring of the same search instead, use Incremental mode below.

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

When ON, the actor remembers this search's results between separate runs and classifies each listing as NEW / UPDATED / REAPPEARED / EXPIRED / UNCHANGED (see the dataset's changeType field). Off by default so existing scheduled runs are unaffected. Distinct from 'Resume from run/dataset ID' above, which continues one specific interrupted run instead of tracking recurring changes.

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

Optional label for the saved baseline Incremental mode uses. Leave empty to auto-derive one from your search mode and filters, so two differently-configured searches never share a baseline. Set your own label to track multiple named campaigns, or to intentionally share a baseline across differently-configured runs.

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

When ON, listings with no changes since the last run are still returned (changeType=UNCHANGED) and billed as a normal result. Off by default so a recurring run only returns and bills for what actually changed.

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

When ON, listings tracked by a previous Incremental mode run that no longer appear are returned once as changeType=EXPIRED and billed as a normal result. Only fires after a run that scans this search's full result set (no page/item cap hit, not a resumed run). Off by default.

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

Output field names that should NOT make a listing count as UPDATED, even when they changed. By default the relative 'time on realtor.ca' text (timeOnRealtor, and the matching entries in tags) and scrapedAt are ignored automatically because they change every run. Add any other field name here, for example price or media. Matches a field name at any nesting depth. Ignored fields are still returned in the output.

## `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. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. 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": "location",
  "locations": [
    {
      "city": "Toronto",
      "province": "ON"
    }
  ],
  "listingType": "buy",
  "propertySearchCategory": "residential",
  "sortBy": "6-D",
  "soldWithinDays": "1year",
  "openHouseOnly": false,
  "urls": [
    "https://www.realtor.ca/map#ZoomLevel=11&Center=43.718245%2C-79.376682&LatitudeMax=43.86034&LongitudeMax=-79.12303&LatitudeMin=43.57584&LongitudeMin=-79.63033&view=list&Sort=6-D&PGeoIds=g30_dpz89rm7&GeoName=Toronto%2C%20ON&PropertyTypeGroupID=1&TransactionTypeId=2&PropertySearchTypeId=1&Currency=CAD&IncludeHiddenListings=false",
    "https://www.realtor.ca/bc/vancouver/real-estate",
    "https://www.realtor.ca/on/toronto/apartments-for-rent"
  ],
  "maxListings": 20,
  "maxPages": 0,
  "simplifyOutput": false,
  "resumeFromCheckpoint": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CA"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `media` (type: `string`):

No description

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

No description

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

No description

## `changes` (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 = {
    "locations": [
        {
            "city": "Toronto",
            "province": "ON"
        }
    ],
    "urls": [
        "https://www.realtor.ca/map#ZoomLevel=11&Center=43.718245%2C-79.376682&LatitudeMax=43.86034&LongitudeMax=-79.12303&LatitudeMin=43.57584&LongitudeMin=-79.63033&view=list&Sort=6-D&PGeoIds=g30_dpz89rm7&GeoName=Toronto%2C%20ON&PropertyTypeGroupID=1&TransactionTypeId=2&PropertySearchTypeId=1&Currency=CAD&IncludeHiddenListings=false",
        "https://www.realtor.ca/bc/vancouver/real-estate",
        "https://www.realtor.ca/on/toronto/apartments-for-rent"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "CA"
    },
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/realtor-ca-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": [{
            "city": "Toronto",
            "province": "ON",
        }],
    "urls": [
        "https://www.realtor.ca/map#ZoomLevel=11&Center=43.718245%2C-79.376682&LatitudeMax=43.86034&LongitudeMax=-79.12303&LatitudeMin=43.57584&LongitudeMin=-79.63033&view=list&Sort=6-D&PGeoIds=g30_dpz89rm7&GeoName=Toronto%2C%20ON&PropertyTypeGroupID=1&TransactionTypeId=2&PropertySearchTypeId=1&Currency=CAD&IncludeHiddenListings=false",
        "https://www.realtor.ca/bc/vancouver/real-estate",
        "https://www.realtor.ca/on/toronto/apartments-for-rent",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "CA",
    },
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/realtor-ca-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": [
    {
      "city": "Toronto",
      "province": "ON"
    }
  ],
  "urls": [
    "https://www.realtor.ca/map#ZoomLevel=11&Center=43.718245%2C-79.376682&LatitudeMax=43.86034&LongitudeMax=-79.12303&LatitudeMin=43.57584&LongitudeMin=-79.63033&view=list&Sort=6-D&PGeoIds=g30_dpz89rm7&GeoName=Toronto%2C%20ON&PropertyTypeGroupID=1&TransactionTypeId=2&PropertySearchTypeId=1&Currency=CAD&IncludeHiddenListings=false",
    "https://www.realtor.ca/bc/vancouver/real-estate",
    "https://www.realtor.ca/on/toronto/apartments-for-rent"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CA"
  },
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/realtor-ca-scraper --silent --output-dataset

```

## MCP server setup

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