# Cars.com Scraper - Vehicles, Prices, Dealers & Deals (`abotapi/cars-com-scraper`) Actor

Scrape cars.com listings by make, ZIP and radius. 90+ fields per car: price, MSRP, monthly payment, mileage, VIN, deal rating, price drop, photos, dealer name, rating and location. Search and URL modes, 11 sort orders, price/year/mileage filters, optional CARFAX flags and dealer phone.

- **URL**: https://apify.com/abotapi/cars-com-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Automation, E-commerce, Developer tools
- **Features:** Limited permissions
- **Stats:** 24 total users, 7 monthly users, 91.7% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 vehicle results

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?

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

## Cars.com Scraper: Prices, Deals, Mileage, VIN & Dealer Data

Cars.com Scraper turns cars.com into a clean vehicle data feed. Search by make, ZIP and radius, or paste any cars.com search or vehicle URL, and get a normalized record per car with price, MSRP, estimated monthly payment, deal rating, mileage, VIN, the primary photo, gallery thumbnails and photo count, and the dealer's name, rating and location. Two modes (search and URL), 11 sort orders, and price, year, mileage and stock-type filters. An optional detail pass adds interior color, transmission, the full equipment list, price history and the dealer phone number. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Two ways in.** Build searches from makes, ZIP and filters, or paste cars.com search and vehicle URLs directly. Filter fields are ignored in URL mode, so pasted links are used exactly as given.
- **90+ fields per vehicle,** including deal rating, price versus market explanation, price drop, monthly payment estimate and photo count.
- **A normalized contract on every row.** Price and currency, mileage and unit, dealer city, state and ZIP, ISO dates, and a NEW, USED or CERTIFIED condition, so you never parse a display string like "$2.6K" or "17,417 mi." yourself.
- **Explicit detail status on every row.** Every vehicle carries a `detailLoaded` / `detailError` pair, so you can tell an enriched row from a base row without guessing from null fields.
- **Optional detail enrichment** adds full specifications, the equipment list, seller type, price history with the original list price and days on market, ownership, title, accident and recall flags, and the dealer phone number.
- **11 sort orders** and price, year, mileage and stock-type filters to narrow a search before you pay for results.
- **Built for schedules.** Incremental mode returns only new and changed listings on recurring runs, and a blocked connection is reported in the run's status message instead of looking like an empty search.

### Use Cases

- **Dealer and inventory monitoring:** track price drops, days on market and deal ratings across your own or a competitor's stock.
- **Car shopping and deal-finding tools:** filter by price, year, mileage and stock type to power a search site or alert bot.
- **Market and pricing research:** build historical datasets of asking prices, MSRP and market-average deltas across makes, models and regions.
- **Lead generation:** collect dealer names, ratings, phone numbers and inventory details across a defined market.
- **Vehicle history and condition analysis:** pull ownership, accident and title flags alongside price and mileage for due diligence.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `listingId` | `00000000-0000-4000-8000-000000000001` |
| `vin` | `1FAKEVIN000000001` |
| `year` / `make` / `model` / `trim` | `2025` / `Toyota` / `Crown` / `Limited` |
| `vehicleCondition` | `CERTIFIED` (also `NEW`, `USED`) |
| `priceAmount` / `priceCurrency` | `39298` / `USD` |
| `msrpAmount` | `40985` (null when the seller does not publish one) |
| `mileageValue` / `mileageUnit` | `17417` / `mi` |
| `priceDropAmount` | `2692` (only on listings with a recent price drop) |
| `dealDeltaAmount` | `-805` (negative means priced below the local market average; only on listings the site rates a great deal) |
| `dealerName` | `Example Toyota of Springfield` |
| `dealerCity` / `dealerState` / `dealerZip` | `Irvine` / `CA` / `92618` |
| `dealerDistanceMiles` | `10` (miles from your search ZIP; null in URL mode) |
| `isShippable` / `shippingNote` | `false` / `null` |
| `imageUrl` / `imageCount` | `https://platform.cstatic-images.com/large/in/v2/example.jpg` / `24` |
| `url` | `https://www.cars.com/vehicledetail/00000000-0000-4000-8000-000000000001/` |
| `detailLoaded` / `detailError` | `true` / `null` |
| `stockNumber` | `STK0001` (Fetch full details only) |
| `daysOnMarket` | `10` (Fetch full details only) |
| `scrapedAt` | `2026-09-13T12:00:00+00:00` |
| `changeType` | `NEW` (Incremental mode only; also `UPDATED`, `UNCHANGED`, `REAPPEARED`, `EXPIRED`) |

