# Kijiji.ca Scraper — Canadian Classified Listings (`muhammadafzal/kijiji-ca-scraper`) Actor

Scrape permitted Kijiji.ca search-result pages for listing titles, prices, descriptions, locations, sellers, images, dates, and attributes. Supports pagination and optional detail-page enrichment.

- **URL**: https://apify.com/muhammadafzal/kijiji-ca-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Automation, MCP servers, E-commerce
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 listing returneds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Kijiji.ca Scraper — Canadian Classified Listings

Scrape Kijiji.ca search-result pages that you are authorized to access and return stable, agent-friendly listing records. The actor supports multiple search URLs, Kijiji’s `page-N` pagination format, Canadian English/French browser headers, and residential proxy routing.

### What it extracts

Each dataset item represents one public Kijiji listing:

| Field | Description |
| --- | --- |
| `title` | Public listing title |
| `listingUrl` / `listingId` | Canonical public URL and ID when available |
| `priceAmount` / `priceText` / `currency` | Numeric and human-readable price |
| `description` | Public description when exposed |
| `location` | Public city, region, or area |
| `sellerName` / `sellerType` | Public seller label/classification when available |
| `postedAt` | Public creation/publication time when available |
| `imageUrls` | Public listing image URLs |
| `attributes` | Flat fields such as mileage, bedrooms, condition, or employment type |
| `sourceSearchUrl` | Search URL that produced the item |
| `detailsFetched` / `_warnings` | Stable schema flag and non-fatal extraction notes; detail pages are not crawled |

The output is intentionally generic across Kijiji cars, real estate, jobs, services, and Buy & Sell categories. Vertical-specific values are preserved in `attributes` when Kijiji exposes them; the actor does not invent missing fields.

### When to use it

Use this actor for permitted market research, resale research, price monitoring, inventory checks, rental research, and AI workflows that need structured Canadian classified listings. It is designed for a narrow outcome: search-result listing extraction from Kijiji.ca.

Do not use it for private messages, watchlists, login-only information, hidden phone/email discovery, account automation, CAPTCHA solving, or bypassing access controls. For a single known listing, pass a search URL that contains the listing or use a dedicated detail-page workflow; this actor’s primary input is a search-results page.

### Important compliance notice

Kijiji’s current Terms of Use state that users may not use a robot, spider, scraper, or other automated means to access Kijiji and collect content without Kijiji’s express written permission. Only run this actor where you have that permission or another lawful, contractually valid basis. You are responsible for complying with Kijiji’s terms, applicable privacy/data-protection law, robots instructions, proxy-provider rules, and any restrictions attached to the data you collect. The actor does not provide permission to scrape and does not bypass authentication, CAPTCHAs, or technical access controls.

### Input

#### `searchUrls`

An array of full Kijiji search-result URLs. The default is a small Canada-wide cars search:

```json
{
  "searchUrls": [
    "https://www.kijiji.ca/b-cars-trucks/gta-greater-toronto-area/c174l1700272",
    "https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273"
  ],
  "maxResults": 50,
  "maxPages": 2
}
```

The actor follows Kijiji’s path-style pagination, for example `/page-2/`, and counts `maxResults` across all supplied URLs. It removes duplicate listing URLs within a run.

#### `maxResults`

Maximum total listing records to return, from 1 to 500. Each returned listing is one billable `listing-returned` event. The actor prints the maximum listing-event cost at run start and stops at the configured cap.

#### `maxPages`

Maximum pages to visit per search URL, from 1 to 20. Keep this at 1 for quick health checks and raise it only when you need deeper pagination.

#### `language`

Controls the browser’s `Accept-Language` header: `all`, `en`, or `fr`. It does not translate listing content.

#### Proxy settings

Apify residential proxy routing is enabled by default and uses Canada (`CA`). You can set `useApifyProxy` to `false` only when direct access is compliant and appropriate. `proxyCountry` changes network geography; it does not change the Kijiji location encoded in your search URL.

### Output example

```json
{
  "listingId": "1701234567",
  "title": "2019 Honda Civic EX",
  "description": "One-owner vehicle with winter tires.",
  "priceAmount": 18500,
  "priceText": "$18,500",
  "currency": "CAD",
  "location": "Toronto, ON",
  "sellerName": "Maple Auto Sales",
  "sellerType": "professional",
  "postedAt": "2026-07-30T16:00:00Z",
  "listingUrl": "https://www.kijiji.ca/v-cars-trucks/toronto/2019-honda-civic/1701234567",
  "imageUrls": ["https://media.kijiji.ca/api/v1/ca-prod-dealer-ads/images/example.jpg"],
  "attributes": { "mileage": 85000, "condition": "Used" },
  "sourceSearchUrl": "https://www.kijiji.ca/b-cars-trucks/canada/c174l0",
  "scrapedAt": "2026-08-01T10:30:00.000Z",
  "detailsFetched": false,
  "_warnings": []
}
```

