# Realtor.ca Scraper| 💰 $0.5 / 1K (`solidcode/realtorca-scraper`) Actor

\[💰 $0.5 / 1K] Extract property listings and real estate agent profiles from Realtor.ca. Search by location, filter by price, property type, bedrooms, and more. Collect detailed property data, photos, agent contact info, and social media links across Canada.

- **URL**: https://apify.com/solidcode/realtorca-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 64 total users, 9 monthly users, 46.6% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

## How to integrate an Actor?

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

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

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

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

# README

## Realtor.ca Scraper

Pull MLS-grade property listings and licensed real-estate agent profiles from Realtor.ca — asking prices, full Canadian addresses, GPS coordinates, building amenities, parking, brokerage details, agent phone numbers and social links, across every province and territory. One actor covers for-sale listings, rentals, and the realtor directory, so market analytics and agent prospecting run from a single configuration. Built for Canadian real estate investors, mortgage brokers, brokerage data teams, and CRM platforms that need current Realtor.ca data without building and maintaining a collection pipeline of their own.

### Why This Scraper?

- **Two record types, one actor** — flip the `mode` dropdown between **Property Listings** and **Real Estate Agents**. Listings and the licensed-agent directory come from the same run configuration, with no second actor or second subscription.
- **All 13 provinces and territories** — search any Canadian city or region by name, from Toronto to Whitehorse. No per-province setup and no hand-built map coordinates.
- **Sale and rent in one schema, never confused** — every property row carries a `priceType` of `sale` or `rent` (100% of rows), so a rental row showing "$2,500/Monthly" can never be mistaken for a purchase price when you merge buy and rent runs into one table.
- **Compound bedroom counts handled the way Ontario boards write them** — a "3 + 1" listing arrives as `bedrooms: 4` for filtering *and* `bedroomsText: "3 + 1"` so you keep the above-grade / below-grade split. On a Toronto run, 157 of 345 rows use the compound form.
- **Both the Realtor.ca listing ID and the MLS number on every property** — 100% fill on both, so rows join straight back to KW Command, Lone Wolf, or your own brokerage feed with no manual cross-walk.
- **Building amenities as the board published them, not a checkbox** — full strings like "Security/Concierge, Recreation Centre, Exercise Centre, Party Room, Storage - Locker". It is a condo-market field, published board by board, and we measured it: 63% of Toronto rows, 43% of Vancouver's, 13% of Calgary's, and none in Montreal or Winnipeg, so you know which markets carry the column before you run.
- **Agent contact block that actually contains contacts** — direct phone (99% of agent rows), full office street address (100%), personal website (43%), plus the agent's team or corporation name where they publish one.
- **Four social profiles per agent** — Facebook, Instagram, LinkedIn, and Twitter/X URLs lifted straight from the agent's Realtor.ca card.
- **Dense metros are not capped at 600** — Realtor.ca limits any single query to 600 listings; this actor splits a busy city into progressively smaller map areas, up to 6 levels deep, so Toronto and downtown Vancouver keep producing past that ceiling.

### Use Cases

**Real Estate Investment & Analysis**

- Compare asking prices across Canadian metros (Toronto vs. Calgary vs. Halifax)
- Track time-on-market by neighbourhood to spot softening segments
- Filter rentals by `priceMax` and bedroom count to model gross yield
- Build comparable-listing tables grouped by postal-code prefix (FSA)

**Lead Generation for Real Estate Services**

- Build agent call lists by city with phone number, brokerage, and office address
- Prospect new licensees by re-running the Real Estate Agents mode each month
- Rank the highest-volume brokerages in a target market by office name
- Target team leaders specifically using the corporation / team name field

**Market Research & Data Enrichment**

- Track property-type mix (condos vs. detached vs. agricultural) by region
- Map listings on a heatmap using the built-in GPS coordinates
- Enrich existing CRM rows with MLS number, listing URL, and listing-agent contact
- Measure amenity prevalence (concierge, gym, party room) across a condo market

**Relocation & Property Search**

- Aggregate listings from several cities into one spreadsheet
- Compare provinces side by side for cost-of-living research
- Watch one exact neighbourhood by pasting its map URL

### Getting Started

#### Search Properties in a City

Just a location — defaults handle the rest.

```json
{
    "mode": "properties",
    "location": "Toronto, ON",
    "maxResults": 100
}
```

#### Filtered Buy Search

Combine price, bedroom, and bathroom filters:

```json
{
    "mode": "properties",
    "location": "Vancouver, BC",
    "transactionType": "buy",
    "propertyType": "residential",
    "priceMin": 500000,
    "priceMax": 1500000,
    "bedroomsMin": 3,
    "bathroomsMin": 2,
    "maxResults": 200
}
```