With Fetch full details on, each row also gets `sellerType` (`DEALER` or `PRIVATE`), `dealer_phone`, `engine`, `transmission`, `interior_color`, `doors`, `cylinders`, `mpg_city` and `mpg_highway`, `features` (the full published equipment list), `priceHistory` (one entry per published price change, oldest first), `originalListPrice` and `priceBelowOriginalAmount`, and `vehicleHistory` (`oneOwner`, `cleanTitle`, `noAccidents`, `serviceRecords`, `openRecall`, `personalUseOnly`, `reportSource`). These keys are present on every row and stay null when detail enrichment is off, so the row shape never changes between runs. cars.com does not publish per-vehicle coordinates, so this actor does not return a latitude and longitude pair either; use `dealerZip`, which is present on every row, to geocode instead.

### How to Use

1. Pick a mode: `search` (build from makes, ZIP and filters) or `url` (paste cars.com search or vehicle links).
2. In search mode, set Makes, ZIP code and Search radius, then narrow with Stock type, price, year or mileage filters as needed. In url mode, paste one or more links; the filter fields are ignored. If you switch to url mode and leave the URLs field untouched, the run scrapes the three example links (one search-results page, one make page and one vehicle page), so clear it first to scrape only your own links.
3. Turn on Fetch full details if you need interior color, transmission, the equipment list, price history or the dealer phone number. It adds a small charge per vehicle.
4. Set Max items to control run size and cost, then click Start.

**Search by make and ZIP:**

```json
{
  "mode": "search",
  "makes": ["toyota"],
  "zip": "90001",
  "maximumDistance": 50,
  "stockType": "used",
  "sortBy": "list_price",
  "maxItems": 50
}
```

**Filter by price, year and mileage:**

```json
{
  "mode": "search",
  "makes": ["honda", "kia"],
  "zip": "10001",
  "priceMin": 15000,
  "priceMax": 28000,
  "yearMin": 2020,
  "mileageMax": 40000,
  "maxItems": 100
}
```

**Supported URL shapes (url mode):**

| Shape | Example |
|---|---|
| Search results (filtered) | `https://www.cars.com/shopping/results/?stock_type=used&makes[]=toyota&zip=90001&maximum_distance=50` |
| Category, make and condition pages | `https://www.cars.com/shopping/suv/`, `https://www.cars.com/shopping/toyota/`, `https://www.cars.com/shopping/new/`, `https://www.cars.com/shopping/certified-preowned/` (page 2 onward works too) |
| Vehicle detail page | `https://www.cars.com/vehicledetail/54b070d6-e344-46b9-9e1d-9c25007a729e/` |

Any other link (dealer pages, research pages, the old for-sale search address, other sites) is skipped and the run log shows a warning naming the link. If none of your links can be scraped, or the list is empty, the run ends as failed with a message saying so, never as a successful run with no items. A vehicle link stops working once that car is sold.

Each row's `source_context` records which start produced it: `seed_type` is `location` (search mode with a ZIP), `search` (search mode by make or all makes) or `url` (a pasted link), `seed_value` is the ZIP, make or pasted link, and `seed_id` is a short stable hash of `seed_value`.

**Paste cars.com URLs (filters ignored):**

```json
{
  "mode": "url",
  "urls": [
    "https://www.cars.com/shopping/results/?stock_type=used&makes[]=tesla&zip=94103&maximum_distance=100"
  ],
  "fetchDetails": true,
  "maxItems": 80
}
```

