Kijiji.ca Scraper - Listings, Prices & Seller Details
Pricing
from $1.00 / 1,000 results
Kijiji.ca Scraper - Listings, Prices & Seller Details
Scrape Kijiji.ca listings across property, vehicles, jobs, electronics, furniture, services, and more. Search by keyword and location or use any Kijiji URL. Extract titles, descriptions, prices, 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
1
Bookmarked
1
Total users
1
Monthly active users
13 days ago
Last modified
Categories
Share
Kijiji.ca Scraper: Listings, Prices, Photos and Seller Contacts
Kijiji.ca Scraper pulls 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 search, category or single-listing URL. A search result record carries the title, a description preview, price, the search page's own photo thumbnails, GPS coordinates, seller details and the category-specific attributes Kijiji shows on the listing; a pasted single-listing URL, or a search with Fetch listing details on, gets the full description text and the complete photo set instead. Switch on Incremental mode for recurring monitoring, download to JSON, CSV or Excel, or pull results straight into your app through the API.
Why This Scraper?
- Every category, one actor. The extractor adapts automatically to property, vehicles and general classifieds, so the same run handles an apartment search and a car search alike.
- Rich records from the search page alone. 20+ fields per listing straight off the results page, including a description preview, the search page's own photo thumbnails and GPS coordinates, no extra fetch required. Turn on Fetch listing details, or paste a single-listing URL, for the full description text and the complete photo set.
- Optional detail enrichment. Switch on view counts, seller phone and website, listing status, virtual tour or video, and dealer profile per run.
- Two ways in. Search by keyword and location across any Canadian city, region or province, or paste Kijiji search, category or single-listing links directly.
- Price and sort filters that actually narrow. Minimum and maximum price apply to a keyword search and a pasted URL alike, and a run walks the whole result set with no artificial page ceiling unless you set one.
- Built for recurring monitoring. Turn on Incremental mode to get only new, updated or reappeared listings on a schedule, or paste a previous run's id to collect just what's new since then.
- Recovers on its own. A run resumes from where it stopped after a platform migration or a manual restart, with no duplicate rows or duplicate charges, and automatically retries with a different connection when one is refused.
Use Cases
- Marketplace resellers and pickers: track new and price-dropped listings across categories to source inventory worth flipping.
- Rental and real estate research: monitor new property listings in a neighbourhood, with price, layout attributes and photos, and get alerted only when something changes.
- Car shopping and dealer tracking: filter listings by price and body type, then watch a saved search for new inventory instead of refreshing the site by hand.
- Local services and gig marketplaces: pull job or services listings for a city to track posting volume and going rates.
- Market and pricing analysts: build a historical price dataset per category and region for trend analysis.
Data You Get
Sample shape: values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
id | "1700000000" |
listingType | "AutosListing" (also "RealEstateListing", "StandardListing", ...) |
title | "Sample listing title" |
description | "Full listing description appears here." |
url | "https://www.kijiji.ca/v-cars-trucks/city-of-toronto/sample-listing/1700000000" |
price / priceType | 12500.0 / "FIXED" |
currency | "CAD" |
categoryId | 174 |
locationName | "Toronto" |
address | "1 Placeholder Crescent, Toronto, ON, M0M 0M0" |
latitude / longitude | 43.65 / -79.4 |
imageUrls / imageCount | one URL per photo (thumbnails from the search page, or the full set with Fetch listing details on) / 8 |
posterId | "1700000001" |
sellerType | "PVT" |
posterRating / posterVerified | 4.5 / false |
activationDate | "2026-01-01T00:00:00.000Z" |
isTopAd | false |
attr_carmake / attr_noofseats | "ford" / "4" |
views | 342 |
phoneNumber | "4165550123" |
sellerWebsiteUrl | "https://example.com" |
Category-specific facts arrive as attr_* keys (for example attr_carmake, attr_numberbedrooms, attr_dateavailable), plus the complete raw attribute list in attributesRaw, so no field is ever dropped; a property, vehicle or general-classifieds listing carries the same shell shape, only the attr_* keys differ. With detail enrichment on, a listing can also carry status, listingTypeOffer, endDate, virtualTourUrl, youtubeVideoId, requestViewingUrl, mlsAd and, for a dealer or agency account, a commercialProfile object (business name, listing count, website, verification). Every record also carries derived aliases built from those same values: address parts (street_address, locality, region, country, postalCode), dateFrom / dateTo, images and features (the listing's own yes/no amenity flags), and for vehicle listings brand, model, modelYear, vin, milage (km), bodyType, color, fuelType, transmission (the page's label, or decoded from Kijiji's transmission code 0, 1 or 2 when the page supplies no label; empty for any other code), driveWheelConfiguration, seats, numberOfDoors, condition and trim (the trim or variant line, free text). brand, bodyType, color, fuelType, driveWheelConfiguration and condition use the page's label when it supplies one, otherwise the readable label for Kijiji's own attribute code (for example suvcrossover becomes SUV, Crossover, gas becomes Gas); model and any code outside the known list come back as Kijiji's own lowercase code, unchanged. In Incremental mode, every record also carries changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields, firstSeenAt and lastSeenAt.
How to Use
- Pick a mode:
search(keyword + location) orurl(paste links). - Fill in the fields for that mode, and set any price filter or sort order you need.
- Set Max listings (and, optionally, Max pages) to control run size and cost, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Search a keyword in one city:
{"mode": "search","keywords": ["iphone"],"location": "city-of-toronto","maxListings": 50}
Search with a price range and sort order:
{"mode": "search","keywords": ["laptop"],"location": "vancouver","minPrice": 200,"maxPrice": 800,"sortBy": "priceAsc","maxListings": 20}
Browse every listing in a location, no keyword, with details on:
{"mode": "search","keywords": [],"location": "calgary","fetchDetails": true,"maxListings": 30}
Paste Kijiji category or listing URLs directly:
{"mode": "url","urls": ["https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273","https://www.kijiji.ca/b-cars-trucks/city-of-toronto/c174l1700273"],"maxListings": 100}
URL mode link shapes. Every shape below is supported and has an example in the input form:
| Shape | Example |
|---|---|
| Keyword search, all Canada | https://www.kijiji.ca/b-canada/iphone/k0l0 |
| Keyword search in a location | https://www.kijiji.ca/b-city-of-toronto/iphone/k0l1700273 |
| Category in a location | https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273 |
| Category, all Canada | https://www.kijiji.ca/b-cars-vehicles/canada/c27l0 |
| All categories in a location | https://www.kijiji.ca/b-city-of-toronto/c0l1700273 |
| A later results page (the walk starts at that page) | https://www.kijiji.ca/b-apartments-condos/city-of-toronto/page-2/c37l1700273 |
| Sorted / price-filtered link (filters are kept and applied) | https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273?sort=priceAsc&price=500__1500 |
| Single listing | https://www.kijiji.ca/v-apartments-condos/city-of-toronto/1-bedroom-apartment-for-rent-st-clair-ave-west-and-spadina-rd/1744550680 |
| Older listing link | https://www.kijiji.ca/v-view-details.html?adId=1744309122 |
Host without www | https://kijiji.ca/b-canada/iphone/k0l0 |
| French-locale link (results are returned in English) | https://www.kijiji.ca/b-grand-montreal/iphone/k0l80002?siteLocale=fr_CA |
Seller profile pages, the m.kijiji.ca mobile host, the bare /v/<id> form and links to other sites are not supported: they are skipped with a warning that names the reason. In Search mode, a keyword that is a Kijiji ad number or a pasted Kijiji link is routed to that listing or page instead of being used as search text.
Behaviour change: the urls field now ships with one example of each shape as its default. A URL-mode run started with the list left untouched (or the field omitted through the API) scrapes those examples, bounded by Max listings, instead of finding nothing to do. Pass your own list to scrape something else.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/kijiji-scraper").call(run_input={"mode": "search","keywords": ["iphone"],"location": "city-of-toronto","maxListings": 50,})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["price"], listing["url"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/kijiji-scraper').call({mode: 'search',keywords: ['iphone'],location: 'city-of-toronto',maxListings: 50,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
How Max pages and Max listings shape a run
Max pages bounds how many result pages are walked per search or pasted URL (40 listings per page); 0 walks until the run stops on its own, at the reported total, an empty page, or a page that returns nothing new to that walk. Max listings is the one setting that bounds how many listings are saved for the whole run; 0 collects everything available (paid plans only), limited only by Max pages and the natural end of results. A pasted single-listing URL returns at most one record (a price filter, a Resume-seeded id, or an UNCHANGED classification in Incremental mode can drop it to zero). With Fetch listing details on (the default), the full description text and complete photo set are always included; a handful of search-only fields are not available from the listing page itself and come back null, including imageCount (the photos in imageUrls are still complete), posterRating, nearestIntersection, hasVirtualTour, and every attr_*_label field (the labels for category-specific attributes, which only the search page's own filter metadata supplies). With detail off, a pasted single-listing URL still returns the full description and photo set, since the listing page has to be read either way to identify the listing; it drops the flat detail-only fields (views, status, and similar), but the nested posterInfo object, which can include the seller's phone number and website, is still copied through in full either way, so switching detail off does not withhold seller contact info for a pasted listing link the way it does for a search result.
Resume, automatic recovery, and Incremental mode
These are three separate features:
-
resumeFromRunIdcontinues one specific prior run (or dataset) as a delta: paste its run or dataset id and this run skips every listing already collected there, walking the full search again but returning, and charging for, only what's new. A page made entirely of listings you already have does not stop the walk early; it keeps going until it reaches genuinely new listings or the natural end of the search. -
Automatic recovery needs no input at all: if the platform migrates the run or it is manually restarted, the run picks its checkpoint back up (same search position, same collected ids) and continues, so a restart never re-collects or re-charges a listing already pushed.
-
Incremental mode (
incrementalMode) is for a search or URL list you run on a schedule. Each listing is classified against the actor's own memory of the last run for that search:changeTypeWhen it fires NEWNot seen in any previous run for this search. UPDATEDSeen before, and at least one field genuinely changed ( changedFieldslists which).UNCHANGEDSeen before, nothing changed. Not returned by default; turn on emitUnchangedto get it anyway.REAPPEAREDThe listing was previously marked gone by an EXPIRED run for this search, and has now been scraped again. EXPIREDPreviously seen, not found in this run, and this run reached the natural end of every tracked search/URL. Only produced when emitExpiredis on.emitExpiredgates more than which rows you see: only a run withemitExpiredon, that also scans its whole tracked search or URL list to the natural end (nomaxListings/maxPagescap reached, noresumeFromRunId, no page that failed to load), ever marks a listing as gone in the actor's memory. If that condition is never met, a listing that stops appearing simply keeps its last-known status; it is never silently marked gone, andREAPPEAREDcan therefore never fire for it either.REAPPEAREDonly exists at all for a listing that some earlier run had already marked gone this way, and is now back.viewsandsortingDatenever trigger a falseUPDATED(Kijiji advances both independently of any real edit); every other field, includingpriceanddescription, participates unless you list it in Ignore fields when detecting changes (ignoreFieldsForChanges, incremental mode only). That input takes exact output field names or dotted paths such asposterInfo.rating; the listed fields stay in the output but never make a listingUPDATEDor appear inchangedFields. Changing the list takes effect immediately, without a one-time wave ofUPDATEDrows. The state key is built from the search settings: in search mode the pre-filled example list in Kijiji URLs is ignored when building it (it is not used by a search run), so a schedule keeps its saved state; a URL list you actually enter is part of the key.
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.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | Search by keyword + location, or paste Kijiji URLs directly. |
keywords | array | (none; prefilled with ["iphone"]) | Search terms (Search mode). Each term runs as its own all-category search. Empty browses everything in the location. |
location | string | canada (prefilled with city-of-toronto) | City, region, or province name, a Kijiji location slug, or a numeric location id. |
sortBy | string | relevance | Result ordering: relevance, newest first, price low to high, or price high to low. Search mode only; Kijiji still inserts a few promoted ads at the top regardless of sort. |
urls | array | one example of each supported link shape (see "URL mode" above) | Kijiji search, category or single-listing URLs (URL mode). Multiple supported; pagination walks forward from the page in each URL. |
minPrice | integer | (none) | Minimum price in Canadian dollars. Applies to Search mode and URL mode alike. Once either bound is set, a listing with no numeric price (e.g. "Please Contact", "Free", "Swap") is dropped. |
maxPrice | integer | (none) | Maximum price in Canadian dollars. Applies to Search mode and URL mode alike; see minPrice for the no-price-listing behavior. |
fetchDetails | boolean | true | Open each listing's detail page for view count, seller phone + website, listing status, virtual tour / video, and dealer profile. |
maxPages | integer | 0 | Optional bound on result pages walked per search/URL (40 listings per page). 0 walks until the run stops on its own: the reported total is reached, a page comes back empty, or a page returns nothing new to that walk. Does not cap how many listings are saved; use Max listings for that. |
maxListings | integer | 20 | Maximum listings to save across the whole run. 0 collects every available listing (paid plans only), bounded only by Max pages and the natural end of results. |
resumeFromRunId | string | (none) | ID of a previous run (or dataset) of this actor. Listings already there are skipped, so this run returns only new listings (a delta). |
incrementalMode | boolean | false | Recurring monitoring: remembers the listings seen last time for this search and, by default, returns only NEW, UPDATED or REAPPEARED ones. |
stateKey | string | (none; derived automatically from your filters) | Optional label for the tracked search when Incremental mode is on. |
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, and only when this run reached the natural end of every tracked search/URL. |
ignoreFieldsForChanges | array | [] | Incremental mode only: fields never counted as a change (views and sortingDate are always ignored). |
proxy | object | Apify Residential, country CA | Connection settings. Kijiji.ca works most reliably on a Canadian residential connection. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which records are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live listing.
{"id": "1700000000","listingType": "AutosListing","title": "Sample listing title","description": "Full listing description appears here.","url": "https://www.kijiji.ca/v-cars-trucks/city-of-toronto/sample-listing/1700000000","price": 12500.0,"priceRaw": 1250000,"priceType": "FIXED","currency": "CAD","categoryId": 174,"locationName": "Toronto","address": "1 Placeholder Crescent, Toronto, ON, M0M 0M0","latitude": 43.65,"longitude": -79.4,"nearestIntersection": ["Sample Road", "Example Avenue"],"imageUrls": ["https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/00/00000000-0000-0000-0000-000000000000?rule=kijijica-200-jpg"],"imageCount": 8,"posterId": "1700000001","sellerType": "PVT","posterRating": 4.5,"posterVerified": false,"activationDate": "2026-01-01T00:00:00.000Z","isTopAd": false,"attr_carmake": "ford","attr_noofseats": "4","views": 342,"status": "ACTIVE","phoneNumber": "4165550123","sellerWebsiteUrl": "https://example.com"}
Plan Requirement
Kijiji.ca is most reliable over a Canadian residential connection, which is the default proxy setting (Apify Residential, country CA) and requires an Apify Starter plan or higher. A datacenter connection loaded Kijiji.ca search and category pages in a single check, but it was not tested at volume and the site may refuse datacenter addresses once a run makes many requests, so use residential for scheduled or large runs. The run automatically retries with a different connection when one is refused. A run that cannot reach Kijiji.ca at all still finishes successfully, with zero listings and a status message pointing at the proxy setting, rather than exiting as an error.
FAQ
How much does it cost?
Billing has three parts: a start charge once per run (apify-actor-start), a charge for each listing pushed to the dataset (apify-default-dataset-item), and a detail-enrichment charge (detail-enrichment) for each pushed listing whose detail page was opened (turned on by Fetch listing details). Suppressed Incremental-mode rows (UNCHANGED, by default), skipped duplicates and listings that were not pushed are never billed. The Pricing tab shows the current rates; use Max listings to cap the cost of any run.
The dataset also ships saved views in the Console (listings overview, seller and contact, changes, detail enrichment, vehicles, and real estate and location) that group the same fields into tables; they do not change the output or the billing.
Is it legal to scrape Kijiji.ca?
This actor collects only publicly available classified-listing data. You are responsible for how you use it: follow Kijiji's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution, especially of seller contact details.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor and turn on Incremental mode. See "Resume, automatic recovery, and Incremental mode" above for exactly when REAPPEARED and EXPIRED can occur; a quiet run with nothing changed can legitimately return zero listings, which is not an error.
Why did my run return fewer listings than I expected?
Max listings and Max pages both cap a run; raise them, or set Max listings to 0, to collect more. A price filter also drops any listing with no numeric price (e.g. "Please Contact", "Free", "Swap") once either bound is set.
Why does a fully blocked run finish "succeeded" with zero listings instead of failing?
An empty result really can mean nothing matched, so a run that scans nothing still finishes successfully rather than exiting as an error; the run's status message explains what happened and recommends a Canadian residential connection. If you're automating this, check the returned listing count (or the status message) rather than only the run status.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more classifieds data?
Pair this actor with these related scrapers from the same team:
| 🏷️ Leboncoin FR Pull public leboncoin.fr listings across cars, property, jobs, electronics, furniture... | 🏷️ Mudah.my Cars, Property, Jobs & Marketplace Scraper Scrape Mudah.my listings across every category: cars, motorcycles, property, mobiles... |
| 🚗 Trade Me NZ Scraper Scrape trademe.co.nz across all five sections: Marketplace goods and auctions, Property... | 🏷️ Tutti.ch Scraper Scrape listings from tutti.ch across all 23 categories, including vehicles, property... |
| 🚗 Willhaben.at Scraper Scrape Willhaben.at listings into clean JSON with 40+ structured fields, including... | 🏷️ Kleinanzeigen.de Scraper Scrape Kleinanzeigen.de ads by keyword, category, location, price, seller type, or search... |
💬 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.