# Webmotors Scraper - Brazil Car Listings, Prices & FIPE (`dami_studio/webmotors-scraper`) Actor

Every car on a Webmotors search as one row: make, model, version, year built and model year, price and where it sits against the FIPE table, mileage, gearbox, fuel, colour, body, features, dealer or private seller, town, photos and a link. Filter by state, price, year, mileage, gearbox and more.

- **URL**: https://apify.com/dami\_studio/webmotors-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.50 / 1,000 listing returneds

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 Scraper: Brazilian car listings, prices and FIPE position

Pulls car listings off [Webmotors](https://www.webmotors.com.br) searches and gives you one row per
car: make, model, version, both years, asking price, where that price sits against the FIPE table,
mileage, gearbox, colour, body, the seller's own feature list, whether it's a dealer or a private
seller, the town, the photos and a link back to the listing.

You can drive it from the make/model form, or paste search links you've already set up on the site.

### What you get

A run with `make: "Fiat"`, `model: "Argo"` and `maxItems: 50` returns 50 rows like this one (a real
row, trimmed to fit):

```json
{
  "listingId": "79799906",
  "title": "FIAT ARGO 1.0 FIREFLY FLEX DRIVE MANUAL",
  "make": "FIAT",
  "model": "ARGO",
  "version": "1.0 FIREFLY FLEX DRIVE MANUAL",
  "yearFabrication": 2024,
  "yearModel": 2025,
  "price": 73890,
  "currency": "BRL",
  "fipePercent": 105,
  "mileageKm": 87900,
  "transmission": "Manual",
  "fuel": "Gasolina e álcool",
  "fuelSource": "version",
  "color": "Branco",
  "bodyType": "Hatchback",
  "doors": 4,
  "engineSize": "1.0",
  "horsePower": "77 cv",
  "traction": "Dianteira",
  "trunkCapacity": "300 l",
  "fuelTankCapacity": "48 l",
  "wheelSize": "14",
  "armored": false,
  "hadAuctionHistory": false,
  "condition": "used",
  "features": ["Aceita troca", "IPVA pago", "Licenciado"],
  "description": "Veículo revisado, documentação em dia.",
  "sellerType": "dealer",
  "sellerCategory": "Loja",
  "dealerName": "Localiza Seminovos Belo Horizonte",
  "dealerId": "3880059",
  "city": "Belo Horizonte",
  "state": "Minas Gerais (MG)",
  "url": "https://www.webmotors.com.br/comprar/fiat/argo/10-firefly-flex-drive-manual/4-portas/2024-2025/79799906",
  "images": ["https://image.webmotors.com.br/_fotos/anunciousados/gigante/2026/..."],
  "imageCount": 18,
  "page": 1,
  "position": 1,
  "scrapedAt": "2026-09-19T18:22:04.113Z"
}
```

#### `fipePercent` is the field most people come here for

Brazil prices used cars against the FIPE table, and Webmotors publishes each listing's position on
it. `fipePercent: 105` means the seller is asking 5% above the FIPE price for that exact version and
year. `92` means 8% below. Sort a few thousand rows by it and the under-market cars fall out of the
list on their own.

It isn't on every listing. Measured over 384 rows across 16 different searches, 362 carried it, so
expect roughly 1 in 17 to come back `null`.

#### Two years, not one

Brazilian listings carry the year the car was built and the model year, and they often differ
(`2024` built, `2025` model). Both are in the row. The year filter works on the model year, which is
the one the site's own filter uses.

### Input

```json
{
  "make": "Fiat",
  "model": "Argo",
  "state": "São Paulo (SP)",
  "priceTo": 90000,
  "yearFrom": 2022,
  "mileageTo": 60000,
  "transmission": "Automática",
  "sellerType": "Pessoa Física",
  "sort": "price-asc",
  "maxItems": 500
}
```

Or skip the form and paste links:

```json
{
  "searchUrls": [
    "https://www.webmotors.com.br/carros/estoque/jeep/compass?tipoveiculo=carros&marca1=jeep&modelo1=compass&precoate=150000"
  ],
  "maxItems": 200
}
```

Filters are in the site's own Portuguese, because they're the site's own lists: `Automática`,
`Gasolina e álcool`, `Utilitário esportivo`, `Pessoa Física`. The Console shows them as dropdowns, so
you pick rather than type.

Run it with no input at all and it returns a single sample row showing the shape, charges nothing,
and tells you what to fill in.

### What this does not do

Worth reading before you buy, because these are real limits, not small print.

**A single search reaches 10,000 listings.** That's the site's ceiling, not ours. It holds whether
the run reads 24 rows a page or 1,000. There are 353,000 cars on the site, so a wide search like
"every Chevrolet" will hand you the first 10,000 and stop. Split it by price band, year or state to
get past that. The run says so plainly when it happens, in the status and in `RUN_REPORT`.

**There is no new-versus-used filter.** The site's own new-car and used-car pages run the identical
search underneath, so there's nothing to filter on. Every row carries `condition` (`"used"` or
`"new"`), so you filter after the fact instead. Most searches come back mixed.