**Recurring monitoring of the same search:**

```json
{
  "mode": "search",
  "makes": ["toyota"],
  "zip": "90001",
  "maximumDistance": 50,
  "incrementalMode": true,
  "maxItems": 500
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/cars-com-scraper").call(run_input={
    "mode": "search", "makes": ["toyota"], "zip": "90001", "maxItems": 50
})
for vehicle in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(vehicle["listingId"], vehicle["priceAmount"], vehicle["priceCurrency"], vehicle["mileageValue"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/cars-com-scraper').call({ mode: 'search', makes: ['toyota'], zip: '90001', maxItems: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID and the actor skips every listing already collected there, so you do not pay twice.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same search. The first run returns every listing as `NEW`. Later runs compare against the saved state: changed listings return `UPDATED` (with `changedFields` naming exactly what moved), and unchanged listings are suppressed and not billed unless `emitUnchanged` is on. `EXPIRED` is opt-in via `emitExpired` and is only produced once a run has scanned the tracked search all the way to the end, never when Max items or Max pages capped it, and never on a resumed run. `REAPPEARED` only happens to a listing a previous run already marked `EXPIRED`: if it is found again later, it comes back as `REAPPEARED` instead of `NEW`, keeping its original `firstSeenAt`. **What is compared:** every returned vehicle field (price, mileage, dealer, badges, photos, price history, features and the rest); a listing is `UPDATED` when any of them moves. **Never compared:** scrape timestamps, search-level metadata (total results, search instance id), ad-tracking blobs, days on market (it ticks up daily) and the detail-fetch status flags; they are still returned in the output. Use `ignoreFieldsForChanges` to add your own noisy fields: a name matches that field at any depth (for example `galleryImages` or `badges`), and a dotted path such as `dealer.rating` matches one exact nested field. Changing the ignore list never causes a one-time wave of `UPDATED` rows. Likewise, version 1.1.7 now fills the `source_context` start details, dealer, rating, location, price text, monthly payment, badges, deal and price-drop notes, photos and similar card fields that earlier versions returned empty on some pages; for a search you already track, that empty-to-filled fill is applied silently once (the listing stays `UNCHANGED` and is not billed), while a later change to any of those values, or any other field, still returns `UPDATED`. `stateKey` names or shares one monitoring campaign; leave it empty and the actor derives one from your search and filter settings. With Incremental mode off, output is exactly as before.

### Send results into your apps (MCP connectors)

Optionally pipe each run's 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, and a connector failure never fails the run.

**What gets written to the connector:** a condensed, human-readable summary of each vehicle, not the full JSON. Each item becomes one entry with a title and its key fields (price, mileage, VIN, body style, stock type, dealer, dealer rating, location, URL) flattened to plain text. The complete record always stays in the Apify dataset.

1. Authorize a connector once under Apify, Settings, API & Integrations (Notion, Linear, Airtable or Apify).
2. Select it in the `mcpConnectors` input field. Notion gets a rich page created per vehicle; other connectors get a best-effort write or digest.
3. For Notion, also set `notionParentPageUrl` to the page under which vehicle pages are created.

`maxNotifyListings` caps how many items are sent to each connector per run. Leave `mcpConnectors` empty to skip connector export entirely.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (build from makes, ZIP and filters) or `url` (use pasted cars.com URLs). |
| `makes` | array | `["tesla"]` | Vehicle makes to search; each make is scraped as its own search. An empty array searches all makes. |
| `models` | array | none | Optional model names to narrow the search, applied across the selected makes. |
| `zip` | string | none (prefill: `90001`) | US ZIP code to center the search on. Leave empty for a nationwide search. |
| `maximumDistance` | integer | `50` | Search radius in miles around the ZIP. |
| `stockType` | string | `all` | `all`, `used`, `new`, or `cpo` (certified pre-owned). |
| `priceMin` | integer | none | Minimum price (USD). |
| `priceMax` | integer | none | Maximum price (USD). |
| `yearMin` | integer | none | Minimum model year. |
| `yearMax` | integer | none | Maximum model year. |
| `mileageMax` | integer | none | Maximum odometer reading. |
| `sortBy` | string | `best_match_desc` | One of 11 sort orders (price, mileage, year, distance, best deal, newest). |
| `urls` | array | three example links (a search, a make page, a vehicle) | cars.com search or vehicle detail URLs (url mode). Filter fields above are ignored in this mode. |
| `fetchDetails` | boolean | `false` | Visit each vehicle page for interior color, transmission, fuel type, ownership/title/accident flags and the dealer phone number. Adds a small per-vehicle charge. |
| `maxItems` | integer | `20` | Stop after collecting this many vehicles, across all searches or URLs. `0` = unlimited. |
| `maxPages` | integer | none (an internal safety bound of 1000 applies if left empty) | Optional cap on result pages per search. If you set it, each search stops at this page count even before Max items is reached. Leave it empty to rely on Max items. |
| `resumeFromRunId` | string | none | Continue one specific previous run or dataset: listings already collected there are skipped. For recurring monitoring of the same search, use Incremental mode instead. |
| `incrementalMode` | boolean | `false` | Turn on for scheduled or recurring runs over the same search. See "Resume and recurring updates" above. |
| `stateKey` | string | none | Name a monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Auto-derived from the search and filter settings when left empty. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) listings unchanged since the last run, marked `UNCHANGED`. |
| `emitExpired` | boolean | `false` | Also return (and bill) listings from a previous run no longer found, marked `EXPIRED`, once a run has fully scanned the search. |
| `ignoreFieldsForChanges` | array | none | Field names excluded from change detection (any depth; dotted path for one nested field). They are still returned, they just never mark a listing `UPDATED`. |
| `proxy` | object | Apify Residential, country US | Connection settings. |
| `maxResidentialMB` | integer | `0` | Kept for backward compatibility with saved inputs. No longer enforced: it does not cap residential traffic. Use `maxItems` to bound a run. |
| `mcpConnectors` | array | none | Optional MCP connectors to pipe results into (Notion, Linear, Airtable, Apify). |
| `notionParentPageUrl` | string | none | Notion parent page URL or id for the Notion connector. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. |

**Output views.**

The dataset has six views, selectable on the Output tab or with `?view=<name>` through the API: Overview, Normalized contract (filled on every row), Detail enrichment (needs Fetch full details), Changes (incremental), Deals and price signals, and Vehicle specs, dealer and photos. Change columns are empty on runs without Incremental mode.

### Output Example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "listingId": "00000000-0000-4000-8000-000000000001",
  "vin": "1FAKEVIN000000001",
  "url": "https://www.cars.com/vehicledetail/00000000-0000-4000-8000-000000000001/",
  "title": "2025 Toyota Crown Limited",
  "year": 2025,
  "make": "Toyota",
  "model": "Crown",
  "trim": "Limited",
  "bodyStyle": "Sedan",
  "mileage": 17417,
  "drivetrain": "All-wheel Drive",
  "fuelType": "Hybrid",
  "exterior_color": "Heavy Metal",
  "interior_color": "Black",
  "transmission": "Automatic",
  "stockType": "Used",
  "cpoIndicator": true,
  "price": 39298,
  "msrp": 40985,
  "monthlyPayment": "$599",
  "price_drop": "$2.6K",
  "deal_rating": "great-deal",
  "deal_badge_text": "Great Deal",
  "price_vs_market": "This vehicle is priced $805 below the average market price of similar vehicles in the area at $40,103.",
  "dealerName": "Example Toyota of Springfield",
  "dealerRating": 4.5,
  "dealer_phone": "(000) 000-0000",
  "location": "Irvine, CA (38 mi)",
  "imageUrl": "https://platform.cstatic-images.com/large/in/v2/example.jpg",
  "imageCount": 24,
  "galleryImages": ["https://platform.cstatic-images.com/large/in/v2/example.jpg"],
  "carfax_1_owner": true,
  "carfax_clean_title": true,
  "carfax_no_accidents": true,
  "badges": [
    { "label": "Great Deal", "variant": "great-deal", "description": "Priced below the average market price." }
  ],
  "vehicleCondition": "CERTIFIED",
  "priceAmount": 39298,
  "priceCurrency": "USD",
  "msrpAmount": 40985,
  "monthlyPaymentAmount": 599,
  "priceDropAmount": 2692,
  "marketAverageAmount": 40103,
  "dealDeltaAmount": -805,
  "mileageValue": 17417,
  "mileageUnit": "mi",
  "dealerCity": "Irvine",
  "dealerState": "CA",
  "dealerZip": "92618",
  "dealerDistanceMiles": 38,
  "isShippable": false,
  "shippingNote": null,
  "sellerType": "DEALER",
  "stockNumber": "STK0001",
  "engine": "2.5L I-4 port/direct injection",
  "doors": 4,
  "cylinders": 4,
  "features": ["Apple CarPlay", "Backup Camera", "Heated Seats"],
  "listedAt": "2026-09-03",
  "daysOnMarket": 10,
  "originalListPrice": 40985,
  "priceBelowOriginalAmount": 1687,
  "priceHistory": [
    { "date": "2026-09-03", "event": "LISTED", "changeAmount": null, "price": 40985 },
    { "date": "2026-09-09", "event": "PRICE_INCREASE", "changeAmount": 1005, "price": 41990 },
    { "date": "2026-09-10", "event": "PRICE_DROP", "changeAmount": 2692, "price": 39298 }
  ],
  "vehicleHistory": {
    "oneOwner": true,
    "cleanTitle": true,
    "noAccidents": true,
    "serviceRecords": true,
    "openRecall": false,
    "personalUseOnly": true,
    "reportSource": "autocheck"
  },
  "detailLoaded": true,
  "detailError": null,
  "scrapedAt": "2026-09-13T12:00:00+00:00"
}
```

