# Carsales.com.au Scraper: Price, Odometer, Year (`themineworks/carsales-scraper`) Actor

Scrape Carsales.com.au vehicle listings by make and model: price, year, odometer, listing URL and photos. Australia residential proxy, no login, pay per vehicle.

- **URL**: https://apify.com/themineworks/carsales-scraper.md
- **Developed by:** [The Mine Works](https://apify.com/themineworks) (community)
- **Categories:** E-commerce, Lead generation, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 vehicles

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## Carsales.com.au Scraper: Price, Odometer, Year

> A single Carsales results page per search, fetched over an Australian residential proxy — no browser involved.

> You are only charged for a vehicle that actually lands in your dataset. A blocked or empty search is never billed.

### Overview

Carsales is Australia's dominant vehicle marketplace, and this actor turns any make and model search into structured rows: title, year, price, odometer reading, body type and a direct link, with photos.

Give it a make such as "toyota", optionally a model such as "camry", and it returns the current listings for sale exactly as carsales itself lists them — one results page per search, roughly 20 to 30 vehicles on Carsales' own "Featured" sort.

Price and odometer parsed as real numbers ✅ · Year parsed from the listing title ✅ · Photos included ✅ · MCP ready for AI agents ✅

### How this gets past Carsales' bot defence, and why it only returns one page

Carsales protects its listing pages with DataDome. A plain HTTP request that mimics a real browser's TLS and header fingerprint is enough to clear it on a fresh residential IP — no interactive challenge, no login, no cookies from a prior visit required. That is the entire transport: one request, one Australian residential proxy session, the same schema.org listing data (JSON-LD) Carsales embeds on every search page for its own SEO.

What this version does **not** do is turn the page. Carsales does not expose page 2 as a URL you can construct — the "Next" control submits a same-page form carrying an opaque, single-use token the server mints only when you click it, so there is no `?page=2` to ask for and no safe way to replay that click without a real browser executing the click itself. Asking for more than one page's worth of vehicles from a single search is therefore not currently possible without reintroducing the exact browser-based blocking this actor exists to avoid. `maxItems` above what one page returns simply gets you everything on that page.

### Input

```json
{
  "make": "toyota",
  "model": "camry",
  "maxItems": 40,
  "minYear": 2020,
  "maxPrice": 40000
}
```

| Field | What it takes |
| --- | --- |
| `make` | Vehicle make, for example "toyota" or "ford". Leave blank to search all makes. |
| `model` | Vehicle model, for example "camry" or "ranger". Leave blank to search every model of the chosen make. |
| `maxItems` | Hard cap for the run. Each search fetches one Carsales results page (roughly 20 to 30 vehicles), so this mostly matters if you want fewer than that. |
| `minYear` | Optional. Only return vehicles from this model year or newer. |
| `minPrice` / `maxPrice` | Optional price band in AUD. |

Year and price filters are applied after fetching the page, because Carsales does not expose them as a URL parameter the way it exposes make and model. This costs nothing extra: the page is already being fetched, and this only decides which rows are worth billing you for.

### Output

```json
{
  "listing_id": "OAG-AD-25834030",
  "title": "2026 Toyota Camry Ascent Auto",
  "year": 2026,
  "make": "Toyota",
  "model": "Camry",
  "price": 43990,
  "odometer_km": 2346,
  "url": "https://www.carsales.com.au/cars/details/2026-toyota-camry-ascent-auto/OAG-AD-25834030/",
  "scraped_at": "2026-08-13T21:00:00.000Z"
}
```

| Field | Description |
| --- | --- |
| 🆔 `listing_id` | Carsales advert ID, stable for deduplication across runs |
| 📄 `title` | Listing title as displayed |
| 📅 `year` | Model year, parsed from the title |
| 🚗 `make` / `model` | Vehicle make and model |
| 💰 `price` | Listed price in AUD, as a number |
| 🛣️ `odometer_km` | Odometer reading in kilometres |
| 🚙 `body_type` | Body type, when the listing discloses it |
| 🖼️ `image_url` / `image_count` | Primary photo and total photo count |
| 🔗 `url` | Direct link to the listing on Carsales |

### Common use cases

**Pricing research.** Build a real price distribution for a make and model by year and odometer, instead of relying on a single valuation guide figure.

**Dealer and private seller monitoring.** Track what is actually listed for a model right now, and how quickly stock turns over.

**Buying alerts.** Run on a schedule against a model and price band to catch new listings the day they go live.

**Fleet and remarketing analysis.** Pull current market supply for specific models to time a fleet disposal or a private sale.

**AI agents.** Feed live Carsales listings into an agent that answers "what is a fair asking price for a 2021 Camry with under 60,000 kilometres right now?"

### Pricing

| Event | Price | You pay when |
| --- | --- | --- |
| Vehicle delivered | $0.0011 | A vehicle lands in your dataset |

**$1.10 per 1,000 vehicles.** A search that returns nothing costs nothing.

### Run it on a schedule

1. Run once with the make and model you want repeated, then click **Save as a task**.
2. In the Apify Console go to **Schedules, Create new**.
3. Pick a frequency and attach the saved task.
4. Wire the dataset to Sheets, Slack or a webhook from the **Integrations** tab.

Deduplicate on `listing_id` so a repeated schedule only surfaces genuinely new listings.

### FAQ

**Do I need a Carsales account?**
No. The actor reads Carsales's own public search results only.

**Why do I only get one page of results?**
Because Carsales does not expose "page 2" as a URL — its "Next" control submits a same-page form carrying a single-use token the server mints only when clicked, with no simpler pattern behind it. Fetching further pages would mean either a real browser clicking through (the approach that used to get this actor DataDome-blocked in production, delivering nothing) or replaying that click over plain HTTP, which Carsales also blocks. One page per search is the honest ceiling today.

**Can I filter by state or location?**
Not in this version. Carsales's location filtering is not exposed as a simple URL parameter the way make and model are, so it is left out rather than shipped half working. Make, model, year and price cover the most common use cases.

**Why did I get fewer vehicles than my cap?**
Because the search has fewer matching vehicles than your cap, your year or price filter narrowed the results, or the one available page returned fewer than `maxItems`. The run reports what it actually found rather than padding the count.

### Use from Claude, ChatGPT and any MCP agent

```
https://mcp.apify.com/?tools=themineworks/carsales-scraper
```

Or call it programmatically:

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('themineworks/carsales-scraper').call({
  make: 'toyota',
  model: 'camry',
  maxItems: 40,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Questions, or need a field we do not return yet? Reach out through the Apify profile.

# Actor input Schema

## `make` (type: `string`):

Vehicle make, for example "toyota" or "ford". Leave blank to search all makes.

## `model` (type: `string`):

Vehicle model, for example "camry" or "ranger". Leave blank to search every model of the chosen make.

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

Hard cap for this run. Each run fetches ONE Carsales results page, which returns roughly 20 to 30 vehicles (Carsales' own "Featured" sort). Multi-page pagination is not available: Carsales' "Next" control is a single-use server-signed token and its bot protection rejects replaying it, so there is no way to reach page 2 without a full browser. Setting this above ~30 simply returns everything on the one page.

## `minYear` (type: `integer`):

Optional. Only return vehicles from this model year or newer. Applied after fetching, since Carsales does not expose this as a URL filter.

## `minPrice` (type: `integer`):

Optional. Only return vehicles listed at or above this price.

## `maxPrice` (type: `integer`):

Optional. Only return vehicles listed at or below this price.

## Actor input object example

```json
{
  "make": "toyota",
  "model": "camry",
  "maxItems": 30
}
```

# Actor output Schema

## `results` (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 = {
    "make": "toyota",
    "model": "camry",
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("themineworks/carsales-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 = {
    "make": "toyota",
    "model": "camry",
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("themineworks/carsales-scraper").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 '{
  "make": "toyota",
  "model": "camry",
  "maxItems": 30
}' |
apify call themineworks/carsales-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/RNhmrhR5fz1jWEIsK/builds/KOlBHQHGZKjbIfVJA/openapi.json
