Kijiji.ca $1๐ฐ Classifieds, Vehicles, Jobs & Property
Pricing
from $1.00 / 1,000 results
Kijiji.ca $1๐ฐ Classifieds, Vehicles, Jobs & Property
Extract listings from Kijiji.ca across property, vehicles, jobs, electronics, furniture, services, and more. Search by keyword and location or use any Kijiji URL. Returns title, description, price, photos, GPS coordinates, seller details, and category-specific attributes.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
5 days ago
Last modified
Categories
Share
Kijiji.ca Scraper
Extract classified listings from Kijiji.ca across every category: property, vehicles, jobs, furniture, electronics, services, and more. Search by keyword and location, or paste any Kijiji URL. Each record carries the title, full description, price, every photo, GPS coordinates, seller details, and the category-specific attributes Kijiji shows on the listing. Fast and inexpensive at scale.
Why this scraper
- Covers all of Kijiji.ca, not just one vertical. The extractor adapts to property, vehicles, and general classifieds automatically.
- Rich records out of the box: 30+ fields from the search page alone, including full description, all image URLs, and GPS coordinates.
- Optional detail enrichment adds view counts, seller phone and website, listing status, virtual tour or video, and dealer profile.
- Two ways in: keyword + location search (any Canadian city, region, or province), or paste Kijiji URLs directly.
- Price and sort filters, covering the whole result set with no artificial page ceiling.
- Resilient connection handling: rotates to a fresh exit IP on rejection and fails over to a backup gateway.
- Resumable: a run interrupted by a platform migration or Resurrect continues from where it left off (same dataset, no duplicate rows).
resumeFromRunIdalso lets you pull just the NEW listings since a previous run. - Incremental mode for recurring monitoring: schedule this actor daily or weekly and get only what changed, with no run/dataset id to paste.
Data you get
Sample shape, values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
| id | "0000000000" |
| listingType | "AutosListing" |
| title | "Sample listing title" |
| description | "Full listing description appears here." |
| url | "https://www.kijiji.ca/v-cars-trucks/city-of-toronto/sample/0000000000" |
| price | 12500.0 |
| priceRaw | 1250000 |
| priceType | "FIXED" |
| currency | "CAD" |
| categoryId | 174 |
| locationName | "Toronto" |
| address | "100 Sample Street, Toronto, ON, M0M 0M0" |
| latitude | 43.6500 |
| longitude | -79.4000 |
| nearestIntersection | ["Sample Road", "Example Avenue"] |
| imageUrls | ["https://media.kijiji.ca/api/v1/.../image?rule=kijijica-200-jpg"] |
| imageCount | 10 |
| posterId | "0000000000" |
| sellerType | "KMB" |
| posterRating | 4.5 |
| posterVerified | false |
| activationDate | "2026-01-01T00:00:00.000Z" |
| sortingDate | "2026-01-01T00:00:00.000Z" |
| isTopAd | false |
| attr_carmake | "ford" |
| attr_noofseats | "4" |
| views | 1140 |
| phoneNumber | "+15550000000" |
| sellerWebsiteUrl | "https://example.com" |
Category-specific facts arrive as attr_* keys (for example attr_carmake, attr_numberbedrooms, attr_dateavailable) plus the full raw list in attributesRaw, so no field is ever dropped.
How to use
Search a single keyword in one city:
{"mode": "search","keywords": ["iphone"],"location": "city-of-toronto","maxListings": 50}
Search with price filter and newest-first sort:
{"mode": "search","keywords": ["sofa"],"location": "vancouver","minPrice": 100,"maxPrice": 500,"sortBy": "dateDesc","maxListings": 100}
Browse every listing in a location (no keyword), unlimited:
{"mode": "search","keywords": [],"location": "calgary","fetchDetails": true,"maxPages": 0,"maxListings": 0}
Paste Kijiji URLs directly (multiple supported):
{"mode": "url","urls": ["https://www.kijiji.ca/b-cars-trucks/city-of-toronto/c174l1700273","https://www.kijiji.ca/b-apartments-condos/vancouver/c37l1700287"],"maxListings": 200}
Resume a previous run, collecting only listings added since then:
{"mode": "search","keywords": ["iphone"],"location": "city-of-toronto","resumeFromRunId": "<a previous run or dataset ID>"}
Input parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| mode | string | "search" | "search" for keyword + location, "url" to paste links. |
| keywords | array | ["iphone"] | Search terms (search mode). Each runs as its own all-category search. Empty browses everything in the location. |
| location | string | "canada" | City, region, or province name, a Kijiji slug, or a numeric id. Resolved against the live Kijiji location list. |
| sortBy | string | "relevance" | "relevance", "dateDesc", "priceAsc", or "priceDesc". Search mode only. |
| urls | array | (example) | Kijiji search or listing URLs (URL mode). Keywords, Location, and Sort by are search-only and have no effect here. |
| minPrice | integer | (none) | Minimum price in Canadian dollars. Applies to both Search mode and URL mode. A listing with no numeric price (e.g. "Please Contact", "Free", "Swap") is dropped once either bound is set. |
| maxPrice | integer | (none) | Maximum price in Canadian dollars. Applies to both Search mode and URL mode; see minPrice for the no-price-listing behavior. |
| fetchDetails | boolean | true | Open each listing page for view count, seller phone and website, status, and more. |
| maxPages | integer | 0 | Optional bound on result pages per search or URL (40 listings per page). 0 = no page bound; the run stops on its own at the reported total, an empty page, or a page with no new listings. Does not cap listings saved: use maxListings for that. |
| maxListings | integer | 20 | Maximum listings to save for the whole run. 0 = unlimited (paid plans only). This is the one setting that bounds run size. |
| resumeFromRunId | string | (none) | ID of a previous run (or dataset) of this actor. Listings already in that dataset are skipped, so this run returns only new listings. |
| incrementalMode | boolean | false | Recurring monitoring: return only NEW/UPDATED/REAPPEARED listings vs. the actor's own memory of the last run for this search. See "Incremental / recurring updates" below. |
| stateKey | string | (empty) | Optional label for the tracked search in Incremental mode. Auto-derived from filters when empty. |
| emitUnchanged | boolean | false | Incremental mode only: also return UNCHANGED listings (the full snapshot every run). |
| emitExpired | boolean | false | Incremental mode only: also return EXPIRED listings, when this run reached the natural end of the search. |
| proxy | object | Residential CA | Proxy configuration. Residential with country CA is strongly recommended. |
The actor also survives a platform migration or a manual Resurrect of a failed/aborted run: it checkpoints its progress (current search, page, and collected IDs) and, when resumed with the same input, continues from there instead of restarting, with no duplicate rows and no duplicate charges.
Incremental / recurring updates
For a search or URL list you run on a schedule (daily, weekly), turn on incrementalMode instead of pasting a run id every time. The actor remembers the listings it saw last time (in a dedicated key-value store, keyed by a hash of your search/URL filters, or your own stateKey) and classifies every listing it scrapes against that memory:
changeType | Meaning |
|---|---|
NEW | Not seen in any previous run for this search. |
UPDATED | Seen before, and at least one field genuinely changed (changedFields lists which). |
UNCHANGED | Seen before, nothing changed. Not returned by default (turn on emitUnchanged to get it anyway). |
REAPPEARED | Seen before, then missing for a run, now back. |
EXPIRED | Seen before, not found in this run, and this run reached the natural end of the search (no cap, no Resume); see emitExpired below. |
By default a recurring run therefore returns only what changed. A quiet run (nothing changed) can legitimately push zero listings, which is not an error.
stateKey: optional human-readable label for the tracked search; leave empty for an automatic key derived from your filters.emitUnchanged(default off): also returnUNCHANGEDrows, i.e. the full current snapshot every run.emitExpired(default off): also return one tombstone row per listing no longer found, but only when this run scanned the search to its natural end (nomaxListings/maxPagescap hit, noresumeFromRunId, no page that failed to load). A capped, page-bounded, or resumed run can't tell "gone from Kijiji" apart from "not reached yet this run", so it skips EXPIRED detection and logs why instead of guessing.resumeFromRunIdandincrementalModeare two different features. Combine them only to bootstrap a monitoring baseline from a prior one-off crawl (the run fails fast if incremental mode already has saved state for that search).- Fields excluded from change detection, so they never trigger a false
UPDATED:views(a view counter that climbs on its own regardless of any real edit; measured live, the only recurring listing found across this actor's own past runs showedviewsdrift with nothing else about the listing changing) andsortingDate(Kijiji's "newest first" ranking timestamp; the same measured listing was a promoted ad, and Kijiji advances a promoted ad'ssortingDateto keep it near the top of the default sort independent of the seller editing anything). Every other field, includingpriceanddescription, participates, since a real change there is exactly what a monitoring user wants surfaced. WhenfetchDetailsis off, or an individual listing's detail page fails to load, the detail-only fields (status,listingTypeOffer,endDate,externalSource,virtualTourUrl,youtubeVideoId,requestViewingUrl,mlsAd,phoneNumber,sellerWebsiteUrl,commercialProfile,posterInfo) are also excluded for that comparison rather than reading their absence as Kijiji having removed them. One tradeoff from that last exclusion: a SERP-only seller-info change (e.g. a poster becoming verified) is only reliably detected when both compared runs have matching detail-page coverage. - A failed detail-page fetch is not distinguishable, from this actor's code, between "the page never loaded" and "it loaded but had nothing to parse". Both are treated as "no detail this run"; the listing is still returned with its search-page fields, just without the detail-only ones.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape โ the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record โ not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify โ Settings โ Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Output example
Sample shape, values are illustrative placeholders, not from a live listing.
{"id": "0000000000","listingType": "AutosListing","title": "Sample listing title","description": "Full listing description appears here.","url": "https://www.kijiji.ca/v-cars-trucks/city-of-toronto/sample/0000000000","price": 12500.0,"priceRaw": 1250000,"priceType": "FIXED","currency": "CAD","categoryId": 174,"locationName": "Toronto","address": "100 Sample Street, Toronto, ON, M0M 0M0","latitude": 43.6500,"longitude": -79.4000,"imageUrls": ["https://media.kijiji.ca/api/v1/sample/image?rule=kijijica-200-jpg"],"imageCount": 10,"posterId": "0000000000","sellerType": "KMB","posterRating": 4.5,"posterVerified": false,"activationDate": "2026-01-01T00:00:00.000Z","attr_carmake": "ford","attr_noofseats": "4","views": 1140,"phoneNumber": "+15550000000","sellerWebsiteUrl": "https://example.com"}
Plan requirement
Kijiji.ca accepts Canadian residential connections most reliably. The default proxy is Apify Residential with country CA, which requires an Apify Starter plan or higher. On the free plan, set the BACKUP_PROXY_URL environment variable to a Canadian residential gateway, or expect reduced results. Datacenter exits are frequently rejected by the site and are not recommended.