### Plan Requirement

cars.com challenges automated traffic on every connection type, including residential, so this actor does not work on a plain datacenter connection and no single proxy setting is guaranteed to pass on its own. The default Proxy setting (Apify Residential, country US) is the connection this actor is built around, and the actor handles the site's check itself on that connection. Residential proxy access is not included on Apify free plans. When the selected connection is unavailable to your account, the run log prints this notice and continues on a backup connection:

> The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable. For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs.

Once a run is served over the backup connection, the log says "Served over the backup connection (the default connection was unavailable)." Some pages may still be unavailable there. If no page can be loaded the run ends as failed, never as a silent empty success, with one of these status messages:

- **Connection refused for the account:** "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid."
- **Residential unavailable and backup failed:** "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested cars.com pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again."
- **Requests failed:** "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

For consistently good coverage keep Proxy on Residential with country US on a paid plan, or supply your own proxy URLs. `maxResidentialMB` is kept only so saved inputs still load; it does not cap traffic, so use Max items to bound a large run.

### FAQ

#### How much does it cost?

This actor uses Apify's pay-per-event pricing. On the Free plan the rates are $0.08 per run start, $0.002 per vehicle returned, and $0.0008 per vehicle only when Fetch full details is on; paid plans get lower tiered rates. The Pricing tab always shows the current rates for your plan. Use Max items to cap the cost of any run.