**There is no city filter**, for the same reason: the parameter exists on the site but doesn't
change the results. State works and is offered. Every row carries the town, so narrowing by town
after the run works fine.

**Fuel is not a field on the listing, it's a filter.** If you set the fuel filter, every row comes
back tagged with it and `fuelSource` says `"filter"`. If you don't, the fuel is read out of the
version text (`1.0 FIREFLY FLEX` is a flex car) and `fuelSource` says `"version"`. That text names
the fuel on about 83 of every 100 listings; the rest come back `null` rather than guessed at. If you
need fuel on every row, set the filter.

**A make or model the site doesn't know returns nothing, and charges nothing.** This matters more
than it sounds: ask the site for a make that doesn't exist and it answers cheerfully with every car
it has: 353,000 of them. This actor checks that the answer names the make and model you asked for
before it keeps a single row, so a typo costs you nothing instead of costing you a full run of
irrelevant cars. Same for a model: an unknown model quietly falls back to "every car of that make",
and that's caught too.

**No contact details, on purpose.** See below.

**No listing pages are opened.** Everything comes from the search results themselves, which is what
keeps it quick and cheap. Anything that only exists on the individual car's page (the full options
list, the seller's phone, financing quotes) isn't here.

**Some spec fields are blank on some cars**, because the seller left them blank. Across those same
384 rows: engine size on 308, power on 362, traction on 358, wheel size on 371, the feature list on
375\. Make, model, version, price, mileage, gearbox, colour, body, town and state were on all 384.

### Personal data

The search answer carries more about the seller than a row needs. Three things are dropped before a
row is built and never appear in the output:

- the **street address and house number**, which the site returns for dealer listings
- the **CEP**, which on a private seller's listing is a home postcode
- the **neighbourhood**

Rows carry the town and the state, and that's it. A dealer's trading name and dealer id come through
because they're a business; a private seller gets neither.

Sellers also type phone numbers and email addresses into the free-text blurb. Measured across 100
private listings, 2 had a phone number sitting in that text. Every free-text field is stripped of
phone numbers and email addresses on its way into a row (Brazilian mobiles and landlines with or
without the DDD, with or without `+55`, with brackets, dots, spaces or none) and replaced with
`[telefone removido]` or `[email removido]`. Prices, mileages, years and engine sizes are left alone;
the test suite has the controls for both directions.

It isn't perfect. A number typed in an unusual way, with no country code and no word like "contato"
in front of it, can still get through. If you spot one, it's a bug worth reporting.

### Pricing

You're charged once per listing delivered, and the current rate is in the pricing box on this page.

What is **never** charged:

- the sample row you get from an empty run
- a make or model the site doesn't know
- a search that finds nothing
- rows that turn out to be from outside your search
- repeats: the same car found twice in one run is delivered once and charged once
- a run that couldn't reach the site at all

The run stops on its own when the maximum charge you set no longer covers another listing, and says
so, rather than running up a bill and failing at the end.

### Speed

Measured on real runs: 12 rows in 1.5 seconds, 500 in 8 seconds, 1,000 in 4 seconds. Listings come
back in large batches rather than one page at a time, so a big run is only a handful of requests and
most of the clock is the platform starting the container. Rows are written as they arrive, so a long
run fills the dataset while it's still going.

### FAQ

**Does it need a Webmotors account or an API key?**
No. There's nothing to sign in to and nothing to configure beyond the search itself.

**Can I get the seller's phone number?**
No, by design. See the Personal data section above.

**How do I find cars priced below the FIPE table?**
Run a search, then filter the rows on `fipePercent` below 100. Sorting by it is usually more useful
than sorting by price, because it's already adjusted for what the car is.

**Why is my search capped at 10,000?**
That's the site's own limit on how deep any one search goes. Split the search (by state, by price
band, by model year) and run the parts.

