Kijiji.ca $1๐Ÿ’ฐ Classifieds, Vehicles, Jobs & Property avatar

Kijiji.ca $1๐Ÿ’ฐ Classifieds, Vehicles, Jobs & Property

Pricing

from $1.00 / 1,000 results

Go to Apify Store
Kijiji.ca $1๐Ÿ’ฐ Classifieds, Vehicles, Jobs & Property

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

Abot API

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

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). resumeFromRunId also 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.

FieldExample
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"
price12500.0
priceRaw1250000
priceType"FIXED"
currency"CAD"
categoryId174
locationName"Toronto"
address"100 Sample Street, Toronto, ON, M0M 0M0"
latitude43.6500
longitude-79.4000
nearestIntersection["Sample Road", "Example Avenue"]
imageUrls["https://media.kijiji.ca/api/v1/.../image?rule=kijijica-200-jpg"]
imageCount10
posterId"0000000000"
sellerType"KMB"
posterRating4.5
posterVerifiedfalse
activationDate"2026-01-01T00:00:00.000Z"
sortingDate"2026-01-01T00:00:00.000Z"
isTopAdfalse
attr_carmake"ford"
attr_noofseats"4"
views1140
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

ParameterTypeDefaultDescription
modestring"search""search" for keyword + location, "url" to paste links.
keywordsarray["iphone"]Search terms (search mode). Each runs as its own all-category search. Empty browses everything in the location.
locationstring"canada"City, region, or province name, a Kijiji slug, or a numeric id. Resolved against the live Kijiji location list.
sortBystring"relevance""relevance", "dateDesc", "priceAsc", or "priceDesc". Search mode only.
urlsarray(example)Kijiji search or listing URLs (URL mode). Keywords, Location, and Sort by are search-only and have no effect here.
minPriceinteger(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.
maxPriceinteger(none)Maximum price in Canadian dollars. Applies to both Search mode and URL mode; see minPrice for the no-price-listing behavior.
fetchDetailsbooleantrueOpen each listing page for view count, seller phone and website, status, and more.
maxPagesinteger0Optional 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.
maxListingsinteger20Maximum listings to save for the whole run. 0 = unlimited (paid plans only). This is the one setting that bounds run size.
resumeFromRunIdstring(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.
incrementalModebooleanfalseRecurring 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.
stateKeystring(empty)Optional label for the tracked search in Incremental mode. Auto-derived from filters when empty.
emitUnchangedbooleanfalseIncremental mode only: also return UNCHANGED listings (the full snapshot every run).
emitExpiredbooleanfalseIncremental mode only: also return EXPIRED listings, when this run reached the natural end of the search.
proxyobjectResidential CAProxy 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:

changeTypeMeaning
NEWNot seen in any previous run for this search.
UPDATEDSeen before, and at least one field genuinely changed (changedFields lists which).
UNCHANGEDSeen before, nothing changed. Not returned by default (turn on emitUnchanged to get it anyway).
REAPPEAREDSeen before, then missing for a run, now back.
EXPIREDSeen 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 return UNCHANGED rows, 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 (no maxListings/maxPages cap hit, no resumeFromRunId, 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.
  • resumeFromRunId and incrementalMode are 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 showed views drift with nothing else about the listing changing) and sortingDate (Kijiji's "newest first" ranking timestamp; the same measured listing was a promoted ad, and Kijiji advances a promoted ad's sortingDate to keep it near the top of the default sort independent of the seller editing anything). Every other field, including price and description, participates, since a real change there is exactly what a monitoring user wants surfaced. When fetchDetails is 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.

  1. Authorize a connector once under Apify โ†’ Settings โ†’ Integrations (Notion, Linear, Airtable, or Apify).
  2. Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to 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.