#### Is it legal to scrape cars.com?

This actor collects only publicly available vehicle listing data. You are responsible for how you use it: follow cars.com's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing facts such as price, mileage and VIN are generally treated differently from photos and dealer branding, which may carry their own rights.

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and changed listings by default, and unchanged ones are suppressed and not billed unless you turn on Emit unchanged.

#### What is the difference between Resume and Incremental mode?

Resume (`resumeFromRunId`) continues one specific interrupted run by its run or dataset ID, a one-time action. Incremental mode remembers a recurring search on its own and classifies every listing as `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED` or `EXPIRED` against its own saved state on every scheduled run. Use Resume once, right after a run got cut off; use Incremental mode for ongoing monitoring of the same search.

#### Why did my run return no items?

If the connection is refused before any page can be read, the run ends as failed with a status message explaining the connection problem, so "no listings match your filters" is never confused with "the connection could not reach cars.com". Only the run-start fee is charged; no items are pushed. Check that Proxy is set to Residential with country US, then run it again in a few minutes.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the `mcpConnectors` field to push results into Notion, Linear or Airtable.

### 🔗 Want more car data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🚗 <a href="https://apify.com/abotapi/autotrader-com-scraper"><b>Autotrader US</b></a><br>Scrape US car listings from Autotrader by make, model, ZIP, price, year, body style...</td><td>🚗 <a href="https://apify.com/abotapi/mobile-de-scraper"><b>mobile.de Scraper</b></a><br>Scrape mobile.de vehicle listings with 30+ fields including price, mileage, registration...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/autotrader"><b>AutoTrader UK Scraper</b></a><br>Pull structured vehicle listings from autotrader.co.uk at scale. Search by filters or use...</td><td>🚗 <a href="https://apify.com/abotapi/autoscout24-scraper"><b>AutoScout24 Scraper</b></a><br>From $0.8/1K. A lean, production-grade scraper for autoscout24.com. Point it at a search...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/willhaben-at-scraper"><b>Willhaben.at Scraper</b></a><br>From $1/1K. Scrape Willhaben.at listings into clean JSON with 40+ structured fields...</td><td>🚗 <a href="https://apify.com/abotapi/carsales-au-scraper"><b>Carsales.com.au Scraper</b></a><br>Scrape structured vehicle listings from Carsales.com.au from $1 per 1K results. Built to...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/cars-com-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

