Kijiji Real-Time Data — Canada Classifieds & Resale Scraper
Pricing
from $1.50 / 1,000 results
Kijiji Real-Time Data — Canada Classifieds & Resale Scraper
Live Kijiji data for vintage & thrift resellers: keyword + location search tasks, full listing details, seller inventory & profiles, sold comps, and any-URL scraping. Freshness, seller-type, and photo tags on every row — predictable volume, real-time webhooks, pay per result.
Pricing
from $1.50 / 1,000 results
Rating
0.0
(0)
Developer
Emmanuel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Kijiji Real-Time Data
Live resale market intelligence for Kijiji — thrift price comps, seller inventory, vintage deal finder. Keyword search, full listing details, seller tracking, sold comps, and URL collection. Checkbox features — enable only what you need. Structured JSON streamed to your dataset in real time.
💎 Paid plans get the full experience. Free Apify plans are capped at 2 results per run so you can try the actor before upgrading. Upgrade to any paid Apify plan for unlimited vintage and thrift data exports.
What it does
Kijiji is Canada's largest classifieds marketplace — and one of the most underrated sourcing channels for thrift and vintage resellers. Every day, private sellers list vintage Carhartt, single-stitch band tees, 90s Levi's, gorpcore shells, and underpriced designer pieces for far below their resale value.
This actor gives you structured, real-time access to that market:
- Find underpriced inventory fast — live keyword search with price, seller-type, and photo filters
- Pull full product details — descriptions, seller-filled attributes (brand, size, condition), complete photo sets, view counts
- Track sellers — a seller's entire active inventory, their profile stats, ratings, and verification status
- Comp your prices — ended and removed listings per seller for margin and sell-through analysis
- Automate everything — Slack/Discord deal alerts via webhooks, or pipe rows into Airtable, Sheets, or your pricing bot
Who it's for
- Vintage & thrift resellers cross-listing on Poshmark, eBay, Depop, Mercari, and Grailed who source from Canadian classifieds
- Resale arbitrageurs hunting undervalued vintage single-stitch, designer denim, gorpcore, and Y2K pieces
- Consignment & vintage shop owners monitoring local inventory and pricing
- Pricing teams & market researchers building multi-platform comps datasets
- AI agents & MCP workflows that need clean, structured resale data
Feature matrix
| Feature | What you get | Output featureType |
|---|---|---|
| 🔍 Listing Search | Search-task list: each row pairs a keyword with an optional location (city/region/province) and category, plus price / seller-type / photo filters and sort order. On by default. | listing_search |
| 📦 Listing Details | Full record for specific listings by URL or ID: description, all seller-filled attributes, full gallery, views, status. | listing_details |
| 👚 Seller Inventory | A seller's complete active listing inventory — track competitor stock and new drops. | closet_listings |
| 👤 Seller Profile | Display name, seller type (private/dealer), inventory size, verification, member since, review summary. | seller_profile |
| 🧾 Sold Item Comps | Ended and removed listings per seller — the comp data behind pricing decisions. | sold_history |
| 🔗 Scrape By URL | Paste any Kijiji URL (search, category, seller, single listing) — page type is auto-detected. | scrape_by_url |
Enrichment, not duplication: when "Enrich with full listing details" is enabled on Listing Search, each search card is upgraded in place — you still get exactly one row per listing (with detailsFetched: true), just with the full description, attributes, and gallery merged in. No duplicate rows, nothing filtered out.
Tags, not filters — predictable run volume: the actor enables details instead of filtering by them. Every listing it discovers goes to the dataset, even if some fields are empty. Freshness (alert mode), category, and location are the only things that scope which listings are searched — never how many rows come back. If you ask for 100 results per keyword, you get 100 rows. That makes per-run volume (and therefore cost) fully predictable: a 1,000-listing run is exactly 1,000 dataset rows, every time. Filtering happens on your side, downstream, on the tag columns (is_newly_listed, posted_age_days, category_path, …).
Quick start
- Click Start — Listing Search is on by default with three demo search tasks pre-filled (keyword + location / category pairs).
- Within seconds you'll have structured results in your dataset — one row per listing discovered.
- Edit the Search tasks list: one row per search, pairing a keyword with an optional place (e.g.
City of Toronto) and category (e.g.men's shoes) — like a maps-style keyword + location search. - Open the dataset table to browse, or grab the JSON/CSV/Excel export.
Practical search examples
Each row of the Search tasks list is one search — a keyword, optionally pinned to a place and a category:
| Goal | Keyword | Location | Category |
|---|---|---|---|
| Vintage workwear flips in Toronto | carhartt detroit jacket | Toronto (GTA) | — |
| Denim sourcing province-wide | vintage 90s levis 501 | Ontario | — |
| Sneakers, men's only | nike jordan | — | men's shoes |
| Band tees in Vancouver | single stitch band tee | Vancouver | — |
| Anything free, anywhere | free | — | — |
Feature-wide filters (price, seller type, photos, sort) in the section below apply to every task.
Location targeting
Like a maps-style search, you combine a keyword with a place. The Location column of a search task accepts city, region, or province names — or a numeric location id:
- Provinces:
Ontario,Quebec,Alberta,British Columbia - Regions:
Toronto (GTA),Kitchener Area,Windsor Region,Muskoka - Cities:
City of Toronto,London,Barrie,Vancouver
The first match wins per task; matching walks the marketplace's official location tree (Canada → provinces → regions → cities), so names resolve to the same ids the site itself uses. Leave the Location column empty to search all of Canada — and if a name can't be matched, the task falls back to all of Canada with a warning in the log. Every row still carries the seller's own city in seller_location.
Input reference
🔍 Listing Search
| Field | Type | Default | Notes |
|---|---|---|---|
| Listing Search | checkbox | true | Master switch for keyword search |
| Search tasks | task list | 3 demo tasks | One row per search: Keyword (required) + optional Location, Category, and per-task Max results. This — not filtering — is what sets your run volume |
| Default max results per task | integer | 10 | 1–200; used when a task omits Max results |
| Category scope / Location for all tasks | string list | empty | Advanced: apply one scope to every task; multiple selections are searched any-of. Prefer the per-task columns |
| Sort order | select | relevance | relevance, newest (best for deal alerts), price_low_to_high, price_high_to_low |
🔍 Search keywords (alternative)
Simple mode without the task table: a plain keyword list, searched everywhere with no per-task location/category. Only used when the Search tasks list is empty.
| Field | Type | Default | Notes |
|---|---|---|---|
| Search keywords | string list | empty | e.g. carhartt detroit jacket, vintage 90s levis 501 |
Feature-wide search filters (apply to whichever search mode above provided the keywords):
| Field | Type | Default | Notes |
|---|---|---|---|
| Min / Max price | integer | empty | CAD |
| Price type | select | Any | Fixed, Contact seller, Give away, Swap/trade |
| Alert mode (newest first) | checkbox | off | Sorts newest-first and tags every row with is_newly_listed + posted_age_days. Nothing is filtered out — run volume stays exactly what you set |
| Freshness window (days) | integer | 7 | Listings posted within this window are flagged is_newly_listed — a tag for downstream filtering, not a row filter |
| Enrich with full listing details | checkbox | off | Upgrades each search row in place with full details (adds a little extra time per listing) |
Tags, not filters: owner-vs-dealer and photo completeness are not run options — every row carries is_private_seller (true = private seller, false = dealer) and has_image so you filter downstream, in your spreadsheet or code. This keeps run volume exactly what you asked for: a 1,000-listing run costs 1,000 result events and returns 1,000 rows, every time, regardless of how many happen to be dealer rows or photo-less.
📦 Listing Details
| Field | Type | Notes |
|---|---|---|
| Listing IDs | string list | The long number at the end of a listing URL, e.g. 1737865031 |
| Listing URLs | string list | Full https://www.kijiji.ca/v-... URLs |
👚 Seller Inventory · 👤 Seller Profile · 🧾 Sold Item Comps
| Field | Type | Default | Notes |
|---|---|---|---|
| Seller IDs | string list | — | The seller's numeric ID — from a profile URL like kijiji.ca/o-profile/1015260943/.... Shared by all three features |
| Max listings per seller | integer | 30 | Seller Inventory, 1–500 |
| Max items per seller | integer | 30 | Sold Comps, 1–500 |
🔗 Scrape By URL
| Field | Type | Notes |
|---|---|---|
| Kijiji URLs | string list | Search results, category pages, seller profiles, or single listings. Page type is auto-detected; non-Kijiji URLs are rejected |
🔔 Delivery
| Field | Type | Default | Notes |
|---|---|---|---|
| Webhook URL | string | empty | Every record is always saved to the dataset; this is an additional real-time POST per record (Slack, Discord, Zapier, Make, n8n, custom bots) |
| Webhook format | select | json | json (full record) or slack (formatted message) |
🌐 Connection
| Field | Notes |
|---|---|
| Proxy settings | Residential proxy with CA (Canada) exits is strongly recommended — it is the most reliable setup for this Canadian marketplace. Defaults are preconfigured. |
Output field reference
Every row carries featureType (which feature produced it), scrapedAt, and url. Filter your dataset view by featureType.
Core listing fields (search, details, inventory, comps, URL scrape)
| Field | Type | Description |
|---|---|---|
item_id | string | Native listing ID (stable, use for dedupe) |
item_url | string | Canonical listing URL |
title | string | Listing title |
description | string | Seller-written description (search cards get a short excerpt; full text on enriched/details rows) |
main_image_url | string | Cover photo |
additional_image_urls | string[] | Full photo gallery |
status | string | available, sold, reserved, … |
current_price | number | Asking price (major units, e.g. 150.00) |
original_price | number | Seller's stated original price, when set |
currency | string | CAD |
discount_percentage | number | Markdown vs. original price |
price_drop_amount | number | Absolute price drop |
price_type | string | fixed, contact_seller, give_away, swap_trade, make_offer |
brand | string | From seller-filled attributes (Carhartt, Levi's, Patagonia, …) |
size | string | Seller-filled size |
condition | string | Seller-filled condition |
category / department / subcategory | string | Catalog classification where available |
category_id | string | Numeric catalog category id — stable key for category-scoped sourcing |
category_path | string | Full catalog path, e.g. Clothing > Women's - Tops & Outerwear |
color / material / style_tags | string / string[] | Seller-filled style attributes |
created_at | string | When the ad was posted |
updated_at | string | Last bump / sort timestamp |
is_sold | boolean | Sold/ended flag |
is_price_dropped | boolean | Seller used a price drop |
is_newly_listed | boolean | Posted within the freshness window (default 7 days) — a tag, never a row filter |
posted_age_days | number | Whole days since the ad was posted, so you can slice freshness downstream |
seller_username / seller_id | string | Seller's numeric ID (the stable identity on this marketplace) |
seller_verified | boolean | Platform-verified seller |
seller_location | string | Listing location (city/province) |
position | number | Rank on the page it was found on |
detailsFetched | boolean | Search rows only: true when enriched in place |
Listing Details extras
views_count (view counter), item_specifics (every seller-filled attribute group), image_count, end_date, has_video, is_featured, is_top_ad, is_highlight, seller_type.
Seller Inventory (closet_listings)
One row per active item: core pricing/product fields + seller_username, seller_id, location, created_at.
Seller Profile (seller_profile)
| Field | Description |
|---|---|
display_name | Seller's display name |
seller_type | FSBO (private) or commercial |
total_listings | Active inventory size |
is_verified | Verification status |
member_since | Account age |
rating / total_reviews | Review summary |
profile_url | Canonical profile URL |
Sold Item Comps (sold_history)
One row per ended/removed listing with pricing, product fields, status, and sold_at where determinable. Use for margin analysis and sell-through.
Scrape By URL (scrape_by_url)
Adds pageType (listing, seller, search, category) to the row so you can segment bulk URL runs.
Webhooks & deal alerts
Set Webhook URL and every record is POSTed the moment it's collected — perfect for Slack/Discord deal bots. Turn on Alert mode to sort results newest-first so the freshest inventory arrives first; every row still lands in the dataset (tagged, not filtered), so your alert volume stays predictable.
jsonformat — the full record object. Ideal for Zapier, Make, n8n, or your own automation.slackformat — a ready-to-post Slack message with title, price, brand, seller, and a link.
{"featureType": "listing_search","title": "Vintage Carhartt Detroit Jacket","current_price": 150,"currency": "CAD","brand": "Carhartt","item_url": "https://www.kijiji.ca/v-...","is_newly_listed": true}
Webhook delivery is best-effort and never blocks or fails the run — the dataset write always happens first.
Using with AI agents (MCP)
This actor is Apify MCP-compatible. Point your agent at it and ask things like:
- "What's the average asking price for a 90s Carhartt J97 jacket on Kijiji right now?"
- "Find vintage Levi's 501 listings under $60 posted this week and alert me on Slack."
- "Track seller 1015260943's inventory and tell me when they list new denim."
- "How fast do vintage band tees sell for sellers in Toronto?"
Free tier & paid plans
| Plan | What you get |
|---|---|
| Free | 2 results per run — enough to validate data quality and see the output structure |
| Any paid plan | Unlimited results, every feature, full enrichment |
Free-tier runs exit gracefully with a clear message (never an error), and the paywall object in the run OUTPUT discloses exactly what was applied. Owners can configure the behavior with Console environment variables (FREE_TIER_MODE = limit/block, FREE_TIER_MAX_ITEMS).
FAQ
How fresh is the data? Every run hits the live marketplace — results reflect what's listed at that moment. Use "Just listed" sort for deal alerts on fresh inventory.
Do I need proxies? Residential proxies (CA) are configured by default and recommended for reliability. This is a Canadian marketplace, so Canadian exit addresses work best.
Can I get sold prices? Sellers curate their public inventory and ads leave the feed once ended or sold. Sold Comps collects each seller's ended/removed listings alongside active ones — the closest available comp signal on this platform.
One row or two per listing? Always one. Enrichment merges into the same search row (detailsFetched: true) — never a duplicate.
Rate limits? The actor paces requests with small random pauses, reuses warmed sessions, and retries transient failures automatically. Keep per-run volume sensible (hundreds, not tens of thousands, of enrichments) and split very large jobs into runs.
Is the seller ID the same as a username? This marketplace uses numeric seller IDs (visible in profile URLs) rather than public usernames. That ID is the stable key for Seller Inventory, Seller Profile, and Sold Comps.
Pricing models & monetization
This actor uses Pay-Per-Event — you're charged per result event (result), so a run that finds nothing costs nothing beyond platform usage. Set your max run cost in Apify Console to control spend; the actor respects spending limits and stops gracefully when the budget is reached.