#### Rental Hunt with Price Cap

```json
{
    "mode": "properties",
    "location": "Montreal, QC",
    "transactionType": "rent",
    "priceMax": 2500,
    "bedroomsMin": 2,
    "maxResults": 100
}
```

#### Find Real Estate Agents

```json
{
    "mode": "agents",
    "location": "Calgary, AB",
    "maxResults": 50
}
```

#### Custom Map Area

Zoom into a neighbourhood on Realtor.ca and paste the URL from your address bar:

```json
{
    "mode": "properties",
    "startUrls": [
        "https://www.realtor.ca/map#ZoomLevel=13&LatitudeMax=43.7&LongitudeMax=-79.3&LatitudeMin=43.6&LongitudeMin=-79.5"
    ],
    "maxResults": 500
}
```

### Input Reference

#### What to Scrape

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `mode` | string | `"properties"` | Choose **Property Listings** or **Real Estate Agents** |
| `location` | string | `"Toronto, ON"` | City and province, e.g. "Vancouver, BC". If both this and Start URLs are empty, the run falls back to Toronto, ON — it does not sweep the whole country |
| `startUrls` | string\[] | `[]` | Realtor.ca map URLs and agent directory URLs (e.g. `https://www.realtor.ca/realtors/on/toronto`). Single-listing pages are not supported. Overrides `location` when provided |
| `maxResults` | integer | `200` | Maximum results to collect. Set to `0` for everything available |

#### Property Filters

These apply only in property mode and are sent to Realtor.ca's own search, so you collect only matching rows.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `transactionType` | string | `"buy"` | **Buy** or **Rent** |
| `propertyType` | string | `"residential"` | **Residential**, **Commercial**, or **Farm / Agriculture** |
| `priceMin` | integer | — | Minimum price (CAD) |
| `priceMax` | integer | — | Maximum price (CAD) |
| `bedroomsMin` | integer | — | Minimum bedrooms (0–10) |
| `bathroomsMin` | integer | — | Minimum bathrooms (0–10) |

### Output

One row per result. **Property Listings** and **Real Estate Agents** have separate field sets.

#### Property Listing Example

```json
{
    "listingId": "27045321",
    "mlsNumber": "E9345812",
    "price": "$1,249,900",
    "priceNumeric": 1249900,
    "priceType": "sale",
    "address": "45 Broadview Ave",
    "city": "Toronto (Riverdale)",
    "province": "Ontario",
    "postalCode": "M4M2E4",
    "latitude": 43.6615,
    "longitude": -79.3522,
    "propertyType": "Single Family",
    "buildingType": "House",
    "ownershipType": "Freehold",
    "bedrooms": 5,
    "bedroomsText": "4 + 1",
    "bathrooms": 3,
    "sizeInterior": "1959.95 sqft",
    "stories": "2",
    "parkingSpaces": 2,
    "parkingType": "Garage",
    "amenities": "Exercise Centre, Party Room, Storage - Locker",
    "photos": [
        "https://cdn.realtor.ca/listing/TS638.../highres_1.jpg"
    ],
    "agentName": "Jane Smith",
    "agentPhone": "(416) 555-1234",
    "agentOffice": "Royal LePage Premium",
    "listingUrl": "https://www.realtor.ca/real-estate/27045321/45-broadview-ave-toronto",
    "timeOnMarket": "24 min ago",
    "listedDate": "2026-08-10T18:33:20Z"
}
```

##### Property Identity

| Field | Type | Description |
|-------|------|-------------|
| `listingId` | string | Unique Realtor.ca listing ID |
| `mlsNumber` | string | MLS listing number |
| `listingUrl` | string | Direct link to the listing on Realtor.ca |
| `propertyType` | string | "Single Family", "Vacant Land", "Agriculture", "Retail", etc. |
| `buildingType` | string | "House", "Apartment", "Row / Townhouse", etc. |
| `ownershipType` | string | "Freehold", "Condominium/Strata", etc. |
| `timeOnMarket` | string | How long the listing has been live, as Realtor.ca phrases it |
| `listedDate` | string | First-listed date and time (ISO 8601) |

##### Address & Location

| Field | Type | Description |
|-------|------|-------------|
| `address` | string | Street address, including unit number where the board publishes one |
| `city` | string | City or municipality. Quebec and Ontario boards often append the neighbourhood, e.g. "Toronto (Riverdale)" |
| `province` | string | Province or territory name |
| `postalCode` | string | Canadian postal code |
| `latitude` | number | GPS latitude |
| `longitude` | number | GPS longitude |