## `mode` (type: `string`):

How to start the scrape. 'search' builds cars.com searches from your makes, ZIP, radius and filters. 'url' walks the cars.com search or vehicle URLs you paste (filter fields below are ignored in URL mode).

## `makes` (type: `array`):

One or more vehicle makes to search for (e.g. 'tesla', 'toyota', 'ford'). Each make is scraped as its own search. Leave empty to search all makes.

## `models` (type: `array`):

Optional model names to narrow the search (e.g. 'model 3'). Applied across the selected makes.

## `zip` (type: `string`):

US ZIP code to center the search on (e.g. '90001'). Combine with Search radius below. Leave empty for a nationwide search.

## `maximumDistance` (type: `integer`):

Radius in miles around the ZIP to include vehicles from.

## `stockType` (type: `string`):

Limit to new, used, or certified pre-owned vehicles. 'all' returns every stock type.

## `priceMin` (type: `integer`):

Only return vehicles priced at or above this amount.

## `priceMax` (type: `integer`):

Only return vehicles priced at or below this amount.

## `yearMin` (type: `integer`):

Only return vehicles from this model year or newer.

## `yearMax` (type: `integer`):

Only return vehicles from this model year or older.

## `mileageMax` (type: `integer`):

Only return vehicles at or below this odometer reading.

## `sortBy` (type: `string`):

Order cars.com returns results in.

## `urls` (type: `array`):

