# Kijiji Scraper - Saved Search Alerts for Cars & Rentals (`superslowsloth/kijiji-listings`) Actor

Kijiji scraper: give it Kijiji search URLs (cars, apartments, anything) and get one row per listing with CAD price, posted date, location, photos, and car year/make/mileage or rental bedrooms/baths. Run it on a schedule to alert on new listings.

- **URL**: https://apify.com/superslowsloth/kijiji-listings.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.42 / 1,000 listing scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Kijiji scraper: new-listing alerts for cars, rentals and anything else (Canada)

This Kijiji scraper reads the search pages of [Kijiji](https://www.kijiji.ca), Canada's
classifieds site, and returns one flat row per listing: the asking price in **CAD**,
title, link, when it was posted, where it is, the photos, who is selling it, and the
details that matter for the category. For **cars and trucks** that is year, make, model,
trim, mileage, body type, fuel, drivetrain, transmission and VIN; for **rentals** it is
bedrooms, bathrooms, unit type, square feet, pets, furnished, move-in date and lease type.
Give it the address of a search you ran on kijiji.ca (with its filters) and run it on a
schedule: every run lists the newest listings first, so a **Kijiji new-listing alert** is
a diff of the dataset by `listing_id`.

No account, no API key, no login, no browser. One request returns 40 listings.

### Input

| Field | Notes |
|---|---|
| `searchUrls` | One or more Kijiji search pages, one per line: copy the address from your browser after you search and filter. Examples: `https://www.kijiji.ca/b-cars-trucks/canada/c174l0` (all cars and trucks in Canada), `https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273?price=1500__2500` (Toronto rentals from $1,500 to $2,500), `https://www.kijiji.ca/b-cars-trucks/canada/honda-civic/k0c174l0` (a keyword search). Filters in the query string (`price`, `radius`, `sort`) are kept. A single ad (an address starting `/v-`) is not a search and is refused, at no charge. A `page-N` part in the address is ignored: every search is read from its first page. |
| `maxItems` | Listings to read per search URL (default 100, up to 10,000). Kijiji returns 40 per page. |
| `newestFirst` | On by default. Adds `sort=dateDesc` so each search is read newest first, which is what an alert needs: Kijiji orders a keyword search by best match unless told otherwise, so without it the newest listings can sit beyond `maxItems`. A URL that already has its own `sort` is read as it is. |
| `includePromoted` | Off by default. Kijiji shows up to 6 paid top ads above the first page and picks a different set on every request, so they are left out. Turn on to get them from page 1, flagged `promoted` with a null `position`. |
| `proxyConfiguration` | Apify RESIDENTIAL proxy by default. Kijiji refused 6 of 10 datacenter requests when this was built. |

Example input:

```json
{
  "searchUrls": [
    "https://www.kijiji.ca/b-cars-trucks/canada/honda-civic/k0c174l0?price=3000__9000",
    "https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273?price=1500__2500"
  ],
  "maxItems": 200
}
```

### Output

**One row is one listing.** `listing_id` is the unique key within a search. Prices are numbers
in Canadian dollars (Kijiji sends cents; this is dollars to the cent).

| Field | Notes |
|---|---|
| `search_url`, `position` | The search the row came from (your address, normalised), and Kijiji's own place of the listing in that search's results: 1 is the first result and each page holds 40, so page 2 starts at 41. Null `position` for a promoted ad. |
| `promoted` | True for a paid top-ad placement. The top-ad slot above the first page is only returned when `includePromoted` is on; a promoted ad that Kijiji also lists among the organic results keeps its `position` and is flagged True. |
| `listing_id`, `url`, `title`, `description` | Kijiji's id (a string), the link to the ad, its title, and the ad text **as the results page shows it**: Kijiji cuts it at about 190 characters and ends it with "...". It is not the full ad. |
| `listing_type`, `category_id` | `autos` (vehicles), `real-estate` (rentals and property) or `standard` (everything else), and Kijiji's category id (174 is Cars & Trucks, 37 is Apartments & Condos). |
| `price`, `price_type`, `currency` | The asking price in dollars, how the ad is priced (`FIXED`, `CONTACT` for "Please contact", `GIVE_AWAY` for free), and `CAD`. **`price` is null, never 0, for "Please contact" and "Free"**, and `currency` is then null too. For a rental the price is the rent as listed, normally per month. |
| `original_price`, `price_drop` | The price before a reduction when Kijiji shows one (null otherwise), and Kijiji's price-drop flag. |
| `posted_at`, `refreshed_at` | When the ad was first activated, and the date Kijiji sorts it by (ISO 8601 UTC). **`posted_at` is null on most dealer vehicle ads** (69 of 76 measured): Kijiji sends no activation date for them. `refreshed_at` was present on every row, and is later than `posted_at` when the seller bumped or renewed the ad. Use `refreshed_at` for "newest". |
| `location_name`, `address`, `latitude`, `longitude` | The place name Kijiji files the ad under ("Toronto", "York"), the address as shown (some sellers give only a city and province), and the coordinates. |
| `image_url`, `image_count`, `image_urls` | The first photo (a 200 px JPEG), how many photos the ad has, and the photo links the results page carries (at most 5). |
| `seller_id`, `seller_type`, `seller_verified`, `seller_rating` | Kijiji's poster id; `owner` or `dealer` (vehicles and general ads) or `owner` or `professional` (rentals); Kijiji's verified flag; the poster's average rating when they have one (null otherwise). |
| `year`, `make`, `model`, `trim`, `mileage_km` | Vehicles. `make`, `model` and `trim` are Kijiji's own catalogue codes in lower case and are not always words you would type (`slvrd1500` is a Silverado 1500). Null on other listings. |
| `body_type`, `color`, `fuel_type`, `drivetrain`, `transmission` | Vehicles. Kijiji codes (`suvcrossover`, `gas`, `awd`); `transmission` is `automatic`, `manual` or `semi-automatic`. Null when the ad does not say. |
| `vin`, `vehicle_type`, `price_rating` | Vehicles. The VIN when the ad carries one (all 76 dealer ads measured, 16 of 62 private-seller ones), `used`, `lease` or `damaged`, and Kijiji's `GREAT`, `GOOD` or `FAIR` price rating, which is mostly on dealer ads (71 of 76; 5 of 62 private-seller ones). |
| `bedrooms`, `bathrooms`, `unit_type`, `area_sqft` | Rentals. `bedrooms` can be fractional (Kijiji sends and shows 1.5; 0 appears on studios). `bathrooms` is in whole and half baths: Kijiji sends tenths (10, 15, 20) and this is that value divided by 10. `unit_type` is Kijiji's code (`apartment`, `condo`, `house`, `basement-apartment`, `townhouse`, `duplex-triplex`). `area_sqft` is square feet as the seller typed it. |
| `pets_allowed`, `furnished`, `date_available`, `agreement_type` | Rentals. `yes`, `no` or `limited`; true or false; the move-in date; `one-year` or `month-to-month`. Null when the ad does not say. |
| `condition` | General ads. Kijiji's code (`new`, `usedlikenew`, `usedgood`, `usedfair`) where the category asks for it, null otherwise. |
| `attributes` | **Every attribute Kijiji attached to the ad**, by name, each a list of the values sent: features, Carfax link, dealer stock number, utilities included. The columns above are read from here; the rest only exist here. |

Example row (real values, 2026-11-02, a cars search, first page, `newestFirst` on):

```json
{
  "search_url": "https://www.kijiji.ca/b-cars-trucks/canada/c174l0",
  "position": 1,
  "promoted": false,
  "listing_id": "1745612971",
  "url": "https://www.kijiji.ca/v-cars-trucks/mississauga-peel-region/2013-acura-tl-brand-new-tires-and-brakes/1745612971",
  "title": "2013 Acura TL brand new tires and brakes!",
  "description": "2nd owner new tires new battery Super well maintained drives amazing!!! I am the second owner only I have cared a lot about it and it has - new tires this spring - new battery - new brake pads Comes ...",
  "listing_type": "autos",
  "category_id": 174,
  "currency": "CAD",
  "price": 10500.0,
  "price_type": "FIXED",
  "original_price": null,
  "price_drop": false,
  "posted_at": "2026-11-01T20:05:10.000Z",
  "refreshed_at": "2026-11-01T20:05:10.000Z",
  "location_name": "Mississauga / Peel Region",
  "address": "Erin Mills, Mississauga, ON, L5M5P6",
  "latitude": 43.56955,
  "longitude": -79.72511,
  "image_url": "https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/03/0324073a-17ab-4feb-813c-1043edb0a8df?rule=kijijica-200-jpg",
  "image_count": 8,
  "image_urls": [
    "https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/03/0324073a-17ab-4feb-813c-1043edb0a8df?rule=kijijica-200-jpg",
    "https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/9d/9d22ef85-0f17-42b1-88c9-1c3f2f99cf55?rule=kijijica-200-jpg",
    "https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/9a/9a8492bd-7700-4f0e-b2ac-0c08db2aaa53?rule=kijijica-200-jpg",
    "https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/76/769fe923-1c6d-4481-a73e-9d4de5c63eb9?rule=kijijica-200-jpg",
    "https://media.kijiji.ca/api/v1/ca-prod-fsbo-ads/images/a1/a16cdd3f-1475-41f4-ab69-b818e9439e38?rule=kijijica-200-jpg"
  ],
  "seller_id": "91065481",
  "seller_type": "owner",
  "seller_verified": false,
  "seller_rating": 4.0,
  "year": 2013,
  "make": "acura",
  "model": "tl",
  "trim": null,
  "mileage_km": 149000,
  "body_type": "sedan",
  "color": null,
  "fuel_type": null,
  "drivetrain": null,
  "transmission": null,
  "vin": null,
  "vehicle_type": "used",
  "price_rating": null,
  "bedrooms": null,
  "bathrooms": null,
  "unit_type": null,
  "area_sqft": null,
  "pets_allowed": null,
  "furnished": null,
  "date_available": null,
  "agreement_type": null,
  "condition": null,
  "attributes": {
    "vehicletype": [
      "used"
    ],
    "forsaleby": [
      "ownr"
    ],
    "carmileageinkms": [
      "149000"
    ],
    "carmodel": [
      "tl"
    ],
    "carbodytype": [
      "sedan"
    ],
    "carmake": [
      "acura"
    ],
    "caryear": [
      "2013"
    ]
  }
}
```

#### Honest nulls

A field the page did not carry is `null`, never `0` or `""`; `image_urls` is an empty list
and `attributes` an empty object when there are none. The vehicle columns are null on a
rental and the other way round.

- **No full ad text, no seller phone or name, no per-listing page data.** This is the search
  results page, one row per listing.
- **Not every search term is a different search.** Kijiji reads the code at the end of the
  address (`c174l0`, `k0c174l0`) and ignores the words before it. A mistyped code is not
  an error on Kijiji's side: it answers "0 results", and so does this actor.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is validated, so a run
  that fails on bad input costs nothing.
- **$0.00042** per listing written (`listing-scraped`), i.e. $0.42 per 1,000 listings, 30%
  under the cheapest comparable Kijiji actor on the Apify Store ($0.0006 per result) when
  this one was built. A row that is not written is not charged: a repeat of a listing the
  same search already returned, a search with no results and a search that fails are free.

### How it fetches

It reads the JSON that kijiji.ca's own search pages embed (`__NEXT_DATA__`, the data the
result cards are drawn from), so every field is what the card was drawn from and nothing
is parsed out of the page layout. Measured 2026-11-02 on a probe run of 10 requests per
proxy group from the Apify platform: **RESIDENTIAL answered a real page 10 of 10 times
and datacenter 4 of 10** (the other 6 were HTTP 403), so the actor defaults to the
residential group, uses a browser TLS fingerprint, and treats a refusal (403, 429, 5xx), a
200 that carries no search results, or a dropped connection as the address being
blocked: it rotates to a fresh exit and retries.