### API usage

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('USERNAME/kijiji-ca-scraper').call({
  searchUrls: ['https://www.kijiji.ca/b-buy-sell/city-of-vancouver/c10l1700287'],
  maxResults: 100,
  maxPages: 3
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map(({ title, priceAmount, listingUrl }) => ({ title, priceAmount, listingUrl })));
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("USERNAME/kijiji-ca-scraper").call(run_input={
    "searchUrls": ["https://www.kijiji.ca/b-apartments-condos/city-of-montreal/c37l1700281"],
    "maxResults": 50,
    "maxPages": 2,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["priceText"], item["listingUrl"])
```

### Pricing

The actor uses Pay-Per-Event pricing:

| Event | Price |
| --- | ---: |
| Actor start | $0.00005 per run, scaled by memory |
| Listing returned | $0.005 per listing |

Example: a 100-listing run has an estimated listing-event charge of $0.50 plus the actor-start event and normal Apify compute/proxy usage. A run capped at 25 listings cannot incur more than 25 listing-returned events. Pay-Per-Usage is also available through Apify’s standard usage billing for users who prefer compute/proxy pass-through.

### Reliability and limits

The actor uses a real browser with Crawlee sessions, conservative concurrency, retries, and Canadian residential proxy defaults. Kijiji may return a challenge page, a temporary block, no results, or a changed page structure. Search-page results are pushed as they are processed. If every request fails before any page completes, the run fails honestly with a retry suggestion.

The parser prefers structured page data such as Next.js hydration JSON and JSON-LD, then falls back to visible listing-card anchors. It uses multiple field-name fallbacks rather than depending on one CSS class. `attributes` is best-effort and may differ by Kijiji category.

### FAQ

**Does this include seller phone numbers or emails?** No. It returns only public fields exposed in permitted page content and does not search for hidden contact information.

**Can it scrape all of Canada?** Yes, if the supplied Kijiji URL represents that scope and your authorized collection workflow permits it. Set a suitable `maxResults` and `maxPages` cap.

**Why is my dataset empty?** A valid search can have no matching listings, or Kijiji may have served a challenge/blocked response. Check the run log and `RUN_SUMMARY`; the actor distinguishes a valid empty result from total request failure.

**Why are some fields null?** Kijiji does not expose every field on every search card. The actor preserves explicit nulls and records only non-fatal extraction notes in `_warnings`.

**How should I report a parser issue?** Include the run ID, exact search URL, actor input, and a sanitized example of the missing field. Do not include account credentials, private messages, or personal contact data.

# Actor input Schema

## `searchUrls` (type: `array`):

Use this when you have one or more Kijiji.ca search-result URLs to scrape. Pass full HTTPS URLs such as https://www.kijiji.ca/b-cars-trucks/canada/c174l0. Defaults to a small Canadian cars search. This is not a list of individual seller contact pages.

## `maxResults` (type: `integer`):

Use this to cap the number of listing records returned and billed. Accepts 1–500 and defaults to 50; for example, set 25 for a quick sample. This is a total across all URLs and pages, not a per-page limit.

## `maxPages` (type: `integer`):

Use this to control Kijiji pagination for each search URL. Set 1 for the first page or a value up to 20, such as 3, to follow Kijiji page-N URLs. Defaults to 1. This does not create new keyword searches.

## `language` (type: `string`):

Use this to prefer English or French Kijiji content during navigation. Choose all for the default Canadian language negotiation, en for English, or fr for French. Defaults to all. This changes request headers only and is not a content translator.

## `useApifyProxy` (type: `boolean`):

Use this when your permitted collection workflow requires a stable Apify residential proxy. Defaults to true with Canada routing. Set false only when you have a compliant direct-access network; this is not an anti-bot bypass switch.

## `proxyCountry` (type: `string`):

Use this to select the two-letter proxy country for geo-sensitive Kijiji results. Defaults to CA and accepts a code such as CA. This affects network location only, not the Kijiji search location encoded in your URL.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.kijiji.ca/b-cars-trucks/canada/c174l0"
  ],
  "maxResults": 25,
  "maxPages": 1,
  "language": "all",
  "useApifyProxy": true,
  "proxyCountry": "CA"
}
```

# Actor output Schema

## `dataset` (type: `string`):

All extracted Kijiji listing records.

## `datasetCsv` (type: `string`):

The same listing records as CSV.

## `runSummary` (type: `string`):

Request, result, billing, and warning summary.

## `consoleRun` (type: `string`):

Open logs and output in Apify Console.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchUrls": [
        "https://www.kijiji.ca/b-cars-trucks/canada/c174l0"
    ],
    "maxResults": 25,
    "maxPages": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/kijiji-ca-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchUrls": ["https://www.kijiji.ca/b-cars-trucks/canada/c174l0"],
    "maxResults": 25,
    "maxPages": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/kijiji-ca-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchUrls": [
    "https://www.kijiji.ca/b-cars-trucks/canada/c174l0"
  ],
  "maxResults": 25,
  "maxPages": 1
}' |
apify call muhammadafzal/kijiji-ca-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=muhammadafzal/kijiji-ca-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/MTWixXkBydghJXNma/builds/YE0QeJ8dt0XmvgRMz/openapi.json