Paste cars.com search URLs (https://www.cars.com/shopping/results/?...) category or make pages (https://www.cars.com/shopping/suv/, /shopping/toyota/, /shopping/new/) or vehicle detail URLs (https://www.cars.com/vehicledetail/<id>/). Multiple URLs supported; filter-mode fields are ignored. Other cars.com pages (dealer pages, research, the old for-sale search address) are not supported and are skipped with a warning; if none of your links can be scraped the run ends as failed. Leaving this field untouched scrapes the three example links (a search, a make page and a vehicle).

## `fetchDetails` (type: `boolean`):

Visit each vehicle page to add interior color, transmission, fuel type, CARFAX flags (one-owner, clean title, no accidents) and the dealer phone number. Slower and adds a small per-item charge; leave off for fast basic listings.

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

The single cap on this run: stop after collecting this many vehicles (across all searches/URLs). Set 0 for unlimited.

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

Optional safety bound on how many result pages to include per search. Defaults to unlimited (leave empty or set 0) and does NOT cap the run; it defers to Max items, which is the single cap. The run stops at Max items, not here.

## `proxy` (type: `object`):

Use Apify Residential proxy with country US for reliable results.

## `maxResidentialMB` (type: `integer`):

Kept for backward compatibility with saved inputs; it is no longer enforced and does not limit residential traffic. Use Max items to bound the size (and cost) of a run. 0 = unlimited.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large crawl of listings without returning or charging for listings already collected there. Use this after an interrupted run, or when continuing a large search in another run. For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching listings as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. Turn on "Emit unchanged" or "Emit expired" only when you also want those listings returned (and billed). State is kept separately per search/URL and Fetch full details setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above. Changes are detected on every returned vehicle field - price, mileage, dealer, badges, photos, price history, features and the rest - and a vehicle is marked UPDATED (with the changed field names in changedFields) when any of them moves. Never compared: scrape timestamps, search-level metadata (total results, search instance id), ad-tracking blobs, days on market (it ticks daily) and the detail-fetch status flags. Add your own noisy fields under "Ignore fields when detecting changes" below.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/URL and filter settings — different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max items capped it or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names to exclude from UPDATED change detection, in addition to the built-in handling (scrape timestamps, search metadata, ad tracking, days on market and detail-fetch flags never trigger UPDATED). A name matches that field at any depth, e.g. galleryImages or badges; use a dotted path such as dealer.rating for one exact nested field. Use this if a field in your setup turns out to be noisy, e.g. one that rotates without the vehicle itself changing. Ignored fields are still returned in the output; they just never mark a vehicle UPDATED.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page-per-item export; other connectors get a best-effort write/digest. Leave empty to skip; never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "makes": [
    "tesla"
  ],
  "zip": "90001",
  "maximumDistance": 50,
  "stockType": "all",
  "sortBy": "best_match_desc",
  "urls": [
    "https://www.cars.com/shopping/results/?stock_type=used&makes[]=toyota&zip=90001&maximum_distance=50",
    "https://www.cars.com/shopping/toyota/",
    "https://www.cars.com/vehicledetail/54b070d6-e344-46b9-9e1d-9c25007a729e/"
  ],
  "fetchDetails": false,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "maxResidentialMB": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `contract` (type: `string`):

No description

## `enrichment` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `deals` (type: `string`):

No description

## `vehicle_details` (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 = {
    "mode": "search",
    "makes": [
        "tesla"
    ],
    "zip": "90001",
    "maximumDistance": 50,
    "urls": [
        "https://www.cars.com/shopping/results/?stock_type=used&makes[]=toyota&zip=90001&maximum_distance=50",
        "https://www.cars.com/shopping/toyota/",
        "https://www.cars.com/vehicledetail/54b070d6-e344-46b9-9e1d-9c25007a729e/"
    ],
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/cars-com-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 = {
    "mode": "search",
    "makes": ["tesla"],
    "zip": "90001",
    "maximumDistance": 50,
    "urls": [
        "https://www.cars.com/shopping/results/?stock_type=used&makes[]=toyota&zip=90001&maximum_distance=50",
        "https://www.cars.com/shopping/toyota/",
        "https://www.cars.com/vehicledetail/54b070d6-e344-46b9-9e1d-9c25007a729e/",
    ],
    "maxItems": 20,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/cars-com-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 '{
  "mode": "search",
  "makes": [
    "tesla"
  ],
  "zip": "90001",
  "maximumDistance": 50,
  "urls": [
    "https://www.cars.com/shopping/results/?stock_type=used&makes[]=toyota&zip=90001&maximum_distance=50",
    "https://www.cars.com/shopping/toyota/",
    "https://www.cars.com/vehicledetail/54b070d6-e344-46b9-9e1d-9c25007a729e/"
  ],
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/cars-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/cars-com-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/4uCHf5GS5vhOnQQjA/builds/Q2ylpIOSLPsmMk3l6/openapi.json