Things worth knowing, all measured:

- **Paging.** Page *n* is the address with `page-n` before its code
  (`/b-cars-trucks/canada/page-2/c174l0`), query string kept. Each page says which
  results it holds (`offset`), and the actor treats any other page as a refusal and
  retries.
- **The end of a search.** Asking for a page past the last one does not return an empty
  page: Kijiji answers page 1 again. The actor stops at the page the result count
  implies and, if the count shrinks during a walk, stops when Kijiji serves page 1 instead
  of returning the first page twice.
- **New ads push the order down.** While a walk is under way new listings arrive, so one
  listing can appear at the end of a page and again at the start of the next. A listing
  is returned and charged once per search.
- **Promoted ads are random.** The 6 top ads change on every request and can also be one
  of the organic results, which is why they are off by default.
- **Page weight.** A search page is 0.5 to 1.6 MB of HTML that is roughly 80 to 160 KB on the
  wire (gzip). At 40 listings a page that is about 3.4 KB of proxy traffic per listing.
- **Rate limits.** Kijiji slows an address that asks for many pages quickly (HTTP 429);
  the actor rotates to a new exit and retries.

If Kijiji changes its page layout, the run fails with a message instead of returning
empty rows.

# Actor input Schema

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

Kijiji search pages to read, one per line: the address of a search you ran on kijiji.ca, with its filters. Examples: https://www.kijiji.ca/b-cars-trucks/canada/c174l0 (all cars and trucks), https://www.kijiji.ca/b-apartments-condos/city-of-toronto/c37l1700273?price=1500__2500 (Toronto rentals), https://www.kijiji.ca/b-cars-trucks/canada/honda-civic/k0c174l0 (a keyword search). Filters in the query string (price, radius, sort) are kept. A single ad (an address starting /v-) is not a search and is refused. A page-N part in the address is ignored: every search is read from its first page.

