# Canada Distressed Property Scraper (`abotapi/distressed-ca-scraper`) Actor

Canadian tax-sale properties from taxsales.ca: tender and auction listings across 9 provinces with minimum bid, deadline or auction date, roll number, legal description and the municipality's notice, plus resume and incremental change tracking.

- **URL**: https://apify.com/abotapi/distressed-ca-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 distressed property records

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

## Canada Distressed Property Scraper

Canadian tax-sale properties in one run: municipal tender and auction listings from taxsales.ca across every province it covers, with the minimum tender or reserve bid, the closing or auction date, the roll number, the legal description and a link to the municipality's own notice. Pick provinces or paste links, and track what is new or changed between scheduled runs.

### Why This Scraper?

- **The whole list, not the first page.** A province walk reads every open listing, not just the first screen. A full run returns all of Canada's open listings, over a thousand at the time of writing, and the run log states how many the portal reports.
- **Tenders and auctions, told apart.** Each record says whether it is a tender sale or an auction, and carries the right date: the tender deadline, or the auction day, time and place.
- **What a buyer needs to decide.** Minimum tender or reserve bid, assessed value, roll number, parcel identifier, legal description, where to submit a tender, the municipality's bidding conditions and the official notice link.
- **Made for recurring monitoring.** Resume an interrupted run from its ID, or let incremental mode return only NEW and UPDATED listings, with EXPIRED markers when a listing leaves the portal.
- **Search by province or paste links.** Walk whole provinces, or paste province, search and listing links.
- **Honest by design.** If the portal refuses every request the run fails with a clear message instead of an empty dataset that looks like "no results". A province with no open listings says so.

### Use Cases

- **Investors and buyers:** find below-market properties sold for unpaid taxes, with the deadline and the conditions in one place.
- **Real-estate professionals:** watch tender deadlines and auction dates by province for clients.
- **Lead-generation teams:** feed new tax-sale listings into a CRM or alerts on a schedule.
- **Analysts:** compare tax-sale volumes and minimum bids across provinces.

### Data You Get

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

| Field | Example |
| --- | --- |
| `recordId` | `"ca:taxsales.ca:sampletown-6001000001400000000"` |
| `country` / `source` | `"ca"` / `"taxsales"` |
| `title` | `"123 Sample Road, Sampleton"` |
| `distressType` | `"tax-sale"` |
| `caseNumber` | `"6001000001400000000"` (the roll number) |
| `deadlineAt` | `"2026-10-14T15:00:00-04:00"` (tender sales: when tenders close) |
| `auctionDate` | `"2026-11-12T10:00:00-05:00"` (auctions: the sale day) |
| `location.address` / `location.city` / `location.region` | `"123 Sample Road, Sampleton"` / `"Sampleton"` / `"ON"` |
| `valueAmount` / `valueCurrency` / `valueText` | `17518.55` / `"CAD"` / `"$17518.55"` (minimum tender or reserve bid; absent when the portal shows no price) |
| `status` | `"Active"` |
| `description` | the portal's tender summary, when it has one |
| `attachments` | `[{ "label": "Map view", "url": "..." }]` |
| `details.saleMethod` / `details.valueKind` | `"tender"` / `"Minimum tender"` |
| `details.legalDescription` / `details.pin` / `details.assessedValue` | `"PCL 1 SEC 1; LT 1 PL 1"` / `"12345-0001"` / `8500` |
| `details.submitTenders` / `details.auctionLocation` | where to send a tender / where an auction is held |
| `details.municipalNotice` / `details.noticeConditions` | the municipality's own bidding terms |
| `details.geo` / `details.locationApproximate` | `"49.40000,-91.65000"` / `true` when only the municipality centre is known |
| `details.officialSourceUrl` / `details.statusHistory` | link to the municipality's notice / dated status lines |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | incremental-mode metadata |
| `url` | the listing page |

Detail fields come from each listing's own page, so they are filled when **Fetch detail pages** is on (the default). Not every listing has every field: Quebec auctions, for example, show no price.

### How to Use