##### Price

| Field | Type | Description |
|-------|------|-------------|
| `price` | string | Price as displayed. Rentals keep their "/Monthly" suffix |
| `priceNumeric` | integer | Same value as a number, for sorting and charting |
| `priceType` | string | `sale` or `rent` — which kind of price this row carries |

##### Size, Rooms & Building

| Field | Type | Description |
|-------|------|-------------|
| `bedrooms` | integer | Total bedrooms. Compound counts such as "4 + 1" are summed to 5 |
| `bedroomsText` | string | The count exactly as the board wrote it, e.g. "4 + 1" |
| `bathrooms` | integer | Total bathrooms, compound counts summed the same way |
| `sizeInterior` | string | Interior size with its unit. Boards publish either imperial ("1404 sqft") or metric ("139.3534 m2") |
| `stories` | string | Number of stories. Published by some boards only |
| `parkingSpaces` | integer | Total parking spaces |
| `parkingType` | string | "Underground", "Garage", "Visitor", etc. (multi-value) |
| `amenities` | string | Building amenities as published, e.g. "Exercise Centre, Party Room, Storage - Locker". Published board by board, so fill swings by market: 63% of Toronto rows, 43% of Vancouver's, 13% of Calgary's, and none in Montreal or Winnipeg |
| `photos` | string\[] | Listing cover photo at full resolution. Realtor.ca's search results carry the cover image only, so this is one URL per row |

##### Listing Agent

| Field | Type | Description |
|-------|------|-------------|
| `agentName` | string | Listing agent's name |
| `agentPhone` | string | Listing agent's phone, formatted |
| `agentOffice` | string | Listing brokerage / office name |

#### Agent Profile Example

```json
{
    "individualId": "1234567",
    "name": "Michael Chen",
    "position": "Salesperson",
    "phone": "416-555-6789",
    "office": "RE/MAX First Realty",
    "officeDesignation": "Brokerage",
    "corporationName": "MRKT Group",
    "officeAddress": "123 Bay St, Toronto, ON M5J 2T3",
    "officeLogo": "https://cdn.realtor.ca/organizations/.../lowres/1242191.jpg",
    "photo": "https://cdn.realtor.ca/individual/.../photo.jpg",
    "website": "https://www.michaelchenrealty.ca/",
    "profileUrl": "https://www.realtor.ca/agent/1234567/michael-chen-123-bay-st-toronto-ontario-m5j2t3",
    "facebook": "https://www.facebook.com/michaelchenrealty",
    "instagram": "https://www.instagram.com/michaelchenrealty",
    "linkedin": "https://www.linkedin.com/in/michael-chen-realty",
    "twitter": "https://www.twitter.com/mchenrealty"
}
```

##### Agent Identity & Contact

| Field | Type | Description |
|-------|------|-------------|
| `individualId` | string | Realtor.ca agent ID — always present |
| `name` | string | Agent's full name — always present |
| `profileUrl` | string | Agent's profile page on Realtor.ca — always present |
| `phone` | string | Direct phone number, on virtually every card |
| `website` | string | Agent's personal website, where they publish one (roughly 4 in 10) |
| `photo` | string | Agent's headshot URL, where they publish one. How many do varies by market, from about half a city's agents to nearly all of them |
| `position` | string | Role, e.g. "Salesperson" or "Broker". Published board by board — common in Ontario, absent in some Atlantic and Prairie markets |

##### Brokerage

| Field | Type | Description |
|-------|------|-------------|
| `office` | string | Brokerage / office name — always present |
| `officeAddress` | string | Office street address — always present |
| `officeLogo` | string | Office logo image URL, on most cards (7 to 8 in 10) |
| `corporationName` | string | The agent's own team or corporation name, e.g. "MRKT Group", where they registered one |
| `officeDesignation` | string | Brokerage designation. An Ontario-board field: filled on about 99% of Toronto and Ottawa cards and on almost none elsewhere in Canada |

##### Social Media

| Field | Type | Description |
|-------|------|-------------|
| `facebook` | string | Facebook profile URL |
| `instagram` | string | Instagram profile URL |
| `linkedin` | string | LinkedIn profile URL |
| `twitter` | string | Twitter / X profile URL |

### Tips for Best Results