## `maxItems` (type: `integer`):

Read at most this many listings for each search URL. Kijiji returns 40 per page, so 100 reads three pages. Up to 10,000 are accepted.

## `newestFirst` (type: `boolean`):

Order each search newest first by adding sort=dateDesc to the address, which is what a new-listing alert needs. Kijiji's keyword searches default to best match, so without this the newest listings of a keyword search can sit beyond Max listings. A search URL that already has its own sort parameter is read as it is. Turn off to read the searches in Kijiji's default order.

## `includePromoted` (type: `boolean`):

Also return the paid top-ad placements Kijiji shows above the first page of results (up to 6, flagged promoted = true, position null). Kijiji picks a different set on every request, so they are left out by default. They count toward Max listings and are charged like any listing.

## `proxyConfiguration` (type: `object`):

Use Apify's RESIDENTIAL proxy group (the default). kijiji.ca answered Apify datacenter addresses with HTTP 403 on 6 of 10 requests and residential addresses with a page on 10 of 10 when this actor was built. A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.kijiji.ca/b-cars-trucks/canada/c174l0"
  ],
  "maxItems": 30,
  "newestFirst": true,
  "includePromoted": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

No description

# 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"
    ],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/kijiji-listings").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"],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

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

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).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"
  ],
  "maxItems": 30,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call superslowsloth/kijiji-listings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/kijiji-listings"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

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