# Realtor.ca Scraper — Canada Sale, Rent & Sold (`crawloop/realtor-ca-scraper`) Actor

Scrape Realtor.ca listings across Canada for sale, rent, or sold. Each row includes price, beds, baths, MLS number, address, photos, and agent phone. JSON export for Python, Node.js, and MCP.

- **URL**: https://apify.com/crawloop/realtor-ca-scraper.md
- **Developed by:** [Andrej Kiva](https://apify.com/crawloop) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 68.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 listings

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

> **Disclaimer:** Unofficial tool — not affiliated with, sponsored by, or endorsed by the Canadian Real Estate Association (CREA), REALTOR.ca, or their affiliates. Data is read from publicly accessible listing search results and listing pages only. No login. You are responsible for complying with applicable law (including privacy law where a row includes an agent name or phone) and the site’s terms. No warranty on accuracy or availability. Provided for informational and research use.

Search Canadian property listings and export price, beds, baths, MLS number, address, photos, and the listing agent’s public phone. This Realtor.ca scraper is an API alternative for market checks, rent comps, and sold-listing pulls. Run it from Python, Node.js, or MCP and save JSON.

| Canada listings | US listings |
| :--- | :--- |
| Realtor.ca Scraper ◄── you are here | [Realtor.com Scraper](https://apify.com/crawloop/realtor-com-scraper) |

### When to use

- Pull for-sale, for-rent, or sold listings for a Canadian city
- Filter by price, beds, and baths before the rows hit your dataset
- Grab MLS number, brokerage, and the public agent phone from the search card
- Open listing details when you also need tax, maintenance, and year built

Use [Realtor.com Scraper](https://apify.com/crawloop/realtor-com-scraper) when the next step is US sold comps or a ZIP search.

### Key features

- City name, map or city URL, or a latitude/longitude radius
- Sale, rent, and sold, with optional sold-within-days
- Price, beds, baths, property type, and keyword filters
- MLS number, address, photos, brokerage, and public agent phone
- Optional listing detail for tax, maintenance fee, and year built
- HTTP-first at 512 MB on Apify; Camoufox clears Cloudflare when curl alone is blocked

### Input

| Field | Required | Description |
| :--- | :--- | :--- |
| `location` | One of these | City or neighbourhood, such as `Toronto` or `Vancouver, BC` |
| `startUrls` | One of these | City page, map link, or one listing URL |
| `latitude` / `longitude` | One of these | Center point. Replaces `location` when both are set |
| `distanceKm` | No | Radius for coordinates. Default 20 |
| `operation` | No | `buy`, `rent`, or `sold`. Default `buy` |
| `propertyType` | No | Residential, condo, vacant land, recreational, multi-family, agricultural, or parking |
| `sortBy` | No | `newest`, `oldest`, `lowPrice`, or `highPrice` |
| `maxItems` | No | Stop after this many rows. Default 20, maximum 1000. One area returns about 600 |
| `minPrice` / `maxPrice` | No | CAD bounds. 0 means no bound |
| `minBeds` / `maxBeds` | No | Bedroom bounds. 0 means any |
| `minBaths` / `maxBaths` | No | Bathroom bounds. 0 means any |
| `keywords` | No | Extra search words |
| `soldWithinDays` | No | Used when `operation` is `sold` |
| `openHousesOnly` | No | Keep open-house listings. Not applied to sold |
| `includeDetails` | No | Add tax, maintenance, year built, and the longer description |
| `proxyConfiguration` | No | Used after a direct request is blocked. Canada residential is the cloud path |

#### Input example

```json
{
  "location": "Toronto",
  "operation": "buy",
  "minBeds": 2,
  "maxPrice": 900000,
  "maxItems": 20,
  "includeDetails": false
}
```

Coordinate search:

```json
{
  "latitude": 49.2827,
  "longitude": -123.1207,
  "distanceKm": 15,
  "operation": "rent",
  "maxItems": 50
}
```

### Output

One dataset row per listing.

| Field | Description |
| :--- | :--- |
| `listingId` | Public listing id |
| `mlsNumber` | MLS number |
| `url` | Listing URL |
| `operation` | `buy`, `rent`, or `sold` |
| `price` | Numeric price in CAD |
| `priceText` | Price as shown on the card |
| `currency` | `CAD` |
| `propertyType` | Type on the card |
| `address` | Street, city, and province |
| `city` | City parsed from the address |
| `province` | Province or territory |
| `postalCode` | Postal code when the card has one |
| `latitude` / `longitude` | Map point |
| `bedrooms` / `bathrooms` | Counts from the card |
| `sizeInterior` | Interior size text |
| `brokerage` | Brokerage name |
| `agentName` | Listing agent |
| `agentPhone` | Public agent phone |
| `publicRemarks` | Public description |
| `photoUrls` | Up to 20 photo URLs |
| `photoCount` | How many photo URLs were saved |
| `openHouse` | True when the card lists an open house |
| `timeOnRealtor` | How long the listing has been public |
| `maintenanceFee` / `taxAmount` / `yearBuilt` | Filled when `includeDetails` is on |
| `scrapedAt` | UTC timestamp |

#### Output example

```json
{
  "listingId": "30362405",
  "mlsNumber": "C13859784",
  "url": "https://www.realtor.ca/real-estate/30362405/635-glenholme-avenue",
  "operation": "buy",
  "price": 995000,
  "currency": "CAD",
  "city": "Toronto",
  "province": "Ontario",
  "bedrooms": "3 + 1",
  "bathrooms": "2",
  "brokerage": "Example Brokerage",
  "agentName": "Ada Agent",
  "agentPhone": "416-5550100"
}
```

### Use cases

- Compare asking prices and bedroom counts for one city before a trip
- Export rent listings around a job site from a coordinate radius
- Pull recent sold rows with `operation` set to `sold` and `soldWithinDays`
- Hand a shortlist to a CRM with MLS number, brokerage, and public phone

### Integration examples

Replace `YOUR_TOKEN` with an Apify API token. The store id is `crawloop/realtor-ca-scraper`.

#### Node.js

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

const client = new ApifyClient({ token: "YOUR_TOKEN" });
const run = await client.actor("crawloop/realtor-ca-scraper").call({
  location: "Toronto",
  operation: "buy",
  maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("crawloop/realtor-ca-scraper").call(run_input={
    "location": "Toronto",
    "operation": "buy",
    "maxItems": 20,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

#### cURL

```bash
curl "https://api.apify.com/v2/acts/crawloop~realtor-ca-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"location":"Toronto","operation":"buy","maxItems":20}'
```

### MCP and AI assistants

Use this Actor from AI tools via [Apify MCP](https://docs.apify.com/platform/integrations/mcp).
Connect your Apify account, then call this Actor by its store id `crawloop/realtor-ca-scraper`.

Example prompts:

- "Run Realtor.ca Scraper for Toronto for-sale listings, max 20, and return price, beds, and MLS number as JSON"
- "Scrape Vancouver rentals within a 15 km radius of downtown and summarize median asking rent"
- "Chain Realtor.ca Scraper for Canadian listings, then Realtor.com Scraper for US sold comps in the same brief"

### Suite next step

After a Canadian shortlist, run [Realtor.com Scraper](https://apify.com/crawloop/realtor-com-scraper) for US sold comps, ZIP searches, and listing monitors.

### FAQ

#### Does this cover sold listings?

Yes. Set `operation` to `sold`. Add `soldWithinDays` when you only want recent closings. Sold coverage depends on what the public search returns for that area.

#### How many listings can one city return?

One search area stops around 600 rows, newest first when you sort by newest. Raise `maxItems` up to 1000 if you want the full window the search will return.

#### Do I get the agent phone without opening every listing?

The search card includes the public agent name and phone when the listing shows them. Turn on `includeDetails` for tax, maintenance fee, year built, and a longer description.

#### Is this a Realtor.ca API?

It is an API alternative. Send a city, a map link, or coordinates and download JSON. Call it from Python, Node.js, cURL, or an AI assistant over MCP. No login and no browser.

#### Which places can I search?

Any Canadian city the public search resolves, including Toronto, Vancouver, Calgary, and Montreal. Use `Vancouver, BC` when the place name is shared. A coordinate radius covers a job site or a neighbourhood the city page does not name.

# Actor input Schema

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

Canadian city or neighbourhood, such as Toronto or Vancouver, BC. Ignored when latitude and longitude are set.

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

Realtor.ca city pages, map links, or a single listing URL.

## `latitude` (type: `number`):

Search center latitude. Must be sent with longitude.

## `longitude` (type: `number`):

Search center longitude. Must be sent with latitude.

## `distanceKm` (type: `integer`):

Radius around the coordinates.

## `operation` (type: `string`):

For sale, for rent, or sold.

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

Residential is the default public search.

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

Order of the search results.

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

Stop after this many saved rows. One area returns about 600 listings.

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

Minimum price in CAD. 0 means no minimum.

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

Maximum price in CAD. 0 means no maximum.

## `minBeds` (type: `integer`):

Minimum bedrooms. 0 means any.

## `maxBeds` (type: `integer`):

Maximum bedrooms. 0 means any.

## `minBaths` (type: `integer`):

Minimum bathrooms. 0 means any.

## `maxBaths` (type: `integer`):

Maximum bathrooms. 0 means any.

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

Optional words matched by the public search.

## `soldWithinDays` (type: `integer`):

Used when operation is sold. 0 means no day limit.

## `openHousesOnly` (type: `boolean`):

Keep listings that have an open house. Not applied to sold searches.

## `includeDetails` (type: `boolean`):

Open each listing for tax, maintenance fee, year built, and the full public description.

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

Canada residential is used by default on Apify. Direct requests are tried locally when proxy is off. You pay platform usage for residential proxy.

## Actor input object example

```json
{
  "location": "Toronto",
  "distanceKm": 20,
  "operation": "buy",
  "propertyType": "residential",
  "sortBy": "newest",
  "maxItems": 8,
  "minPrice": 0,
  "maxPrice": 0,
  "minBeds": 0,
  "maxBeds": 0,
  "minBaths": 0,
  "maxBaths": 0,
  "soldWithinDays": 0,
  "openHousesOnly": false,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CA"
  }
}
```

# Actor output Schema

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

Default dataset items — one listing per row.

# 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 = {
    "location": "Toronto",
    "maxItems": 8
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawloop/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 = {
    "location": "Toronto",
    "maxItems": 8,
}

# Run the Actor and wait for it to finish
run = client.actor("crawloop/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 '{
  "location": "Toronto",
  "maxItems": 8
}' |
apify call crawloop/realtor-ca-scraper --silent --output-dataset

```

## MCP server setup

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