# Realtor.ca Property Scraper — Canada · Market KPIs (`sian.agency/realtor-ca-property-scraper`) Actor

Scrape Realtor.ca listings nationwide — sale + rent, by city/province, postal code, coordinates, polygon, or URL. Built-in market KPIs (CAD), property lookup with agents, history, mortgage links. Independent tool — not affiliated with Realtor.ca or CREA.

- **URL**: https://apify.com/sian.agency/realtor-ca-property-scraper.md
- **Developed by:** [SIÁN OÜ](https://apify.com/sian.agency) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 listing extracteds

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## 🇨🇦 Realtor.ca Property Scraper — Canadian MLS Listings + Market KPIs 🚀

[![SIÁN Agency Store](https://img.shields.io/badge/Store-SI%C3%81N%20Agency-1AE392)](https://apify.com/sian.agency?fpr=sian) [![SIÁN-Bayut Property Scraper](https://img.shields.io/badge/SI%C3%81N-Bayut%20Property%20Scraper-93D500)](https://apify.com/sian.agency/bayut-property-scraper?fpr=sian) [![SIÁN-Smart Idealista Scraper](https://img.shields.io/badge/SI%C3%81N-Smart%20Idealista%20Scraper-E60023)](https://apify.com/sian.agency/smart-idealista-scraper?fpr=sian) [![SIÁN-Property Skip Tracing](https://img.shields.io/badge/SI%C3%81N-Property%20Skip%20Tracing-1AE392)](https://apify.com/sian.agency/property-skip-tracing?fpr=sian)

#### 🎉 NEW: The only Canadian Realtor.ca scraper on Apify with tiered pay-per-event pricing — FREE $0.030 → flat $0.003 at GOLD
##### Built for Canadian real estate investors, brokerages, market analysts, lead-gen teams, PropTech builders, and investment underwriters who need reliable Realtor.ca MLS data at scale.

---

### 📋 Overview

**Pull every Realtor.ca listing in your target Canadian market and ship clean, normalized rows straight into your dataset — no proxies, no headless browsers, no broken scrapes.** This actor wraps the full Realtor.ca surface (sale + rent, every province, every postal code) and gives you a single-property lookup mode with agents, history, and mortgage deep links — so you can underwrite a Canadian deal in minutes, not days.

**Why Canadian investors, brokerages, and PropTech teams choose us:**
- ✅ **Nationwide Canadian coverage** — all 10 provinces + 3 territories, sale and rent in one feed, filtered client-side via the `listingType` flag. Toronto, Vancouver, Montréal, Calgary, Ottawa, Edmonton — every market, one actor.
- ⚡ **6 search modes** — location (city + province), Canadian postal code (`A1A 1A1`), coordinates + radius (km), polygon, Realtor.ca URL, plus single-property lookup by ID / address / URL. Most community scrapers ship 1–2.
- 🎯 **Property lookup with full detail block** — agents (name, phone, email, brokerage), property history, open house schedule, mortgage calculator deep links, building features (basement, exterior finish, appliances, fire protection, amenities), land features (frontage, depth, sewer).
- 💰 **Tiered Idealista-style curve** — FREE $0.030 → flat **$0.003 at GOLD**. Beats every Canadian community-scraper rate ($5–15 / 1,000 results) at scale, and we charge per-listing, not per-page.
- 💎 **Canadian quirks normalized** — `"5 + 1"` bedrooms split into above-grade + basement totals; metric `m²` sizes converted to imperial sqft (1 m² = 10.7639 sqft); postal codes normalized to `A1A 1A1`; CAD-formatted tax strings parsed to integers.
- ✨ **NEW**: HTML market report per run — median list price (CAD), $/sqft distribution, top cities, top provinces, sale/rent split, per-query totals — saved automatically to the key-value store, no extra cost.

---

### ✨ Features

- 🏙️ **Search by Location** — `"City, Province"` (e.g. `"Toronto, ON"`, `"Vancouver, BC"`, `"Montréal, QC"`)
- 📮 **Search by Postal Code** — Canadian `A1A 1A1` format, with or without space (normalized internally)
- 📍 **Search by Coordinates** — Lat/lon + radius in kilometres for tight neighbourhood targeting
- 🗺️ **Polygon Search** — Custom multi-point lat/lon boundaries for irregular markets (ski-resort zones, waterfronts, neighbourhood-level)
- 🔗 **URL Search** — Paste a Realtor.ca search URL; we mirror what you see in the browser
- 🆔 **Single-Property Lookup** — Direct fetch by Realtor.ca property ID, full address, or URL — returns the rich detail block
- 🏷 **Sale + Rent Filter** — One feed, one `listingType` flag (`sale` / `rent` / `both`)
- 👥 **Agents Data** — Name, phone, email, brokerage on every lookup row
- 📜 **Property History** — Past list dates, price changes, status transitions
- 💵 **Mortgage Deep Links** — Pre-filled mortgage calculator URLs for downstream lead-gen workflows
- 📊 **Market KPIs in CAD** — Median list price, $/sqft, beds / baths / sqft distributions, top cities, top provinces, property-type and transaction-type breakdowns
- 📑 **HTML Market Report** — Auto-generated, saved to the run's key-value store at `report.html`
- 📦 **Bulk Inputs (PAID)** — Multiple cities, postal codes, URLs, property IDs, or addresses in one run
- 🔁 **Auto-Pagination** — Drains every search query (50/page) up to your `maxResults` cap
- 🧹 **Smart Deduplication** — Same property never pushed twice across paginated pages

---

### 🎬 Quick Start

Pick a search mode, hand us a Canadian location (or postal code, coordinates, polygon, URL, property ID), choose your `listingType`, and run. Listings stream straight into the Apify dataset; the HTML market report saves to the run's key-value store at `report.html`.

```bash
curl -X POST https://api.apify.com/v2/acts/sian.agency~realtor-ca-property-scraper/runs?token=[YOUR_TOKEN] \
-H 'Content-Type: application/json' \
-d '{"searchMode": "location", "location": "Toronto, ON", "listingType": "sale", "maxResults": 100}'
````

***

### 🚀 Getting Started (3 Simple Steps)

#### Step 1: Pick a search mode

Choose one of: `location`, `postalCode`, `coordinates`, `polygon`, `url`, or `lookup`. For most workflows, `location` is the simplest entry point ("Toronto, ON", "Vancouver, BC").

#### Step 2: Set your filters

Set `listingType` to `sale`, `rent`, or `both` (default). Set `maxResults` to cap the run (FREE tier always tops out at 25 listings; PAID has no ceiling).

#### Step 3: Hit run

The first **25 listings are free** (no credit card). Watch the dataset fill in real time. Download as JSON / CSV / Excel / RSS / JSONL / XML directly from the Apify UI.

**That's it! In under 5 minutes, you'll have:**

- A clean, normalized dataset of Realtor.ca listings with 40+ fields
- Median list price (CAD), $/sqft, and distribution KPIs computed
- An HTML market report you can share with clients or your team

***

### 📥 Input Configuration

| Field | Type | Required | Description |
|---|---|---|---|
| `searchMode` | string | Yes | One of: `location`, `postalCode`, `coordinates`, `polygon`, `url`, `lookup` |
| `listingType` | string | No | `both` (default), `sale`, or `rent`. Client-side filter on Realtor.ca's mixed feed |
| `maxResults` | integer | No | 1–1,000, default 50. FREE tier is always capped at 25 per run |
| `location` | string | Conditional | `"City, Province"` (e.g. `"Toronto, ON"`) — used when `searchMode=location` |
| `locations` | array | PAID | Bulk locations array — PAID tier only |
| `postalCode` | string | Conditional | Canadian postal code `A1A 1A1` (space optional) — used when `searchMode=postalCode` |
| `postalCodes` | array | PAID | Bulk postal codes |
| `latitude` / `longitude` | number | Conditional | Centre point for `coordinates` mode |
| `radius` | number | Optional | Search radius in kilometres (coordinates mode) |
| `polygon` | string | Conditional | `"lon lat,lon lat,..."` (≥3 points, space-separated pairs, comma-separated points) |
| `url` | string | Conditional | A Realtor.ca search URL — used when `searchMode=url` |
| `urls` | array | PAID | Bulk Realtor.ca search URLs |
| `lookupBy` | string | Conditional | `propertyId`, `address`, or `url` — used when `searchMode=lookup` |
| `propertyId` | string | Conditional | Realtor.ca numeric property ID |
| `address` | string | Conditional | Full Canadian address |
| `propertyIds` / `addresses` | array | PAID | Bulk lookup inputs |

**Example — Toronto for-sale listings:**

```json
{
  "searchMode": "location",
  "location": "Toronto, ON",
  "listingType": "sale",
  "maxResults": 100
}
```

**Example — Vancouver rentals only:**

```json
{
  "searchMode": "location",
  "location": "Vancouver, BC",
  "listingType": "rent",
  "maxResults": 50
}
```

**Example — Canadian postal code lookup:**

```json
{
  "searchMode": "postalCode",
  "postalCode": "M5V 2H1",
  "listingType": "both"
}
```

**Example — Polygon-bounded neighbourhood scan (downtown Toronto):**

```json
{
  "searchMode": "polygon",
  "polygon": "-79.395 43.660,-79.380 43.660,-79.380 43.650,-79.395 43.650",
  "listingType": "sale",
  "maxResults": 200
}
```

**Example — Single-property full details:**

```json
{
  "searchMode": "lookup",
  "lookupBy": "propertyId",
  "propertyId": "29768213"
}
```

***

### 📤 Output

Results are saved to the Apify dataset with **40+ fields** per listing including:

| Field | Type | Description |
|---|---|---|
| `propertyId` | string | Realtor.ca numeric ID |
| `mlsNumber` | string | Canadian MLS reference number |
| `transactionType` | string | `"For sale"`, `"For rent"`, or `null` |
| `status` | string | Active / sold / pending |
| `propertyType` | string | Single Family, Condo Apartment, Multi-family, etc. |
| `ownershipType` | string | Freehold, Condominium, Leasehold |
| `address.line` | string | Street line (e.g. `"44 WALLACE STREET"`) |
| `address.locality` | string | City + province + postal (e.g. `"Vaughan (West Woodbridge), Ontario L4L2P3"`) |
| `address.full` | string | Combined display string |
| `address.postalCode` | string | Normalized to `A1A 1A1` |
| `address.province` | string | Two-letter (ON, BC, QC, AB, ...) |
| `latitude` / `longitude` | number | Geo coordinates (coerced from upstream strings) |
| `listPriceCad` | number | Parsed from `priceUnformatted` (CAD integer) |
| `priceDisplay` | string | CAD-formatted string (e.g. `"$1,495,000"`) |
| `pricePerSqft` | number | Computed CAD per sqft |
| `taxAmount` | integer | Annual property tax in CAD (parsed from `"$5,349"` string) |
| `specs.beds` | integer | Total bedrooms |
| `specs.bedsAboveGrade` | integer | Above-grade bedrooms (parsed from `"5 + 1"`) |
| `specs.bedsBelowGrade` | integer | Basement bedrooms (parsed from `"5 + 1"`) |
| `specs.bathrooms` | integer | Total bathrooms |
| `specs.halfBaths` | integer | Half / powder rooms |
| `specs.sizeInterior` | object | `{ value, unit, sqftDerived }` — handles `"232.2557 m2"` or `"2500 sqft"` |
| `building` | object | Constructed date, amenities, appliances, basement type, exterior finish, fire protection, cooling, age |
| `land` | object | Total size, frontage, depth, sewer type |
| `tags` | array | Realtor.ca display tags (`"New"`, `"Open House"`, `"17 min ago"`) |
| `photoUrl` | string | Primary photo URL |
| `photoUrls` | array | All listing photo URLs |
| `photoCount` | integer | Total photo count |
| `description` | string | Public remarks / listing description |
| **`agents`** | array | (lookup only) `[{ name, phone, email, brokerage }, ...]` |
| **`history`** | array | (lookup only) Past list dates + price changes |
| **`openHouse`** | object | (lookup only) Open house schedule |
| **`mortgageLinks`** | object | (lookup only) Pre-filled mortgage calculator URLs |

**Example row (truncated):**

```json
{
  "propertyId": "29768213",
  "mlsNumber": "N8123456",
  "transactionType": "For sale",
  "propertyType": "Single Family",
  "ownershipType": "Freehold",
  "address": {
    "line": "44 WALLACE STREET",
    "locality": "Vaughan (West Woodbridge), Ontario L4L2P3",
    "postalCode": "L4L 2P3",
    "province": "ON"
  },
  "latitude": 43.7889,
  "longitude": -79.5996,
  "listPriceCad": 1495000,
  "priceDisplay": "$1,495,000",
  "pricePerSqft": 598,
  "taxAmount": 5349,
  "specs": {
    "beds": 6,
    "bedsAboveGrade": 5,
    "bedsBelowGrade": 1,
    "bathrooms": 4,
    "sizeInterior": { "value": 232.2557, "unit": "m2", "sqftDerived": 2500 }
  },
  "tags": ["New", "Open House"],
  "photoCount": 32
}
```

An **HTML market report** is also saved to `report.html` in the run's key-value store — median list price (CAD), $/sqft distribution, top cities, top provinces, sale/rent split, per-query totals, and property-type breakdowns.

***

### 💼 Use Cases & Examples

#### 1. Canadian real-estate investor underwriting

**A residential investor sizing up a Toronto or Vancouver acquisition.**

**Input:** City + province (e.g. `"Toronto, ON"`) with `listingType=sale` and `maxResults=500`
**Output:** Every active for-sale listing with list price (CAD), $/sqft, beds (above + below grade), annual tax amount, agent contacts, full building features
**Use:** Build comp sets, score deals against neighbourhood medians, defend offers to lenders with real per-listing data — not aggregator averages.

#### 2. Comparative Market Analysis (CMA) for brokerages

**A Toronto/Montréal brokerage shipping CMA decks to vendor clients.**

**Input:** Postal code or polygon around the subject property
**Output:** Median list price, $/sqft distribution, sale/rent split, top property types, plus the auto-generated HTML market report
**Use:** Ship CMA reports to clients in minutes instead of hours. Drop the HTML report straight into a vendor pitch — it's pre-formatted and shareable.

#### 3. Lead-gen prospecting from province-targeted searches

**A Canadian real-estate SaaS or referral business building outreach lists.**

**Input:** City + province + `listingType=sale`, plus `lookup` mode for each top result to pull agent data
**Output:** Full listings + agents (name, phone, email, brokerage) per property
**Use:** Build buyer/seller outreach lists or agent-poaching pipelines for Toronto, Vancouver, Montréal, Calgary, Ottawa, Edmonton — wherever Realtor.ca has coverage.

#### 4. PropTech inventory + price monitoring

**A Canadian housing dashboard or price-trend product running weekly snapshots.**

**Input:** Polygon or city, scheduled weekly via Apify's built-in cron
**Output:** Deltas in inventory count, median list price, $/sqft week-over-week
**Use:** Feed Canadian housing dashboards, price-trend product features, investor newsletters, or real-time alerting for inventory spikes / price drops.

#### 5. Investment-property due diligence

**An out-of-town buyer or institutional acquirer running deal-by-deal due diligence.**

**Input:** Property ID, full address, or Realtor.ca URL via `lookup` mode
**Output:** Full detail block — agents, property history, open house schedule, mortgage calculator deep links, building features (basement, exterior finish, appliances, fire protection, amenities), land features (frontage, depth, sewer)
**Use:** Skip the manual Realtor.ca click-through. Pull every public data point on a property into your DD spreadsheet or CRM in one API call.

#### 6. Rental market sizing for STR / LTR operators

**A long-term or short-term rental operator entering a new Canadian market.**

**Input:** City + province with `listingType=rent`
**Output:** Active rental inventory, rent distribution, beds / baths / sqft breakdowns, top neighbourhoods
**Use:** Compute total addressable rental supply, identify undersupplied bedroom counts, set competitive rent benchmarks before signing a lease or buying a multi-family.

***

### 🔗 Integration Examples

#### JavaScript / Node.js

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });

const run = await client.actor('sian.agency/realtor-ca-property-scraper').call({
  searchMode: 'location',
  location: 'Toronto, ON',
  listingType: 'sale',
  maxResults: 200
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].listPriceCad, items[0].pricePerSqft, items[0].address.full);
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')

run = client.actor('sian.agency/realtor-ca-property-scraper').call(run_input={
    'searchMode': 'location',
    'location': 'Vancouver, BC',
    'listingType': 'sale',
    'maxResults': 100,
})

for item in client.dataset(run['defaultDatasetId']).iterate_items():
    print(item['address']['full'], item.get('listPriceCad'), item.get('pricePerSqft'))
```

#### cURL

```bash
curl -X POST 'https://api.apify.com/v2/acts/sian.agency~realtor-ca-property-scraper/runs?token=YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"searchMode": "location", "location": "Montréal, QC", "listingType": "sale", "maxResults": 150}'
```

#### Automation Workflows (N8N / Zapier / Make)

1. **Trigger**: Weekly schedule (Mondays 06:00 ET) or webhook from your CRM
2. **HTTP Request**: Call the actor with your saved polygon or city list
3. **Process**: Read the dataset, compute deltas vs. the previous run
4. **Action**: Push new listings / price-drop alerts to Slack, email, a Notion database, or your investor portal

***

### 📊 Performance & Pricing

#### FREE Tier (Try It Now)

- **25 listings per run** — full feature access, identical data quality
- All 6 search modes available
- No credit card required
- Perfect for testing a Canadian market before you commit

#### PAID Tier (Production Ready)

- **Unlimited** listings per run
- Bulk inputs (multiple cities, postal codes, URLs, property IDs in one run)
- Volume-tiered pricing — the more you scrape, the cheaper each listing becomes
- Pay-per-event: only charged for listings successfully extracted

**Headline pricing — `ListingExtracted` event:**

| Tier | Price / listing |
|---|---|
| FREE | $0.030 |
| BRONZE ($20 spend) | $0.005 |
| SILVER ($100 spend) | $0.004 |
| **GOLD ($500 spend)** | **$0.003** |
| PLATINUM ($2,000 spend) | $0.003 |
| DIAMOND ($5,000 spend) | $0.003 |

Plus `apify-actor-start` at a flat **$0.005 per run** (charged once after input validation passes).

💰 **Best price on the market at scale** — Canadian community scrapers charge $5–15 per 1,000 results with no volume curve. At GOLD floor ($0.003 × 1,000 = $3.00), we beat every alternative on the headline event and ship full single-property lookup data on the same row price.

🔗 [View live pricing on the actor page](https://apify.com/sian.agency/realtor-ca-property-scraper?fpr=sian)

***

### ❓ Frequently Asked Questions

**Q: How many listings can I process?**
A: FREE tier: 25 per run. PAID tier: unlimited. The tier curve drops the per-listing price at $20, $100, $500, $2,000, and $5,000 of lifetime spend on the actor.

**Q: Sale + rent — how is the split handled?**
A: Realtor.ca returns sale and rent listings in one mixed feed. We apply a client-side filter via the `listingType` input (`sale` / `rent` / `both`). The filter runs after the upstream response so you can pull both sides in one run if you want a full inventory snapshot.

**Q: Does this scrape sold-price data?**
A: No. Realtor.ca hides sold price and sold date on the consumer-facing website. This scraper only exposes what Realtor.ca makes publicly visible, so sold price/date is not returned. For sold data, you need an MLS board membership or the agent-side MLS feed.

**Q: How do I search by Canadian postal code?**
A: Set `searchMode=postalCode` and pass any Canadian postal in `A1A 1A1` format. The space is optional — `"M5V 2H1"` and `"M5V2H1"` are both accepted, and we normalize to the spaced format internally. Do NOT pass US 5-digit ZIP codes; they will return zero results.

**Q: What's the difference between coordinates and polygon mode?**
A: `coordinates` mode uses a circle: centre point (lat/lon) + radius in km. `polygon` mode uses an arbitrary closed shape: at least 3 lat/lon points. Use coordinates for "everything within 5 km of downtown"; use polygon for irregular shapes like a riverfront strip, a ski-resort zone, or a custom neighbourhood boundary.

**Q: What does single-property lookup return that search modes don't?**
A: The `lookup` mode hits Realtor.ca's full detail endpoint and returns agents (name, phone, email, brokerage), property history (past list dates + price changes), open house schedule, mortgage calculator deep links, full building features (basement, exterior finish, appliances, fire protection, amenities), and land features (frontage, depth, sewer). Search modes give you the listing summary; lookup gives you the full DD packet.

**Q: How are `"5 + 1"` bedrooms handled?**
A: Realtor.ca uses CA-specific strings like `"5 + 1"` (5 above-grade + 1 in the basement). We parse these into three numeric fields: `specs.beds` (total = 6), `specs.bedsAboveGrade` (5), and `specs.bedsBelowGrade` (1). You can sort, filter, and aggregate without any string-parsing on your side.

**Q: What about metric vs imperial sizes?**
A: Realtor.ca returns Quebec listings in `m²` and other provinces in `sqft`. The `specs.sizeInterior` object has `{ value, unit, sqftDerived }` — `sqftDerived` is always normalized to imperial sqft (1 m² = 10.7639 sqft) so you can run cross-province KPIs without unit conversion.

**Q: What output formats are available?**
A: JSON, CSV, Excel, RSS, JSONL, and XML — export directly from the Apify dataset UI or via the API.

**Q: Is this legal?**
A: Yes — we only extract publicly available Realtor.ca data. See our [legal section](#%EF%B8%8F-is-it-legal-to-scrape-data) below.

**Q: How long does processing take?**
A: ~1–2 seconds per listing in search modes (50 per page, auto-paginated). Single-property `lookup` mode is ~2 seconds per ID. A 200-listing Toronto run completes in 3–5 minutes.

***

### 🐞 Troubleshooting

**The dataset is empty after a successful run.**

- Check that `searchMode` matches the input field you provided (e.g. `location` mode needs `location`; `postalCode` mode needs `postalCode`; `lookup` mode needs `lookupBy` + the matching ID/address/URL).
- On FREE tier, only 25 results return per run — if your search has fewer than 25 matches, that's the full set.
- If `listingType=sale` and the market is rentals-heavy (e.g. downtown Montréal condo blocks), try `listingType=both` to see the unfiltered feed.

**Postal code search returns 0 results.**

- Ensure you're using the Canadian `A1A 1A1` format (one letter, one digit, one letter, optional space, one digit, one letter, one digit). Not a US 5-digit ZIP.
- The space is optional — `"M5V2H1"` and `"M5V 2H1"` both work; we normalize internally.
- Some Quebec rural postal codes have very low Realtor.ca coverage; try a coarser search via `coordinates` or `location` mode.

**Polygon mode returns 0 results.**

- Polygons need **at least 3 distinct points**.
- Format is `"lon lat,lon lat,..."` — **longitude first, latitude second**, space-separated within a pair, comma-separated between pairs. This is the opposite order from most "lat,lng" conventions, so double-check before debugging further.
- Polygons should be roughly closed (we don't require an explicit closing point; Realtor.ca closes implicitly).

**Bedrooms field looks weird or is missing.**

- Realtor.ca strings like `"5 + 1"` mean 5 above-grade + 1 in the basement. Don't read the raw upstream string — read our parsed fields: `specs.beds` (total), `specs.bedsAboveGrade`, `specs.bedsBelowGrade`.
- For commercial listings, beds is often `null`; check `propertyType` first.

**Size is shown in `m²` instead of sqft.**

- Quebec listings come from Realtor.ca in metric. Use `specs.sizeInterior.sqftDerived` — it's always normalized to imperial sqft regardless of the upstream unit (1 m² = 10.7639 sqft).
- The original value and unit are preserved in `specs.sizeInterior.value` and `specs.sizeInterior.unit` if you need them.

***

### ⚖️ Is it legal to scrape data?

Our actors are ethical and do not extract any private user data, such as email addresses, gender, or location. They only extract what the user has chosen to share publicly. We therefore believe that our actors, when used for ethical purposes by Apify users, are safe.

However, you should be aware that your results could contain personal data. Personal data is protected by the **GDPR** in the European Union and by Canadian regulations such as **PIPEDA** (Personal Information Protection and Electronic Documents Act) and provincial equivalents (Quebec's Law 25, British Columbia's PIPA, Alberta's PIPA). You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

You can also read Apify's blog post on the [legality of web scraping](https://blog.apify.com/is-web-scraping-legal/).

***

### ⚠️ Trademark Disclaimer

**Realtor.ca®** and **REALTOR®** are registered trademarks of **The Canadian Real Estate Association (CREA)**. This Apify Actor is an **independent third-party scraping tool**. It is **not affiliated with, endorsed by, sponsored by, or connected to** Realtor.ca, CREA, or any of their subsidiaries. The Realtor.ca® name appears in this listing under nominative fair use, solely to identify the public platform whose publicly available listing data this tool helps you collect.

This tool does not provide access to Realtor.ca's internal systems, private APIs, member-only MLS feeds, partner data, or proprietary services. Users are responsible for ensuring their use complies with Realtor.ca's Terms of Service, CREA's policies, and all applicable Canadian federal and provincial laws (including PIPEDA, Quebec's Law 25, BC PIPA, and Alberta PIPA).

***

### 🤝 Support

[![Telegram Support](https://img.shields.io/badge/Telegram-Support%20Group-0088cc?logo=telegram)](https://t.me/+vyh1sRE08sAxMGRi)

**Join our active support community**

- For issues or questions, open an issue in the actor's repository
- Check the [SIÁN Agency Store](https://apify.com/sian.agency?fpr=sian) for more automation tools
- 📧 <apify@sian-agency.online>

***

**Built by [SIÁN Agency](https://www.sian-agency.online)** | **[More Tools](https://apify.com/sian.agency?fpr=sian)**

# Actor input Schema

## `searchMode` (type: `string`):

How to specify the search.

• **location** — by city + province (e.g. 'Toronto, ON')
• **postalCode** — by Canadian postal code (A1A 1A1)
• **coordinates** — by lat/lon center + radius (km)
• **polygon** — by polygon boundary
• **url** — by Realtor.ca search URL
• **lookup** — single property by ID, address, or URL (returns full details with agents, history, mortgage links)

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

Filter by transaction type. Realtor.ca returns sale + rent in one feed; this filter is applied client-side on the response.

• **both** — keep every listing (default)
• **sale** — only 'For sale' listings
• **rent** — only 'For rent' listings

## `maxResults` (type: `integer`):

Hard cap on listings returned per query. Auto-paginates until the cap is reached or the search is fully drained. FREE tier is always capped at 25 listings per run.

## `location` (type: `string`):

City + province, e.g. 'Toronto, ON', 'Vancouver, BC', 'Montréal, QC'.

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

Array of 'City, Province' strings. PAID tier only.

## `postalCode` (type: `string`):

Canadian postal code, e.g. 'M5V 2H1' or 'L4L2P3' (with or without space).

## `postalCodes` (type: `array`):

Array of Canadian postal codes. PAID tier only.

## `latitude` (type: `string`):

Latitude in decimal degrees, e.g. 43.6532 (Toronto).

## `longitude` (type: `string`):

Longitude in decimal degrees, e.g. -79.3832 (Toronto).

## `radius` (type: `string`):

Optional search radius in kilometres (0.1–80). Defaults to provider default.

## `polygon` (type: `string`):

Polygon boundary as 'lon lat,lon lat,...'. Min 3 points. Example: '-79.45 43.65,-79.40 43.65,-79.40 43.70,-79.45 43.70,-79.45 43.65'.

## `realtorUrl` (type: `string`):

Realtor.ca search URL (for url mode) or property URL (for lookup mode).

## `realtorUrls` (type: `array`):

Array of Realtor.ca URLs. PAID tier only.

## `lookupBy` (type: `string`):

How to identify the property to look up.

• **propertyId** — Realtor.ca numeric property ID
• **address** — full street address
• **url** — Realtor.ca property URL

## `propertyId` (type: `string`):

Realtor.ca numeric property ID, e.g. '29768213'.

## `propertyIds` (type: `array`):

Array of Realtor.ca property IDs for bulk lookup. PAID tier only.

## `address` (type: `string`):

Full street address, e.g. '44 Wallace Street, Vaughan, ON'.

## Actor input object example

```json
{
  "searchMode": "location",
  "listingType": "both",
  "maxResults": 50,
  "location": "Toronto, ON",
  "postalCode": "M5V 2H1",
  "latitude": "43.6532",
  "longitude": "-79.3832",
  "radius": "2",
  "lookupBy": "propertyId"
}
```

# Actor output Schema

## `results` (type: `string`):

Structured Realtor.ca listings (sale + rent) with calculated price/sqft and (lookup mode) full property details: agents, history, mortgage links, building features.

## `htmlReport` (type: `string`):

HTML summary with run stats, market KPIs (median price CAD, $/sqft distribution), city + province + property-type breakdowns, and per-query totals.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("sian.agency/realtor-ca-property-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("sian.agency/realtor-ca-property-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 '{}' |
apify call sian.agency/realtor-ca-property-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=sian.agency/realtor-ca-property-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Realtor.ca Property Scraper — Canada · Market KPIs",
        "description": "Scrape Realtor.ca listings nationwide — sale + rent, by city/province, postal code, coordinates, polygon, or URL. Built-in market KPIs (CAD), property lookup with agents, history, mortgage links. Independent tool — not affiliated with Realtor.ca or CREA.",
        "version": "1.0",
        "x-build-id": "A0uLeb0jFMI66DbpR"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/sian.agency~realtor-ca-property-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-sian.agency-realtor-ca-property-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/sian.agency~realtor-ca-property-scraper/runs": {
            "post": {
                "operationId": "runs-sync-sian.agency-realtor-ca-property-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/sian.agency~realtor-ca-property-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-sian.agency-realtor-ca-property-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchMode": {
                        "title": "🧭 Search Mode",
                        "enum": [
                            "location",
                            "postalCode",
                            "coordinates",
                            "polygon",
                            "url",
                            "lookup"
                        ],
                        "type": "string",
                        "description": "How to specify the search.\n\n• **location** — by city + province (e.g. 'Toronto, ON')\n• **postalCode** — by Canadian postal code (A1A 1A1)\n• **coordinates** — by lat/lon center + radius (km)\n• **polygon** — by polygon boundary\n• **url** — by Realtor.ca search URL\n• **lookup** — single property by ID, address, or URL (returns full details with agents, history, mortgage links)",
                        "default": "location"
                    },
                    "listingType": {
                        "title": "🏷 Listing Type",
                        "enum": [
                            "both",
                            "sale",
                            "rent"
                        ],
                        "type": "string",
                        "description": "Filter by transaction type. Realtor.ca returns sale + rent in one feed; this filter is applied client-side on the response.\n\n• **both** — keep every listing (default)\n• **sale** — only 'For sale' listings\n• **rent** — only 'For rent' listings",
                        "default": "both"
                    },
                    "maxResults": {
                        "title": "📊 Max results per query",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Hard cap on listings returned per query. Auto-paginates until the cap is reached or the search is fully drained. FREE tier is always capped at 25 listings per run.",
                        "default": 50
                    },
                    "location": {
                        "title": "📍 Location  (used when Search Mode = location)",
                        "type": "string",
                        "description": "City + province, e.g. 'Toronto, ON', 'Vancouver, BC', 'Montréal, QC'."
                    },
                    "locations": {
                        "title": "📋 Bulk Locations  (PAID, location mode)",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Array of 'City, Province' strings. PAID tier only.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "postalCode": {
                        "title": "📮 Postal Code  (used when Search Mode = postalCode)",
                        "type": "string",
                        "description": "Canadian postal code, e.g. 'M5V 2H1' or 'L4L2P3' (with or without space)."
                    },
                    "postalCodes": {
                        "title": "📋 Bulk Postal Codes  (PAID, postalCode mode)",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Array of Canadian postal codes. PAID tier only.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "latitude": {
                        "title": "🧭 Latitude  (used when Search Mode = coordinates)",
                        "type": "string",
                        "description": "Latitude in decimal degrees, e.g. 43.6532 (Toronto)."
                    },
                    "longitude": {
                        "title": "🧭 Longitude",
                        "type": "string",
                        "description": "Longitude in decimal degrees, e.g. -79.3832 (Toronto)."
                    },
                    "radius": {
                        "title": "📏 Radius (km)",
                        "type": "string",
                        "description": "Optional search radius in kilometres (0.1–80). Defaults to provider default."
                    },
                    "polygon": {
                        "title": "📐 Polygon  (used when Search Mode = polygon)",
                        "type": "string",
                        "description": "Polygon boundary as 'lon lat,lon lat,...'. Min 3 points. Example: '-79.45 43.65,-79.40 43.65,-79.40 43.70,-79.45 43.70,-79.45 43.65'."
                    },
                    "realtorUrl": {
                        "title": "🔗 Realtor.ca URL  (url or lookup mode)",
                        "type": "string",
                        "description": "Realtor.ca search URL (for url mode) or property URL (for lookup mode)."
                    },
                    "realtorUrls": {
                        "title": "📋 Bulk URLs  (PAID, url mode)",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Array of Realtor.ca URLs. PAID tier only.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "lookupBy": {
                        "title": "🔎 Lookup By  (used when Search Mode = lookup)",
                        "enum": [
                            "propertyId",
                            "address",
                            "url"
                        ],
                        "type": "string",
                        "description": "How to identify the property to look up.\n\n• **propertyId** — Realtor.ca numeric property ID\n• **address** — full street address\n• **url** — Realtor.ca property URL",
                        "default": "propertyId"
                    },
                    "propertyId": {
                        "title": "🆔 Property ID  (lookup mode, lookupBy=propertyId)",
                        "type": "string",
                        "description": "Realtor.ca numeric property ID, e.g. '29768213'."
                    },
                    "propertyIds": {
                        "title": "📋 Bulk Property IDs  (PAID, lookup mode)",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Array of Realtor.ca property IDs for bulk lookup. PAID tier only.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "address": {
                        "title": "🏠 Address  (lookup mode, lookupBy=address)",
                        "type": "string",
                        "description": "Full street address, e.g. '44 Wallace Street, Vaughan, ON'."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