1. Pick a **Mode**: `search` (default) walks the selected provinces; `url` reads only the links you paste.
2. In search mode, pick your **provinces**, or leave it empty to walk every province. Optionally narrow the walk with the site's own filters: **status**, **property type**, **minimum / maximum tender**, **closing within (days)**, and a **sort** order. Filtered or re-sorted walks are partial scopes, so they never mark unseen listings as expired. Newest first stays the default because it is the only order that pages stably. In URL mode, paste taxsales.ca province, search or listing links.
3. Set **Max items** (50 by default; `0` returns everything) and whether to fetch detail pages, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Ontario walk:**

```json
{
  "mode": "search",
  "provinces": ["on"],
  "fetchDetails": true,
  "maxItems": 50
}
```

**Everything open in Canada:**

```json
{
  "mode": "search",
  "fetchDetails": true,
  "maxItems": 0
}
```

**URL mode:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.taxsales.ca/province/ontario/",
    "https://www.taxsales.ca/listing/ignace-6001000001400610000/"
  ],
  "fetchDetails": true,
  "maxItems": 20
}
```

#### Run it from your code

**Python:**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("abotapi/distressed-ca-scraper").call(run_input={
    "provinces": ["on"],
    "fetchDetails": True,
    "maxItems": 50,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["recordId"], item["title"], item.get("valueAmount"))
```

**JavaScript:**

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('abotapi/distressed-ca-scraper').call({
    provinces: ['on', 'ns'],
    fetchDetails: true,
    maxItems: 50,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) console.log(item.recordId, item.title);
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run ID and the actor skips everything already collected there.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same scope. Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on) or `EXPIRED` (only after a run that read the whole scope, and only with `emitExpired`). Use `stateKey` to share one baseline across runs. In search mode the automatic baseline ignores `urls` (they are read only in url mode), so leaving the example links in place or removing them uses the same baseline. The portal adds a dated line to every listing's status history each day, so the history is returned but never counts as a change by itself; a new status does. The comparison covers every returned field (title, status, minimum bid / amount, deadline, description, parties, details); `ignoreFieldsForChanges` accepts dotted paths such as `details.img`. `changeScope: priceAndStock` compares only the minimum bid / amount, currency, status and deadline. `compactUpdates` makes UPDATED rows slim (EXPIRED markers are always returned in full) (identifiers, current bid / status / deadline and `priceChanges`) and does not apply the detail-enrichment charge to them. `expireAfterMissedScans` waits for N consecutive complete scans before an EXPIRED marker; a listing missing from the portal does not prove it is unavailable, so read `status`. EXPIRED is never produced by a capped run (`maxItems`, `maxPages`), a pasted listing link or a pasted search link.

### Send results into your apps (MCP connectors)

Optionally send a summary of each returned record into an app connected through Apify MCP integrations. Select one or more **MCP connectors** and, for Notion, provide the parent page URL or ID. The actor creates a Notion page per record; other connectors receive a best-effort write or digest. Exports are capped by **Max items to export per connector** (50 by default), and connector failures do not change dataset output.

| Field | What it does |
|---|---|
| `mcpConnectors` | Selected MCP connectors (empty = no export). |
| `notionParentPageUrl` | Notion parent page URL or ID; required for Notion exports. |
| `maxNotifyListings` | Maximum records sent to each selected connector (default `50`, maximum `1000`). |

### Input Parameters

