# Webmotors Car Listings Scraper (Brazil) (`superslowsloth/webmotors-listings`) Actor

Scrape Webmotors, Brazil's largest car marketplace: used and new cars by make, model, state or city with price in BRL, FIPE percent, year, kilometres, transmission, dealer or individual seller and location. Sort by price, year or mileage for inventory and price feeds.

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

## Pricing

from $1.75 / 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

## Webmotors Car Listings Scraper (Brazil)

Scrape the cars listed on [Webmotors](https://www.webmotors.com.br/carros/estoque),
Brazil's largest car marketplace, new and used, by make, model, state or city, and get
one flat row per car: the asking price in **BRL**, the price as a percentage of the
**FIPE** reference table, model year, kilometres, transmission, body type, colour,
whether the seller is a dealer or a private person, the dealership's name, the city and
state, the link and the photo.

Built for a **Brazilian used-car inventory and price feed**: run it on a schedule for
the makes, models or states you track, keep the `listing_id`s you have seen, and diff the
dataset against last time. A `listing_id` that is new is a new listing; the same
`listing_id` with a lower `price_brl` is a price drop.

No account, no API key, no login, no browser.

### Input

| Field | Notes |
|---|---|
| `searchUrls` | Optional. Webmotors search pages, one per line, copied from the browser: `https://www.webmotors.com.br/carros/estoque/toyota/corolla`, `/carros/sp-campinas`, `/carros/sp-sao-paulo/toyota`. A query string's filters (price, year, kilometres) are kept. Use this **or** make/model/state/city. |
| `make`, `model` | Optional. As written in Webmotors' URL: `toyota`, `land-rover`, `mercedes-benz`; `corolla`, `corolla-cross`, `hilux-sw4`. A model needs its make. |
| `state`, `city` | Optional. A two-letter state code (`SP`) and a city inside it (`campinas`, `sao-paulo`, `rio-de-janeiro`; accents optional). A city needs its state. With none of the four filled, the whole of Webmotors is read. |
| `minPrice`, `maxPrice` | Optional, whole reais. |
| `minYear`, `maxYear` | Optional, model year. |
| `minKm`, `maxKm` | Optional, kilometres. `maxKm` of 0 returns new cars only. |
| `sellerType` | `any` (default), `dealership` (Concessionária), `store` (Loja) or `individual` (Pessoa Física). |
| `maxItems` | Cars to read per search (default 100, up to 10,000 - the most Webmotors serves for one search). |
| `sort` | `best-match` (Webmotors' relevance order, default), `price-asc`, `price-desc`, `year-newest`, `km-low`. |
| `proxyConfiguration` | Apify datacenter proxy by default. Residential was less reliable: see "How it fetches". |

An unknown make, model, state or city **fails the run at no charge** instead of returning
something else. Webmotors itself answers those with a 200 for a broader search (an
unknown make is the whole market, an unknown city the whole state); the actor compares
the page Webmotors says it served with the one you asked for and refuses to bill rows
that are not the search you asked for.

### Output

Prices are Brazilian reais as numbers. Example row (2026-10-20):

```json
{
  "search": "/carros/estoque",
  "position": 1,
  "listing_id": 78029164,
  "url": "https://www.webmotors.com.br/comprar/ford/f-150/5-0-v8-gasolina-cd-lariat-black-fx4-automatico/4-portas/2025-2025/78029164",
  "title": "FORD F-150 5.0 V8 GASOLINA CD LARIAT BLACK FX4 AUTOMÁTICO",
  "condition": "used",
  "make": "FORD",
  "model": "F-150",
  "version": "5.0 V8 GASOLINA CD LARIAT BLACK FX4 AUTOMÁTICO",
  "year_manufacture": 2025,
  "year_model": 2025,
  "km": 3000,
  "price_brl": 519900.0,
  "fipe_percent": 106,
  "good_deal": false,
  "promotion": null,
  "transmission": "Automática",
  "body_type": "Picape",
  "color": "Preto",
  "doors": 4,
  "engine_litres": 5.0,
  "horsepower": 405,
  "traction": "Integral permanente",
  "armored": false,
  "attributes": ["Aceita troca", "Único dono", "IPVA pago", "Licenciado", "Garantia de fábrica"],
  "seller_type": "dealer",
  "seller_category": "Concessionária",
  "seller_id": 55693,
  "seller_name": "Sonnervig Vila Guilherme",
  "city": "São Paulo",
  "state": "SP",
  "state_name": "São Paulo",
  "neighborhood": "Vila Guilherme",
  "photo_count": 20,
  "image_url": "https://image.webmotors.com.br/_fotos/anunciousados/gigante/2026/202608/20260818/ford-f150-5.0-v8-gasolina-cd-lariat-black-fx4-automatico-wmimagem17475804762.jpg"
}
```

| Field | Notes |
|---|---|
| `search`, `position` | The Webmotors path the row came from (`/carros/estoque/toyota/corolla`) and the car's place in that search's result list (1 is first). |
| `listing_id`, `url` | Webmotors' id, stable for the life of the ad, and the ad's page in the site's own link form. |
| `title`, `make`, `model`, `version` | As Webmotors prints them, in capitals. `model` is the family (`COROLLA CROSS`), `version` the trim line. |
| `condition` | `used` or `new` (a 0 km car sold by a dealer). |
| `year_manufacture`, `year_model` | A 2025 car can carry model year 2026. |
| `km` | Odometer in kilometres. 0 on a new car. |
| `price_brl` | Asking price. Null when Webmotors sends none or 0. Webmotors holds some mistyped prices (a 2025 car listed at R$1.7 billion was in the data on 2026-10-20), so cap the read with `maxPrice` or filter on your side. |
| `fipe_percent` | The asking price as a percentage of the FIPE reference price (100 = at FIPE, below 100 = cheaper). Webmotors does not document the field; the meaning is read from its values (old, cheap cars read well under 100). Null when not sent. |
| `good_deal`, `promotion` | Webmotors' "good deal" badge, and the name of the seller promotion attached to the ad. |
| `transmission`, `body_type`, `color`, `doors`, `engine_litres`, `horsepower`, `traction`, `armored` | Specification as Webmotors prints it. Null when the ad did not carry it (power and engine size are often missing on older cars). |
| `attributes` | The ad's badges (`Aceita troca`, `IPVA pago`, `Único dono`). Empty when it lists none. |
| `seller_type`, `seller_category` | `dealer` or `individual`, and Webmotors' ad category: `Concessionária`, `Loja` or `Pessoa Física`. |
| `seller_id`, `seller_name`, `neighborhood` | The dealer's Webmotors id, trade name and neighbourhood. **Always null for an individual**: a private seller's id and name identify a person and are not published. |
| `city`, `state`, `state_name` | Where the car is. `state` is the two-letter code. |
| `photo_count`, `image_url` | Photos on the ad and the first one. Null `image_url` when the ad has no photo. |

#### Honest nulls

A field Webmotors did not send, or sent as 0 where 0 means "unknown" (price, FIPE,
power), is `null`, never `0`. A car with no price is still returned, because the listing
exists.

### 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.0021** per car row written (`listing-scraped`), i.e. $2.10 per 1,000 cars. A row
  that is not written is not charged; a car Webmotors repeats is written and charged once.

### Coverage - read this before you diff runs

- **One search returns at most 10,000 cars**, whatever its size. Webmotors holds far more
  than that in total, so a nation-wide read stops at 10,000 and the run log says so. To
  read more, split by make, model, state, city, seller type or price band, one search
  URL each.
- **Relevance order and kilometres order repeat a few cars across page borders** because
  many cars tie on the sort key (measured 2026-10-20: 71 distinct of 72 on relevance,
  62 of 72 on kilometres over three pages). The repeats are returned and charged once.
  The price sorts had no repeats in the same test, so use `price-asc` or `price-desc`
  when you want a clean sweep of a narrow search.
- **Sorting a very broad search is of limited use.** Webmotors serves only the first
  10,000 results of a sort, and across the whole country those are all model year 2027
  for `year-newest` and all 0 km new cars for `km-low` (sampled at pages 1, 150, 300 and
  417 on 2026-10-20). Narrow the search by make, model or state first.
- **There is no "recently listed" sort.** Find new listings by diffing `listing_id`s
  between runs.
- **Not offered.** Motorcycles and trucks (other pages of Webmotors), version-level
  pages, and the other filters on the site (colour, body type, fuel, options).

### How it fetches

The actor calls the JSON endpoint Webmotors' own search page uses, with a Chrome TLS
fingerprint (`curl_cffi`), 24 cars per request. Measured 2026-10-20: HTTP 200 from Apify
datacenter exits (12 of 12), no key or cookie needed. Residential exits were less
reliable (3 of 4 answered; the refusals were a CloudFront 403 and an Akamai "Access
Denied"), which is why the default is the datacenter proxy. Webmotors sits
behind PerimeterX: a refused request is a 403 with a small JSON body, and a 200 that is
not the search JSON (a challenge page) is treated the same way. The actor treats both,
429 and 5xx as the address being blocked, rotates to a fresh one and retries.

Things worth knowing, all measured:

- **The walk ends on the data.** Webmotors reports no page count. A search with fewer
  than 10,000 results ends on a short page and then an empty one. A search with more
  ends at page 417 (results 9,985 to 10,000), after which every page, however high,
  answers the same 10 cars: the first ten of page 1. The actor stops at the first page
  that brings no car it has not already seen.
- **Unknown slugs do not 404.** See the note under Input.
- **Filters are checked.** The price, year and kilometre bounds are echoed back by
  Webmotors; if the echo does not match the request the run fails at no charge. A
  filter name Webmotors does not know is ignored by Webmotors itself without a word, so
  only the filters listed under Input are offered as inputs.

# Actor input Schema

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

Webmotors car search pages to read, one per line, copied from the browser: https://www.webmotors.com.br/carros/estoque, /carros/estoque/toyota/corolla, /carros/sp-campinas or /carros/sp-sao-paulo/toyota. Filters in the URL's query string (price, year, kilometres) are kept. Each URL is read separately. When this is filled, leave Make, Model, State and City empty. Only car pages down to make and model are supported.

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

One make as written in Webmotors' URL: toyota, honda, land-rover, mercedes-benz. Leave empty for every make. A make Webmotors does not know fails the run at no charge rather than returning the whole market.

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

One model of the make, as written in Webmotors' URL: corolla, corolla-cross, hilux-sw4, hr-v. Needs Make. A model Webmotors does not know fails the run at no charge.

## `state` (type: `string`):

Two-letter Brazilian state code: SP, RJ, MG, PR, RS and so on. Leave empty for the whole country.

## `city` (type: `string`):

A city inside the State, written as in Webmotors' URL: campinas, sao-paulo, rio-de-janeiro (accents optional). Needs State. A city Webmotors has no page for fails the run at no charge rather than returning the whole state.

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

Only cars asking at least this many reais.

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

Only cars asking at most this many reais. Webmotors holds some mistyped prices in the hundreds of millions, so a ceiling also keeps those out.

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

Only cars of this model year or newer.

## `maxYear` (type: `integer`):

Only cars of this model year or older.

## `minKm` (type: `integer`):

Only cars with at least this many kilometres on the odometer.

## `maxKm` (type: `integer`):

Only cars with at most this many kilometres on the odometer. Use 0 for new cars only.

## `sellerType` (type: `string`):

Limit the search to one kind of ad: a dealership (Concessionária), a used-car store (Loja) or a private seller (Pessoa Física). 'Any seller' returns all three. Every row also carries seller_type and seller_category, so the full read can be filtered later.

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

Read at most this many cars for each search. Up to 10,000 are accepted, the most Webmotors serves for one search. A bigger inventory has to be split by make, model, state, city or price band (use several search URLs).

## `sort` (type: `string`):

The order Webmotors lists the cars in. 'Best match' is its default relevance order. For a price feed or a full sweep of a narrow search use a price sort. Webmotors repeats a few cars across page borders when many tie on the sort key (relevance and kilometres); repeats are returned and charged once.

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

Use Apify's automatic datacenter proxy (the default). Webmotors answered every datacenter exit in testing and was less reliable on residential ones. A refused request is retried from a fresh address.

## Actor input object example

```json
{
  "sellerType": "any",
  "maxItems": 100,
  "sort": "best-match",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/webmotors-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 = { "proxyConfiguration": { "useApifyProxy": True } }

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/webmotors-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 '{
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/webmotors-listings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/webmotors-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/IjbqwyxOLTGFJCo8M/builds/ipy8zSTpSl87Jqv39/openapi.json