**Why are the filter values in Portuguese?**
Because they're the site's own filter labels, and matching them exactly is what makes them work. The
Console shows them as dropdowns so there's nothing to spell.

**Can it do motorbikes?**
Not at the moment. Webmotors lists them, but this actor only searches cars.

**What happens if the site is having a bad day?**
Requests are retried a few times with a growing pause. If a search still can't be loaded, the run
says which one, charges nothing for it, and carries on with the others. A page that fails in the
middle of a long run is reported in `RUN_REPORT` so you know there's a gap.

**Does it return the same car twice?**
Not within a run. Listing ids are tracked across every search in the run, so a car matching two of
your searches is delivered once.

### Run report

Every run writes a `RUN_REPORT` record to the key-value store: each search, what the site answered,
how many listings it has in total, pages read, pages that failed, repeats skipped, rows dropped for
being outside the search, and why the run stopped. When something looks short, that's the first place
to look.

# Actor input Schema

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

The make as Webmotors spells it: Fiat, Volkswagen, Chevrolet, Mercedes-Benz, Caoa Chery. A make the site doesn't know returns nothing and charges nothing. Leave it empty if you only use search links below.

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

Optional, and it needs a make above. The model as the site names it: Argo, Onix, Corolla, Compass. A model the site doesn't know returns nothing and charges nothing.

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

The most listings returned in one run, across every search. Each one returned is charged once. Repeats are skipped and cost nothing. Webmotors lets a single search reach 10,000 listings; past that, narrow it by price, year or state.

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

The order listings are read in. With a limit below the number of listings, this decides which ones you get.

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

Or paste up to 10 results pages from webmotors.com.br, with the filters you picked there. One car's page is not a search and is skipped. The fields on this form don't apply to a pasted link.

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

Only cars sold in this state. Leave it empty for the whole country. There is no city filter on the search this actor uses, but every row carries the town, so you can narrow it afterwards.

## `priceFrom` (type: `integer`):

Lowest asking price, in reais.

## `priceTo` (type: `integer`):

Highest asking price, in reais.

## `yearFrom` (type: `integer`):

Earliest model year. Brazilian listings carry two years, the year built and the model year; this is the model year.

## `yearTo` (type: `integer`):

Latest model year.

## `mileageFrom` (type: `integer`):

Lowest mileage, in kilometres.

## `mileageTo` (type: `integer`):

Highest mileage, in kilometres. Set it to 0 to get only brand-new cars.

## `fuel` (type: `string`):

Flex cars are "Gasolina e álcool". Set this and every row comes back tagged with it; leave it empty and the fuel is read out of the version text instead, which names it on about 8 of every 10 listings.

## `transmission` (type: `string`):

The site keeps CVT, DCT and the other automatics apart, so pick the one you mean.

## `bodyType` (type: `string`):

"Utilitário esportivo" is the site's name for an SUV; "Perua/SW" is an estate.

## `color` (type: `string`):

The colour the seller picked. "Indefinida" is the site's own catch-all.

## `armored` (type: `string`):

Armoured cars are a real slice of the Brazilian market, so the site filters on it.

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

"Loja" is an independent dealer, "Concessionária" a franchised one, "Pessoa Física" a private seller.

## `features` (type: `array`):

Every item you pick has to be on the car. The list is the site's own, in its own words.

## `attributes` (type: `array`):

The badges sellers put on a listing. "Aceita troca" means they'll take a trade-in, "IPVA pago" that this year's road tax is paid, "Alienado" that the car still has finance on it.

## Actor input object example

```json
{
  "make": "Fiat",
  "model": "Argo",
  "maxItems": 50,
  "sort": "relevance"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per listing: make, model, version, year built and model year, price, the FIPE comparison, mileage, gearbox, fuel, colour, body, features, seller type and town, photos and a link. A search that finds nothing adds no rows.

## `report` (type: `string`):

Each search with what Webmotors answered, how many listings it has, pages read, repeats and off-search rows left out, and why the run stopped.

# 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": "Fiat",
    "model": "Argo",
    "maxItems": 50,
    "sort": "relevance"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/webmotors-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": "Fiat",
    "model": "Argo",
    "maxItems": 50,
    "sort": "relevance",
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/webmotors-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": "Fiat",
  "model": "Argo",
  "maxItems": 50,
  "sort": "relevance"
}' |
apify call dami_studio/webmotors-scraper --silent --output-dataset

```

## MCP server setup

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