All parameters are optional unless noted.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` walks the selected provinces; `url` reads only the pasted links. |
| `provinces` | array | all provinces | Province codes to walk, in order: `ab`, `bc`, `mb`, `nb`, `nl`, `ns`, `on`, `qc`, `sk`. Empty means all of them. Search mode only. |
| `urls` | array | one example per link type | Pasted taxsales.ca province, municipality (for example `/ontario/tiny/`), search or listing links. Read in URL mode. A free-text `q=` on a search link is ignored by the site (the link returns the unfiltered list and the run logs a warning). Search links keep their status, type, tender, closing-in and sort filters and start at the page you paste. The site home and results pages are skipped with a warning. With the field untouched, URL mode scrapes the example links (a province page, a municipality page, a listing, and four search pages). |
| `fetchDetails` | boolean | `true` | Open each listing's own page for the full field set. |
| `maxItems` | integer | `50` | Total result cap across all provinces (`0` = no limit; a full run holds over a thousand listings, so set a number you are happy to pay for). |
| `maxPages` | integer | `0` | Stop each walk after this many result pages (24 listings per page); `0` = read to the last page. |
| `status` | string | active (site default) | Search mode: the listing status to read (`active`, `deadline_passed`, `cancelled`, `results_pending`, `sold`, `no_tenders`, `needs_review`). Left empty the site returns its active listings; any other status selects listings outside that default walk rather than narrowing it. A filtered walk is a partial scope. |
| `type` | string | (any) | Search mode: property type (`unknown`, `vacant_land`, `residential`, `commercial`, `waterfront`, `mixed`). Partial scope. |
| `minTender` | integer | (none) | Search mode: minimum tender / bid in CAD. Partial scope. |
| `maxTender` | integer | (none) | Search mode: maximum tender / bid in CAD. Partial scope. |
| `closingIn` | integer | (none) | Search mode: only listings closing within this many days. Partial scope. |
| `sort` | string | `newest` | Search mode: `newest`, `closing`, `amount_asc` or `amount_desc`. Anything but `newest` is a partial scope. |
| `proxy` | object | standard pool | Connection settings. |
| `resumeFromRunId` | string | (none) | Continue a previous run; only unpushed records are saved. |
| `incrementalMode` | boolean | `false` | Track changes across scheduled runs; only NEW and UPDATED returned. Cannot be combined with `resumeFromRunId`. |
| `stateKey` | string | (none) | Named incremental baseline; derived from the scope when empty. |
| `emitUnchanged` | boolean | `false` | Also return UNCHANGED records in incremental mode. |
| `emitExpired` | boolean | `false` | Also return EXPIRED records in incremental mode. |
| `changeScope` | string | `all` | `priceAndStock` compares only bid / amount, currency, status and deadline. |
| `compactUpdates` | boolean | `false` | Slim UPDATED rows with `priceChanges` (EXPIRED stays full). |
| `expireAfterMissedScans` | integer | `1` | Complete scans a record must be missing before EXPIRED. |
| `ignoreFieldsForChanges` | array | (none) | Extra fields that never count as a change; dotted paths allowed. |
| `mcpConnectors` | array | (none) | Optional MCP connectors to receive returned record summaries after the run. |
| `notionParentPageUrl` | string | (none) | Parent page URL or ID required for Notion page creation. |
| `maxNotifyListings` | integer | `50` | Maximum records sent to each connector (`1`-`1000`). |

### Output Example

The Output tab offers three tables: **Overview** (one row per record), **Detail-enriched** (runs with detail fetching on) and **Changes** (change type, changed fields, first and last seen, for incremental runs).

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

**Example record (Ontario tender sale, detail-enriched):**

```json
{
  "recordId": "ca:taxsales.ca:sampletown-6001000001400000000",
  "country": "ca",
  "source": "taxsales",
  "title": "123 Sample Road, Sampleton",
  "distressType": "tax-sale",
  "caseNumber": "6001000001400000000",
  "deadlineAt": "2026-10-14T15:00:00-04:00",
  "location": { "address": "123 Sample Road, Sampleton", "city": "Sampleton", "region": "ON", "country": "ca" },
  "valueAmount": 17518.55,
  "valueCurrency": "CAD",
  "valueText": "$17518.55",
  "status": "Active",
  "attachments": [{ "label": "Map view", "url": "https://example.com/sample-map.jpg" }],
  "details": {
    "saleMethod": "tender",
    "valueKind": "Minimum tender",
    "legalDescription": "PCL 1 SEC 1; LT 1 PL 1",
    "pin": "12345-0001",
    "assessedValue": 8500,
    "submitTenders": "the Township of Sampleton, 1 Sample Street, Sampleton, ON",
    "geo": "49.40000,-91.65000",
    "locationApproximate": false,
    "officialSourceUrl": "https://example.com/sample-notice",
    "statusHistory": ["Oct 12, 2026 - Active (Automated)"]
  },
  "url": "https://www.taxsales.ca/listing/sampletown-6001000001400000000/"
}
```

### Plan Requirement

The actor reads taxsales.ca through the Apify proxy, so your Apify plan must include proxy access. It runs with the default connection settings and does not need a residential proxy: a standard-pool exit and a Canadian residential exit both returned the full Ontario province page when tested. If the proxy connection is refused for your account, the run fails with a message saying so. Free-plan accounts without proxy access should upgrade to a paid plan, or enter their own proxy URLs in the proxy setting.

### FAQ

#### How much does it cost?

You pay per record returned plus the actor start; opening each listing's own page adds a small per-record charge when **Fetch detail pages** is on. There are no monthly fees; check the Pricing tab for the current prices.

#### Is it legal to scrape this site?

These are public municipal tax-sale notices that the municipalities publish for open tender or auction, and every record links back to the official notice. The actor reads the same pages a browser would, at a polite pace. You remain responsible for how you use the data.

#### Which provinces have listings?

The portal covers Alberta, British Columbia, Manitoba, New Brunswick, Newfoundland and Labrador, Nova Scotia, Ontario, Quebec and Saskatchewan. Quebec and Nova Scotia hold most listings; British Columbia and Newfoundland and Labrador often have none open, and a run over them says so instead of failing. Prince Edward Island and the territories are not listed by the portal.

#### Why does a record have no price?

Some municipalities, notably Quebec, publish auctions without a minimum bid. Those records keep their auction date, place and conditions but carry no `valueAmount`.

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

Yes. Use incremental mode: the actor remembers its baseline per scope and returns only what changed (NEW, UPDATED), with optional EXPIRED markers for listings that left the portal. Use `stateKey` to share one baseline across slightly different input variants.

#### Why does a run return fewer records than I expected?

**Max items** (50 by default) stops the run once the cap is reached, newest listings first; raise it or set it to `0` to collect everything. Only listings that are open for tender or auction are returned, so closed and sold listings are not included.

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

No page could be read, so the run fails rather than returning an empty dataset that would look like "no results". Two messages are possible. "The proxy connection was refused for this account" means your plan does not include proxy access (a paid plan is required) or your own proxy credentials are invalid. "Could not load any results page" is a connection problem such as temporary rate limiting or an outage: wait and rerun. Pasted links that cannot be used (the site home page, the sold-prices results page, other sites) are skipped with a warning; if none of your pasted links is usable the run fails and says so.

#### Can I use it with AI agents or MCP?

Yes, the output is a clean JSON dataset that works with MCP connectors and any AI workflow. Use the `mcpConnectors` input to send results into your apps after each run.

### 🔗 Want more Distressed data?

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

<table>
<tr><td>🧩 <a href="https://apify.com/abotapi/distressed-au-nz-scraper"><b>Distressed Property Scraper</b></a><br>Find mortgagee-sale and distressed-property listings in Australia and New Zealand. NZ...</td><td>🏷️ <a href="https://apify.com/abotapi/kijiji-scraper"><b>Kijiji.ca Scraper</b></a><br>Scrape Kijiji.ca listings across property, vehicles, jobs, electronics, furniture...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/boe-es-auction-scraper"><b>Spain Property &amp; Judicial Auctions Scraper</b></a><br>Scrape Spain's official BOE auction portal: judicial and public auction listings with...</td><td>🏠 <a href="https://apify.com/abotapi/auction-com-scraper"><b>Auction.com Scraper</b></a><br>Extract foreclosure, REO, short-sale, and auction listings from Auction.com in minutes...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/realtor-ca-scraper"><b>Realtor.ca</b></a><br>Scrape Realtor.ca property listings with prices, descriptions, images, agent and...</td><td>🧩 <a href="https://apify.com/abotapi/propwire-property-leads-scraper"><b>Propwire Scraper</b></a><br>Scrape Propwire.com: 157M+ US MLS &amp; off-market properties with owner names, mailing...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/distressed-ca-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

How to discover records. 'search' walks the selected provinces with your filters. 'url' reads only the links you paste into urls. Pasted links are ignored in search mode; the run log tells you when that happens.

## `provinces` (type: `array`):

Which provincial lists to walk, in order. Leave empty to walk all 9 provinces the portal publishes (British Columbia and Newfoundland and Labrador often have no open listings).

## `status` (type: `string`):

Listing status filter. Leave empty for the site default (active listings).

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

Property type filter (search mode). Leave empty for any type.

## `minTender` (type: `integer`):

Only listings with a minimum tender at or above this amount (CAD). Empty = no limit.

## `maxTender` (type: `integer`):

Only listings with a minimum tender at or below this amount (CAD). Empty = no limit.

## `closingIn` (type: `integer`):

Only listings closing within this many days. Empty = no limit.

## `sort` (type: `string`):

Order of the walk. Newest first is the default and the only order that pages stably; other orders can shift between pages and miss listings on long provinces.

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

Pasted taxsales.ca links: province pages (/province/ontario/), municipality pages (/ontario/tiny/), search pages (/search/ with its province, status, type, tender, closing_in and sort filters and ?page=N, read from the page you paste) or listing pages (/listing/<slug>/). The site ignores free-text q= on search links (the link returns the unfiltered list and the run warns); use the province, status, type, tender and closing filters. Listing links emit one enriched record each. The site home and results pages are not listing sources and are skipped with a warning. Read in url mode only; left untouched it scrapes the example links.

## `fetchDetails` (type: `boolean`):

Open each listing's own page for the full record: price, tender deadline or auction date, roll number, parcel identifier, legal description, assessed value, where to submit or attend, the municipality's conditions, map location and status history. Adds one request and a small per-record charge; turn it off for a fast listing-only run.

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

Stop after this many results in total across all provinces. Default 50; 0 = no limit (a full run holds more than a thousand listings, so set a number you are happy to pay for).

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

Stop each walk after this many result pages (24 listings per page). 0 = read to the last page. Max items always applies too.

## `proxy` (type: `object`):

Connection used for requests. The register serves the standard pool; residential exits are used automatically when selected.

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

Continue a previous run: only records not already in that run’s dataset are pushed. Leave empty for a fresh run.

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

Track records across scheduled runs with the same scope. Compared: every returned field (title, status, minimum bid / amount, deadline, description, parties, details) except the built-in volatile ones (the daily status-history line, timestamps, change markers) and any you list under ignore fields; a change in any compared field makes the record UPDATED with changedFields. Only NEW or UPDATED records are returned (UNCHANGED suppressed). Records that disappear are marked EXPIRED only with Emit expired, only after a full uncapped scan of whole provinces. Mutually exclusive with Resume.

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

Name for the incremental baseline when you want to share one baseline across different filter variants. Leave empty to derive it from the scope automatically.

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

In incremental mode, also return records that did not change (marked UNCHANGED).

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

In incremental mode, also return records seen previously that no longer appear (marked EXPIRED). Never emitted from a capped run, a pasted listing or search link. A listing dropping off the portal can mean sold, withdrawn or just hidden; read status for the outcome.

## `changeScope` (type: `string`):

all (default) compares every field. priceAndStock compares only the minimum bid / amount, currency, status and deadline, so a description or detail edit never makes a record UPDATED.

## `compactUpdates` (type: `boolean`):

UPDATED rows carry only identifiers, current bid / status / deadline and priceChanges {field: {old, new}}. NEW and EXPIRED rows stay full. The detail-enrichment charge is not applied to a compact row, because it no longer carries the detail data.

## `expireAfterMissedScans` (type: `integer`):

With Emit expired, a record is marked EXPIRED only after it was missing from this many consecutive complete scans. Default 1. A missing listing is not proof it is unavailable; check status.

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

Advanced: record fields that should never count as a change (in addition to the built-in ignore list). Use a dotted path such as "details.img" to ignore one nested key. Example: \["valueText"].

## `mcpConnectors` (type: `array`):

Optionally send a summary of each returned listing to connected apps. Authorize a connector under Apify > Settings > Integrations, then select it here. Notion creates a page per record; other connectors receive a best-effort write or digest. Leave empty to skip. Dataset results are unaffected.

## `notionParentPageUrl` (type: `string`):

URL or ID of the Notion page where record pages should be created. Required for Notion; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Maximum number of records written to each connector. Does not affect dataset output.

## Actor input object example

```json
{
  "mode": "search",
  "provinces": [
    "on"
  ],
  "urls": [
    "https://www.taxsales.ca/province/ontario/",
    "https://www.taxsales.ca/listing/alfred-and-plantagenet-0231010001105050000/",
    "https://www.taxsales.ca/search/",
    "https://www.taxsales.ca/search/?page=2",
    "https://www.taxsales.ca/search/?province=ontario&page=2",
    "https://www.taxsales.ca/search/?province=ontario&status=active&type=residential&sort=amount_asc",
    "https://www.taxsales.ca/ontario/tiny/",
    "https://www.taxsales.ca/province/ontario",
    "https://taxsales.ca/province/ontario/",
    "https://www.taxsales.ca/province/quebec/",
    "https://www.taxsales.ca/",
    "https://www.taxsales.ca/results/"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `changes` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `overview` (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 = {
    "mode": "search",
    "provinces": [
        "on"
    ],
    "urls": [
        "https://www.taxsales.ca/province/ontario/",
        "https://www.taxsales.ca/listing/alfred-and-plantagenet-0231010001105050000/",
        "https://www.taxsales.ca/search/",
        "https://www.taxsales.ca/search/?page=2",
        "https://www.taxsales.ca/search/?province=ontario&page=2",
        "https://www.taxsales.ca/search/?province=ontario&status=active&type=residential&sort=amount_asc",
        "https://www.taxsales.ca/ontario/tiny/",
        "https://www.taxsales.ca/province/ontario",
        "https://taxsales.ca/province/ontario/",
        "https://www.taxsales.ca/province/quebec/",
        "https://www.taxsales.ca/",
        "https://www.taxsales.ca/results/"
    ],
    "fetchDetails": true,
    "maxItems": 20,
    "maxPages": 0,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/distressed-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 = {
    "mode": "search",
    "provinces": ["on"],
    "urls": [
        "https://www.taxsales.ca/province/ontario/",
        "https://www.taxsales.ca/listing/alfred-and-plantagenet-0231010001105050000/",
        "https://www.taxsales.ca/search/",
        "https://www.taxsales.ca/search/?page=2",
        "https://www.taxsales.ca/search/?province=ontario&page=2",
        "https://www.taxsales.ca/search/?province=ontario&status=active&type=residential&sort=amount_asc",
        "https://www.taxsales.ca/ontario/tiny/",
        "https://www.taxsales.ca/province/ontario",
        "https://taxsales.ca/province/ontario/",
        "https://www.taxsales.ca/province/quebec/",
        "https://www.taxsales.ca/",
        "https://www.taxsales.ca/results/",
    ],
    "fetchDetails": True,
    "maxItems": 20,
    "maxPages": 0,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/distressed-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 '{
  "mode": "search",
  "provinces": [
    "on"
  ],
  "urls": [
    "https://www.taxsales.ca/province/ontario/",
    "https://www.taxsales.ca/listing/alfred-and-plantagenet-0231010001105050000/",
    "https://www.taxsales.ca/search/",
    "https://www.taxsales.ca/search/?page=2",
    "https://www.taxsales.ca/search/?province=ontario&page=2",
    "https://www.taxsales.ca/search/?province=ontario&status=active&type=residential&sort=amount_asc",
    "https://www.taxsales.ca/ontario/tiny/",
    "https://www.taxsales.ca/province/ontario",
    "https://taxsales.ca/province/ontario/",
    "https://www.taxsales.ca/province/quebec/",
    "https://www.taxsales.ca/",
    "https://www.taxsales.ca/results/"
  ],
  "fetchDetails": true,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false
}' |
apify call abotapi/distressed-ca-scraper --silent --output-dataset

```

## MCP server setup

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