Canada Distressed Property Scraper
Pricing
from $1.80 / 1,000 distressed property records
Canada Distressed Property Scraper
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.
Pricing
from $1.80 / 1,000 distressed property records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 hours ago
Last modified
Categories
Share
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
- Pick a Mode:
search(default) walks the selected provinces;urlreads only the links you paste. - 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.
- Set Max items (50 by default;
0returns everything) and whether to fetch detail pages, then click Start. - Download the dataset as JSON, CSV or Excel, or read it through the API.
Ontario walk:
{"mode": "search","provinces": ["on"],"fetchDetails": true,"maxItems": 50}
Everything open in Canada:
{"mode": "search","fetchDetails": true,"maxItems": 0}
URL mode:
{"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:
from apify_client import ApifyClientclient = 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:
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 classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on) orEXPIRED(only after a run that read the whole scope, and only withemitExpired). UsestateKeyto share one baseline across runs. In search mode the automatic baseline ignoresurls(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);ignoreFieldsForChangesaccepts dotted paths such asdetails.img.changeScope: priceAndStockcompares only the minimum bid / amount, currency, status and deadline.compactUpdatesmakes UPDATED rows slim (EXPIRED markers are always returned in full) (identifiers, current bid / status / deadline andpriceChanges) and does not apply the detail-enrichment charge to them.expireAfterMissedScanswaits for N consecutive complete scans before an EXPIRED marker; a listing missing from the portal does not prove it is unavailable, so readstatus. 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):
{"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:
| 🧩 Distressed Property Scraper Find mortgagee-sale and distressed-property listings in Australia and New Zealand. NZ... | 🏷️ Kijiji.ca Scraper Scrape Kijiji.ca listings across property, vehicles, jobs, electronics, furniture... |
| 🧩 Spain Property & Judicial Auctions Scraper Scrape Spain's official BOE auction portal: judicial and public auction listings with... | 🏠 Auction.com Scraper Extract foreclosure, REO, short-sale, and auction listings from Auction.com in minutes... |
| 🏠 Realtor.ca Scrape Realtor.ca property listings with prices, descriptions, images, agent and... | 🧩 Propwire Scraper Scrape Propwire.com: 157M+ US MLS & off-market properties with owner names, mailing... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. 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.