# BC Assessment Property Values Scraper (`nice_dev/bcassessment-property-scraper`) Actor

Official BC Assessment values of any property in British Columbia (Vancouver and the whole province) from an address, PID, roll number, plan or Realtor.ca listing: assessed value, 10-year history, land and building, sales, comparables, asking price vs value.

- **URL**: https://apify.com/nice\_dev/bcassessment-property-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.72 / 1,000 properties

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

### 🏠 What is BC Assessment Property Values Scraper?

**BC Assessment Property Values Scraper** gives the **official BC Assessment value of any property in British Columbia** — Vancouver, Victoria, Kelowna, Surrey and every other town: **assessed value of the year and the year before, 10 years of values, land and building values, year built, rooms, floor area, PID, roll number, legal description, sales of the last 3 years and 9 comparable sales**. For a **[Realtor.ca](https://www.realtor.ca) listing**, it also gives the **asking price divided by the official value**.

Paste **addresses** (`202-1000 Beach Ave, Vancouver`), **PIDs**, **roll numbers**, **plan + lot**, **Realtor.ca listing URLs** or **MLS® numbers** — or search Realtor.ca by **city and filters** and get the official value of every listing — click **Start**, and download the results in JSON, CSV or Excel. No login, nothing to set up.&#x20;

### 📋 What data can you extract from BC Assessment?

One item per property, 115 fields:

| Category | What you get |
| --- | --- |
| 💰 **Official value** | assessed value of the year and the year before, the change, land value and building value — `938,000 CAD`, `2026 assessment as of July 1, 2025` |
| 📈 **10-year history** | the value of each of the last 10 years, with the change of the property and of its municipality |
| 🏠 **Property** | description (`Strata Apartment -Hi-Rise`), year built, bedrooms, bathrooms, floor area, land size, storeys, units |
| 🧾 **Identifiers** | PID, roll number, area and jurisdiction, legal description, plan and lot, BC Assessment page and photo |
| 🤝 **Sales** | sales of the last 3 years (date and price) and 9 comparable sold properties with their price, date and values |
| 🏘️ **Neighbours** | the 9 nearest properties with their total, land and building values |
| 🏷️ **Realtor.ca listing** | asking price, **asking price ÷ assessed value**, description, photos, days on market; with **Listing details** on: yearly property tax, strata fee, features, video, open houses, the full listing page |
| 📞 **Agents** | listing agents with their phone, website and brokerage |
| 🏛️ **City of Vancouver** (option) | yearly property tax levied and zoning district |

Every field, with an example, is listed in the **Output** section below.

### ✅ Why use BC Assessment Property Values Scraper?

- 🎯 **Any entry**: a free-text address, a PID, a roll number, a plan and lot, a Realtor.ca listing URL, an MLS® number, or a whole Realtor.ca search.
- 🏢 **The right unit**: for a condo, the value of the unit itself, never the building's by mistake — when the roll only has the whole building or lot, the row says so (`matchType` `wholeBuilding`) and gives no asking ÷ value ratio.
- 📉 **Find under-priced listings**: filter the Realtor.ca listings of a city on **asking price ÷ assessed value** (`0.95` = at least 5 % under the assessment).
- 📚 **10 years of values, land / building, 9 comparable sales** in the same row.
- 🗂️ **Several searches in one run**: keywords × cities, with a cap per search; big cities are covered in full, never cut at the 600 listings Realtor.ca shows.
- 🔔 **Monitoring built in**: tick **Only new results** and schedule the Actor: new listings only, or a property again once its new yearly assessment is out.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape BC Assessment

1. Create a free Apify account.
2. Open **BC Assessment Property Values Scraper** and paste **Addresses** (one per line, the unit first for a condo: `202-1000 Beach Ave, Vancouver`), or PIDs, roll numbers, plans, MLS® numbers.
3. Or paste Realtor.ca listing URLs or a Realtor.ca map search into **Realtor.ca or BC Assessment URLs**, or type a **Location** to get the listings of a city with their official value.
4. Set **Max results** (100 by default, 0 = no limit), then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API. Entries not found in the roll are listed, with the reason, in the `NOT_FOUND` record of the run's key-value store.

### 💰 How much does it cost to scrape BC Assessment?

This Actor uses **pay per event** pricing. Prices without a subscription (Bronze, Silver and Gold subscribers pay a little less — see the **Pricing** tab):

| Event | Price |
| --- | --- |
| Property delivered (official value, history, roll record) | $5.00 per 1,000 |
| Land / building split and neighbours (**Land / building split**, off by default) | $0.50 per 1,000 |
| Comparable sales (**Comparable sales**, off by default) | $0.50 per 1,000 |
| Property that comes from a Realtor.ca listing | $0.45 per 1,000 |
| Realtor.ca listing page (**Listing details**, off by default) | $0.75 per 1,000 |
| City of Vancouver tax and zoning (option) | $0.065 per 1,000 |
| Listing or property dropped by your filters | $0.15 per 1,000 |
| Run start | $0.001 per run |

Examples with the default options: **1,000 addresses or PIDs ≈ $5.00**; **1,000 Realtor.ca listings of a city with their official value ≈ $5.45**. An entry the roll has no property for is never charged, and filtering never costs more than taking everything. Platform usage (compute, proxy) is included in the price.

Speed, measured on the Apify platform: about **37 properties a minute** for Realtor.ca listings with their listing page and land / building values, comparable sales off (1,000 listings of Vancouver in 27 minutes); 200 PIDs without options in under 2 minutes, with land / building values and comparable sales in under 6 minutes. Each option adds time.

### ⚙️ Input

```json
{
    "addresses": ["202-1000 Beach Ave, Vancouver", "1000 Douglas St, Victoria"],
    "pids": ["017-580-285"],
    "rollNumbers": ["09-200-030-617-121-24-0140"],
    "maxItems": 100
}
```

The Realtor.ca listings of a city, with their official value, only those listed under their assessment:

```json
{
    "location": "Vancouver",
    "radiusKm": 10,
    "buildingType": "apartment",
    "maxPrice": 1500000,
    "maxAskingToAssessedRatio": 0.95,
    "maxItems": 200
}
```

Your own URLs, MLS® numbers and monitoring:

```json
{
    "startUrls": [
        { "url": "https://www.realtor.ca/real-estate/30329771/3895-w-20th-avenue-vancouver" },
        { "url": "https://www.bcassessment.ca/Property/Info/QTAwMDAwNENMMQ==" }
    ],
    "mlsNumbers": ["R3169686"],
    "onlyNew": true,
    "stateKey": "my-listings"
}
```

| Field | Notes |
| --- | --- |
| `addresses` | Civic addresses in British Columbia, the unit first for a condo (`202-1000 Beach Ave, Vancouver`). |
| `pids` | Parcel identifiers, 9 digits, with or without dashes (`017-580-285`). |
| `rollNumbers` | Roll numbers with their jurisdiction: `09-200-030-617-121-24-0140`, `200-030-617-121-24-0140` or `City of Vancouver: 030-617-121-24-0140`. |
| `plans` | Plan and lot (`VAS2613 140`). |
| `startUrls` | Realtor.ca listing URLs, Realtor.ca map searches (every filter of the map kept) and BC Assessment property pages. |
| `mlsNumbers` | Realtor.ca listing numbers (`R3169686`). |
| `location`, `locations` | Cities of British Columbia whose Realtor.ca listings are searched (`Vancouver`, `Kelowna`); `latitude` + `longitude` for a point instead; `radiusKm` around it. |
| `query`, `searchQueries` | Words the listings must contain (Realtor.ca keyword search), e.g. `view`. |
| `transactionType`, `propertyTypeGroup`, `propertyType`, `buildingType`, `ownershipType`, `constructionType` | Realtor.ca's own filters: for sale or rent, residential or commercial, type, building, ownership, style. |
| `minPrice`, `maxPrice`, `minBedrooms`, `maxBedrooms`, `minBathrooms`, `maxBathrooms`, `minLandSizeAcres`, `maxLandSizeAcres`, `listedWithinDays` | More Realtor.ca filters (price in CAD, land in acres, days since listed). |
| `openHouseOnly`, `liveStreamsOnly`, `sortBy` | Listings with an open house or a live stream only; the order of the search. |
| `maxItems`, `maxItemsPerQuery` | Stop after this many properties for the run (`0` = unlimited), and for each search. |
| `includeLandAndBuilding`, `includeComparableSales`, `includeListingDetails`, `includeVancouverTaxData`, `language` | What to add to each property: land / building and neighbours, comparable sales, the listing page, the City of Vancouver's tax and zoning; the language of the listing texts. |
| `minAssessedValue`, `maxAssessedValue`, `minLandValue`, `maxLandValue`, `maxAskingToAssessedRatio`, `minAskingToAssessedRatio` | Bounds on the official values and on asking price ÷ assessed value (listings). |
| `minYearBuilt`, `maxYearBuilt`, `minSquareFootage`, `maxSquareFootage`, `minStoreys`, `maxStoreys` | Bounds on the property. |
| `minPropertyTax`, `maxPropertyTax`, `minMaintenanceFees`, `maxMaintenanceFees` | Bounds on the yearly tax and the monthly strata fee. |
| `soldAfter`, `soldBefore`, `postedAfter`, `postedBefore` | A sale in the last 3 years inside a range; a listing date range: `2026-09-01`, or `7 days`, `12 months`. |
| `excludeKeywords`, `exactMatchOnly` | Drop properties whose description holds a word; keep only the exact property or unit (never a whole building). |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only what was never delivered under this memory key; `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

A real item, shortened (the lists cut to their first entries):

```json
{
    "id": "QTAwMDAwNENMMQ==-2026",
    "propertyId": "QTAwMDAwNENMMQ==",
    "url": "https://www.bcassessment.ca/Property/Info/QTAwMDAwNENMMQ==",
    "query": "202-1000 Beach Ave, Vancouver",
    "queryType": "address",
    "matchType": "unit",
    "address": "202-1000 BEACH AVE VANCOUVER V6E 4M2",
    "unitNumber": "202",
    "city": "VANCOUVER",
    "postalCode": "V6E 4M2",
    "jurisdiction": "City of Vancouver",
    "areaJurisdictionRoll": "09-200-030-617-121-24-0140",
    "pid": "017-580-285",
    "assessedValue": 938000,
    "assessmentYear": 2026,
    "valuationDate": "2025-07-01",
    "previousAssessedValue": 926000,
    "valueChangePercent": 1.3,
    "landValue": 619000,
    "improvementValue": 319000,
    "valueHistory": [
        { "year": 2026, "value": 938000, "changePercent": 1, "jurisdictionChangePercent": -6 },
        { "year": 2025, "value": 926000, "changePercent": -12, "jurisdictionChangePercent": -1 }
    ],
    "propertyDescription": "Strata Apartment -Hi-Rise",
    "yearBuilt": 1991,
    "bedrooms": 2,
    "strataArea": 1157,
    "buildingStoreys": 27,
    "sales": [],
    "comparableSales": [
        {
            "address": "1005-1000 BEACH AVE VANCOUVER",
            "areaJurisdictionRoll": "09-200-030-617-121-24-0178",
            "url": "https://www.bcassessment.ca/Property/Info/QTAwMDAwNENNNQ==",
            "salePrice": 1140000,
            "saleDate": "2026-09-08",
            "assessedValue": 1087000,
            "landValue": 760000,
            "improvementValue": 327000
        }
    ],
    "neighbours": [
        { "address": "201-1000 BEACH AVE VANCOUVER", "assessedValue": 764000, "landValue": 534000, "improvementValue": 230000 }
    ],
    "photos": [],
    "photoCount": 0,
    "openHouses": [],
    "agents": [],
    "fieldNotes": [],
    "scrapedAt": "2026-09-25T22:20:41.301Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 115 fields

| Fields | What you get |
| --- | --- |
| `id`, `propertyId`, `url` | the result's key, BC Assessment's property id, its page |
| `query`, `queryType`, `searchUrl` | the entry this row answers (`address`, `pid`, `rollNumber`, `plan`, `bcAssessmentUrl`, `listingUrl`, `mlsNumber`, `search`) |
| `matchType`, `matchNote` | `exact`, `unit` or `wholeBuilding` (the unit is not in the roll: the values of the whole building or lot), and why |
| `address`, `unitNumber`, `streetAddress`, `city`, `postalCode`, `province`, `latitude`, `longitude` | the address in the roll and its point |
| `areaCode`, `jurisdictionCode`, `jurisdiction`, `rollNumber`, `areaJurisdictionRoll`, `pid`, `legalDescription`, `plan`, `lot` | **Identifiers** — `09-200-030-617-121-24-0140`, `017-580-285` |
| `assessedValue`, `assessmentYear`, `valuationDate`, `previousAssessedValue`, `valueChange`, `valueChangePercent`, `landValue`, `improvementValue`, `valueHistory` | **Official value** — `938000`, `2026`, `2025-07-01`, 10 years |
| `propertyDescription`, `yearBuilt`, `bedrooms`, `bathrooms`, `carports`, `garages`, `landSize`, `firstFloorArea`, `secondFloorArea`, `basementFinishArea`, `strataArea`, `floorArea`, `buildingStoreys`, `grossLeasableArea`, `netLeasableArea`, `apartmentUnits`, `manufacturedHome`, `imageUrl` | **Property** — areas in square feet |
| `sales`, `lastSaleDate`, `lastSalePrice`, `comparableSales`, `neighbours` | **Sales** and **neighbours** |
| `taxLevy`, `taxYear`, `zoningDistrict`, `zoningClassification`, `legalType` | **City of Vancouver** — `3155.39`, `CD-1 (195)` |
| `listingUrl`, `listingId`, `mlsNumber`, `askingPrice`, `currency`, `askingToAssessedRatio`, `askingMinusAssessed`, `transactionType` | **Realtor.ca listing** — `2698000`, `CAD`, `1.12` |
| `listingAddress`, `listingPropertyType`, `listingBuildingType`, `listingOwnershipType`, `listingBedrooms`, `listingBathrooms`, `listingInteriorSize`, `listingLandSize`, `listingFrontage`, `listingParking`, `listingParkingSpaces` | the listing's own description of the property |
| `listingDescription`, `listingPropertyTax`, `listingMaintenanceFee`, `listingFeatures`, `listingAmenities`, `listingYearBuilt`, `listingHeating`, `listingCooling`, `listingFireplace`, `listingBasement`, `listingAppliances`, `listingCommunityFeatures`, `listingView`, `neighbourhood`, `board` | the listing page (option **Listing details**, off by default) |
| `photos`, `photoCount`, `videoUrl`, `listedAt`, `listingUpdatedAt`, `timeOnRealtor`, `daysOnMarket`, `openHouses` | photos, video, dates, open houses |
| `agentName`, `agentPhone`, `agentWebsite`, `agentUrl`, `agents`, `brokerageName`, `brokeragePhone`, `brokerageAddress`, `brokerageWebsite` | **Agents** |
| `fieldNotes`, `scrapedAt` | why a field is empty, ISO timestamp |

### 💡 Tips

#### How to get more results

Leave `maxItems` at `0` and give a larger `radiusKm`: a search that counts more than the 600 listings Realtor.ca shows is split into smaller map squares until every listing is read. Several cities at once: fill `locations`.

#### The newest (or cheapest) listings of a big area

The sort is exact up to 600 listings per search. A bigger search is split into map squares, and a `maxItems` (or `maxItemsPerQuery`) between 600 and the search's count is filled square by square, as the squares come back: not with the N newest or cheapest of the whole area (a Vancouver search capped at 1,000: 285 of them listed more than 8 weeks before the run, while whole squares of the map gave none). For the newest listings of a big area, set **Listed in the last N days** (`listedWithinDays`) and leave `maxItems` at `0`: every listing of those days comes. For the cheapest, the same with **Max asking price (CAD)** (`maxPrice`).

#### How to reduce costs

The levers are `maxItems`, `maxItemsPerQuery`, the options (**Comparable sales**, **Land / building split** and **Listing details** are off by default: turn on only what you need), the filters (a dropped property costs only the filter fee) and `onlyNew` for recurring runs.

#### Several searches in one run

Fill `searchQueries` and / or `locations`: the Actor runs one Realtor.ca search per keyword × city (up to 500 per run). A property found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap. The lookups (addresses, PIDs, rolls, plans, URLs, MLS® numbers) run in the same run.

#### Monitoring: only the new results

Tick **Only new results** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips what was already delivered: a listing already seen, or a property already delivered for the same assessment year — so a watched list of addresses comes back once a year, when BC Assessment publishes the new values. The memory lives in a named key-value store of your account (`bcassessment-property-scraper-seen`, up to 150,000 results per key) and is only updated with rows that really reached the dataset. Give each schedule its own `stateKey`, and tick `resetState` once to start over. With the default sort, a search sorted by **Newest first** stops after 200 listings in a row you already have (or after your **Max results** / per-search cap, if lower); another sort reads the whole search each time, and only the new results are saved.

#### Filter by date

`postedAfter` and `postedBefore` read the date a listing was added to Realtor.ca (`listedAt`): a date (`2026-09-01`, the whole day included, British Columbia time) or a period before now (`7 days`, `2 weeks`, `1 month`). The lookups (address, PID, roll, plan) are not affected. `soldAfter` and `soldBefore` read BC Assessment's sales of the last 3 full years. Filtered-out properties are not saved and do not count in `maxItems`.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per property of British Columbia with its official BC Assessment value. Actor id: `nice_dev/bcassessment-property-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/bcassessment-property-scraper`.

Smallest input, for a cheap first call:

```json
{
    "addresses": ["202-1000 Beach Ave, Vancouver"],
    "maxItems": 1
}
```

Key output fields: `address`, `assessedValue`, `previousAssessedValue`, `landValue`, `improvementValue`, `valueHistory`, `pid`, `askingToAssessedRatio`.

Cost: $5.00 per 1,000 properties plus $0.001 per run start; the options cost extra, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.&#x20;

### ❓ FAQ

#### Is it legal to scrape BC Assessment?

The Actor only reads what BC Assessment and Realtor.ca show publicly to any anonymous visitor. It logs in to nothing and solves no captcha. BC Assessment shows no owner name, and the Actor returns none. Listings can contain personal data (a listing agent's name and phone), which is protected by privacy law (PIPEDA in Canada, GDPR in Europe): do not store it without a legitimate reason. British Columbia's Assessment Act forbids using assessment information to obtain names, addresses or phone numbers for solicitation. You are responsible for using the data in compliance with BC Assessment's and Realtor.ca's Terms of Use and applicable law. This Actor is not affiliated with BC Assessment, Realtor.ca or the Canadian Real Estate Association.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request a site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

#### Why is a property "not found"?

An address the roll does not know (a new building not assessed yet), a unit missing from its building's list, a building with units when no unit number is given, a listing outside British Columbia, a PID or roll number that is not in the roll, a location that is no place of British Columbia (the closest place the Province geocoder found is given, and never searched instead). Each one is listed with its reason in the `NOT_FOUND` record of the run's key-value store, and none is charged. Write the unit first for a condo (`202-1000 Beach Ave`) and the town when the same street exists in several towns.

#### Is the data safe to open in Excel or to show on a web page?

Listing descriptions are the agents' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@`: Excel and Google Sheets may read such a cell of a CSV file as a formula. When you open a CSV, import the text columns as text. Every URL field holds an http(s) URL or `null`. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- A unit the roll does not list separately (a duplex, a rented building) comes with the values of the whole property, marked `wholeBuilding`: tick `exactMatchOnly` to drop them.
- `landValue`, `improvementValue` and `neighbours` need **Land / building split** (off by default); `comparableSales` needs **Comparable sales** (off by default).
- The City of Vancouver's tax and zoning cover the City of Vancouver only (not North Vancouver, Burnaby…).
- `garages` and `carports` are empty for most properties: BC Assessment leaves them blank on its own pages.
- A listing URL is found on Realtor.ca's map around the address its URL gives; when it is not there (a rental, a commercial listing), the property of that address is returned without the listing.
- Two runs sharing the same `stateKey` at the same time may both return the same new result.

**A run the platform stops without warning** (out of memory, run timeout)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the properties already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved once a minute: resurrect the stopped run and the rows it had saved meanwhile join the memory; leave it stopped for good, and the next run may return up to a minute of them once more.

#### Something doesn't work?

The last line of the log counts the properties saved, not found, filtered out, and the requests that failed after every retry. Those requests are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store, and the entries not found in the `NOT_FOUND` record. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If BC Assessment changes its pages, you are told instead of paying for blank rows: if the first 20 properties read all lack their value, their address or their value history (or, for listings, their listing date), the run saves nothing more, stops and fails, and its last message names the missing field: at most those first properties are charged.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` / `NOT_FOUND` records of the key-value store show exactly which entries failed and why.

# Actor input Schema

## `addresses` (type: `array`):

Civic addresses in British Columbia, one per line, as you would write them: `202-1000 Beach Ave, Vancouver`, `1000 Douglas St Victoria`, `3895 W 20th Avenue Vancouver`. Put the unit number first (`202-1000 …` or `202 1000 …`) for a condo; add the city when the same street exists in several towns. Max 10 000.

## `pids` (type: `array`):

Land title parcel identifiers, 9 digits, with or without dashes (`017-580-285`). Max 10 000.

## `rollNumbers` (type: `array`):

Assessment roll numbers WITH their jurisdiction, as printed on the assessment notice: `09-200-030-617-121-24-0140` (area-jurisdiction-roll), `200-030-617-121-24-0140` (jurisdiction-roll) or `City of Vancouver: 030-617-121-24-0140` (jurisdiction name, then the roll). Max 10 000.

## `plans` (type: `array`):

Legal plan number and lot, separated by a space or `/`: `VAS2613 140`, `EPP12345/7`. The lot is required (a plan alone matches too many properties). Max 10 000.

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

Realtor.ca listing URLs (`https://www.realtor.ca/real-estate/30329771/…`), Realtor.ca map searches (`https://www.realtor.ca/map#…`, every filter set on the map is kept) and BC Assessment property pages (`https://www.bcassessment.ca/Property/Info/…`). Listings outside British Columbia are listed in the `NOT_FOUND` record (not charged). Max 1 000 URLs.

## `mlsNumbers` (type: `array`):

Realtor.ca listing numbers (`R3169686`, `10401741`): each listing is read, then its property is looked up in the roll. Max 10 000.

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

City, town or area in British Columbia (`Vancouver`, `North Vancouver`, `Kelowna`, `Victoria`). The search covers a square of **Search radius** around its centre.

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

Several locations in one run: every keyword is searched in every location (max 500 searches). Added to **Location**; a listing found by several searches is saved once.

## `radiusKm` (type: `integer`):

Half the side of the square searched around each location's centre.

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

Search around a point instead of a named location: latitude in decimal degrees (`49.2827`), with **Centre longitude** and **Search radius**.

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

Longitude in decimal degrees (`-123.1207`).

## `query` (type: `string`):

Word that must appear in the listing (Realtor.ca keyword search), e.g. `view`, `laneway`, `suite`.

## `searchQueries` (type: `array`):

One search per keyword (times each location). Added to **Listing keyword**.

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

Listings for sale (the asking price is compared with the assessed value) or for rent.

## `propertyTypeGroup` (type: `string`):

Realtor.ca residential listings, or its commercial listings (offices, retail, industrial, land for business).

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

Realtor.ca property type.

## `buildingType` (type: `string`):

Realtor.ca building type.

## `ownershipType` (type: `string`):

Freehold, condo / strata, timeshare or leasehold.

## `constructionType` (type: `string`):

Detached, attached, semi-detached, stacked or link (houses and townhouses).

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

Lowest asking price (monthly rent for rentals).

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

Highest asking price (monthly rent for rentals).

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

At least this many bedrooms (listing).

## `maxBedrooms` (type: `integer`):

At most this many bedrooms (listing).

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

At least this many bathrooms (listing).

## `maxBathrooms` (type: `integer`):

At most this many bathrooms (listing).

## `minLandSizeAcres` (type: `number`):

Smallest lot, in acres (listing).

## `maxLandSizeAcres` (type: `number`):

Largest lot, in acres (listing).

## `listedWithinDays` (type: `integer`):

Only listings added to Realtor.ca in the last N days. Empty = any date.

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

Keep the listings that announce an open house (read on the listing, dates in `openHouses`).

## `liveStreamsOnly` (type: `boolean`):

Keep the listings that announce a live-stream visit.

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

Order in which Realtor.ca returns the listings (matters when a cap stops the search). Exact up to 600 listings per search: a bigger one is split into map squares, and a cap between 600 and its count is filled square by square (README: the newest listings of a big area). With **Only new results**, keep **Newest first**: the search then stops at the listings already delivered; another order reads the whole search on every run.

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

Maximum number of properties to save for the whole run (lookups and searches together, after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH Realtor.ca search (keyword × location, or map URL), so that the first search cannot use up the whole **Max results** budget. 0 = no per-search cap.

## `includeLandAndBuilding` (type: `boolean`):

Land value and building value of the property, and its 9 nearest neighbours with their values. Off by default: charged per property (see Pricing).

## `includeComparableSales` (type: `boolean`):

The 9 sold properties BC Assessment shows as similar (sale price, sale date, values, year built, size). Off by default: charged per property (see Pricing).

## `includeListingDetails` (type: `boolean`):

For a Realtor.ca listing: open the listing for its description, yearly property tax, strata fee, features, photos and video. Off by default: charged per listing page read (see Pricing).

## `language` (type: `string`):

Language of the listing texts (description, features) when the board provides both.

## `includeVancouverTaxData` (type: `boolean`):

For a property in the City of Vancouver: the yearly property tax levied and the zoning district, from the City's open data.

## `minAssessedValue` (type: `integer`):

Keep the properties whose current total assessed value is at least this.

## `maxAssessedValue` (type: `integer`):

Keep the properties whose current total assessed value is at most this.

## `minLandValue` (type: `integer`):

Keep the properties whose land value is at least this (needs **Land / building split**); useful to find land-heavy lots.

## `maxLandValue` (type: `integer`):

Keep the properties whose land value is at most this (needs **Land / building split**).

## `maxAskingToAssessedRatio` (type: `number`):

Listings only: keep the listings whose asking price is at most this multiple of the assessed value, e.g. `0.95` = listed at least 5 % under the assessment. Listings without a price or a value are dropped.

## `minAskingToAssessedRatio` (type: `number`):

Listings only: keep the listings whose asking price is at least this multiple of the assessed value, e.g. `1.2`.

## `minYearBuilt` (type: `integer`):

Keep the properties built in or after this year (BC Assessment year built).

## `maxYearBuilt` (type: `integer`):

Keep the properties built in or before this year.

## `minSquareFootage` (type: `integer`):

Keep the properties with at least this floor area (BC Assessment strata area, or first + second floor + finished basement; the listing size when the roll has none).

## `maxSquareFootage` (type: `integer`):

Keep the properties with at most this floor area.

## `minStoreys` (type: `integer`):

Keep the properties in buildings with at least this many storeys (BC Assessment).

## `maxStoreys` (type: `integer`):

Keep the properties in buildings with at most this many storeys.

## `minPropertyTax` (type: `integer`):

Listings: the tax shown on the listing (needs **Listing details**); City of Vancouver: the tax levied (needs **City of Vancouver tax and zoning**). Properties without a tax figure are dropped.

## `maxPropertyTax` (type: `integer`):

Same source as the minimum.

## `minMaintenanceFees` (type: `integer`):

Listings only (needs **Listing details**). Listings without a fee are dropped.

## `maxMaintenanceFees` (type: `integer`):

Listings only (needs **Listing details**). Listings without a fee are kept (a house has none).

## `soldAfter` (type: `string`):

Keep the properties with a sale on or after this date in BC Assessment's sales history (last 3 full calendar years): `2025-01-01` or a period such as `12 months`.

## `soldBefore` (type: `string`):

Keep the properties with a sale on or before this date (whole day included).

## `postedAfter` (type: `string`):

Listings only: keep the listings added to Realtor.ca on or after this date: `2026-09-01`, or `7 days`, `2 weeks`. Lookups (address, PID, roll, plan) are not affected.

## `postedBefore` (type: `string`):

Listings only: keep the listings added on or before this date (whole day included), or older than a period such as `30 days`.

## `excludeKeywords` (type: `array`):

Drop the properties whose BC Assessment description or listing text contains one of these words (case and accents ignored), e.g. `commercial`, `leasehold`.

## `exactMatchOnly` (type: `boolean`):

Save only the properties found exactly (the property itself, or its unit). Off: an address or listing whose unit is not in the roll is saved with the value of the WHOLE building or lot, marked `matchType` `wholeBuilding`.

## `onlyNew` (type: `boolean`):

Skip what a previous run (same **Memory key**) already delivered: a listing already seen, or a property already delivered for the same assessment year. Skipped entries are not saved and not charged. First run = everything is new.

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

Name of the memory used by **Only new results**. Give each schedule / task its own key (e.g. `vancouver-condos`). Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) everything again. Untick it afterwards.

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

Apify Proxy or your own proxies, used for BC Assessment. Keep the default: it is included in the price. Realtor.ca listings are read through the Actor's own Canadian residential connection (included in the price) unless you give your own proxies here. The residential Apify proxy is not available in this Actor.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests in any 60 seconds, all sites counted. A budget, not an even pace (spread them with the minimum delay below). Lower it if the log shows HTTP 429 / 403.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. Behind a proxy, a request the site turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "addresses": [
    "202-1000 Beach Ave, Vancouver",
    "1000 Douglas St, Victoria"
  ],
  "pids": [],
  "rollNumbers": [],
  "plans": [],
  "startUrls": [],
  "mlsNumbers": [],
  "locations": [],
  "radiusKm": 10,
  "searchQueries": [],
  "transactionType": "sale",
  "propertyTypeGroup": "residential",
  "propertyType": "residential",
  "buildingType": "any",
  "ownershipType": "any",
  "constructionType": "any",
  "openHouseOnly": false,
  "liveStreamsOnly": false,
  "sortBy": "newest",
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "includeLandAndBuilding": false,
  "includeComparableSales": false,
  "includeListingDetails": false,
  "language": "en",
  "includeVancouverTaxData": false,
  "excludeKeywords": [],
  "exactMatchOnly": false,
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 240,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "addresses": [
        "202-1000 Beach Ave, Vancouver",
        "1000 Douglas St, Victoria"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/bcassessment-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 = {
    "addresses": [
        "202-1000 Beach Ave, Vancouver",
        "1000 Douglas St, Victoria",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/bcassessment-property-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 '{
  "addresses": [
    "202-1000 Beach Ave, Vancouver",
    "1000 Douglas St, Victoria"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/bcassessment-property-scraper --silent --output-dataset

```

## MCP server setup

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