- **Name one city per run.** "Toronto, ON" or "Vancouver, BC" finishes quickly. Leaving `location` empty does **not** sweep Canada — the run falls back to Toronto, so cover several regions by queueing one run per city or province, or by pasting several agent directory URLs into `startUrls`.
- **Set `maxResults` to 0 when you want a whole city.** A capped run in a large metro fills from one part of the map before moving on, so a 300-row cap on Toronto gives you a slice of the city rather than an even spread across it. Uncapped runs cover the full area.
- **Read `bedroomsText` before you trust a bedroom count in the GTA.** Ontario boards write "4 + 1" for a four-bedroom house with a finished basement bedroom. `bedrooms` gives you 5 for filtering, and `bedroomsText` tells you one of those rooms is below grade — a strong signal for in-law suites and rental potential.
- **Filter before you collect, not after.** `priceMin`, `priceMax`, `bedroomsMin`, and `bathroomsMin` go to Realtor.ca directly, so you pay only for matching rows instead of trimming a large dump on your side.
- **Use the map URL for a tight neighbourhood.** Zoom to the exact area on realtor.ca, copy the URL from your address bar, and paste it into `startUrls`. The map corners come straight from that link, so you get the polygon you drew and nothing outside it.
- **Split sale from lease with `priceType`.** If you merge a buy run and a rent run into one table, sort or group on `priceType` — `$2,500/Monthly` and `$2,500,000` sit in the same `price` column otherwise.
- **Key your CRM on `listingId`, join on `mlsNumber`.** MLS numbers get reused when a property re-lists; `listingId` stays unique per Realtor.ca record, so use both together as a composite key.

### Pricing

**From $0.50 per 1,000 results** — one flat rate for property rows and agent rows alike, and it is the same rate on every Apify discount tier, so a free-plan run costs exactly what a Gold-tier run costs.

| Results | No discount | Bronze | Silver | Gold |
|---------|-------------|--------|--------|------|
| 100 | $0.05 | $0.05 | $0.05 | $0.05 |
| 1,000 | $0.50 | $0.50 | $0.50 | $0.50 |
| 10,000 | $5.00 | $5.00 | $5.00 | $5.00 |
| 100,000 | $50.00 | $50.00 | $50.00 | $50.00 |

A "result" is one property row or one agent row in your dataset. There are no compute or time-based charges — you pay per result, plus a small fixed fee each time a run starts. Standard Apify platform fees apply on top.

### Integrations

Export results in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications when new listings appear
- **Webhooks** — Trigger downstream APIs on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

This actor is intended for legitimate real estate research, market analysis, and prospecting. You are responsible for complying with applicable Canadian privacy laws (PIPEDA and provincial equivalents), CASL (Canada's Anti-Spam Legislation) when contacting agents, and Realtor.ca's Terms of Service. Do not use collected data for spam, harassment, or any illegal purpose, and respect the personal information of agents and listing parties.

# Actor input Schema

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

Choose what to collect: property listings (homes, condos, etc.) or real estate agent profiles.

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

City or area to search, e.g. "Toronto, ON" or "Vancouver, BC". Leave empty only if you are using Start URLs below — if both are empty the run falls back to Toronto, ON. To cover several regions, run one search per city or province.

## `startUrls` (type: `array`):

Paste one or more Realtor.ca URLs directly. Supports map URLs (zoom into an area on realtor.ca and copy the address bar) and agent directory URLs such as https://www.realtor.ca/realtors/on/toronto. Single-listing pages are not supported. When provided, these override the Location field.

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

Search for properties to buy or to rent. Only applies when scraping property listings.

## `propertyType` (type: `string`):

Type of property to search for.

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

Only include properties at or above this price. Leave empty for no minimum.

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

Only include properties at or below this price. Leave empty for no maximum.

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

Only include properties with at least this many bedrooms. Leave empty for any.

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

Only include properties with at least this many bathrooms. Leave empty for any.

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

Maximum number of results to collect. Set to 0 to collect all available results.

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

Proxy settings. The default works well for Realtor.ca — switch groups only if you see a run come back empty.

## Actor input object example

```json
{
  "mode": "properties",
  "location": "Toronto, ON",
  "transactionType": "buy",
  "propertyType": "residential",
  "maxResults": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Table of scraped property listings or agent profiles.

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

Full details of each result with all fields.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "properties",
    "location": "Toronto, ON",
    "transactionType": "buy",
    "propertyType": "residential",
    "maxResults": 200,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/realtorca-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "properties",
    "location": "Toronto, ON",
    "transactionType": "buy",
    "propertyType": "residential",
    "maxResults": 200,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/realtorca-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "properties",
  "location": "Toronto, ON",
  "transactionType": "buy",
  "propertyType": "residential",
  "maxResults": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call solidcode/realtorca-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solidcode/realtorca-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/b7XDV1A8e7jCvSmz5/builds/1sehnfsaZ8Mc3s9IF/openapi.json
