What the Webmotors scraper collects
Scrapes webmotors.com.br search results and individual adverts for cars and motorcycles into JSON and CSV. Search rows carry price, make, model and version, years, mileage, transmission, body, colour, engine, horsepower, the seller's ticked facts, price against the FIPE table and the "Bom negócio" badge; ad mode adds fuel, optionals, the FIPE code and value, and the site's own lowest / average / highest market price for that version and year.
Search by make and model
Input: --make and --model (and optionally --category motos)One row per listing for every matching car or motorcycle in Brazil: price, specs, mileage, FIPE percentage, good-deal badge, seller type, city and state.
Search by the site's own URL
Input: A webmotors.com.br search address with any filters (state, city, years, price)The site's own API reads the filters out of the address, and the run checks after page 1 that the site applied what you asked for.
Motorcycles
Input: --category motos with a makeMotorcycle listings in the same row shape, here ordered newest model year first.
Advert pages
Input: A search run's JSON via --ads-fileEverything in a search row plus fuel, plate's final digit, optionals, a dealer's description, creation date, FIPE code and value, and the site's market-price range.
From zero to Webmotors data in 3 steps
Install
Clone the repository and install one browser engine. Python 3.9+.
$ git clone https://github.com/2scraper/webmotors-scraper $ cd webmotors-scraper $ python3 -m venv .venv $ .venv/bin/pip install -r requirements.txt -r requirements-playwright.txt $ .venv/bin/playwright install chromium $ cp .env.example .env
Run a scraper
Pick what you need and run it. Every flag is documented in the README.
$ .venv/bin/python playwright_scraper.py --make volkswagen --model gol --pages 3$ .venv/bin/python playwright_scraper.py --url "https://www.webmotors.com.br/carros/sp/toyota/corolla?anode=2020" --sort price-asc --pages 5$ .venv/bin/python playwright_scraper.py --category motos --make honda --sort year-desc$ .venv/bin/python playwright_scraper.py --mode ad --ads-file webmotors_rows.json --out advertsGet structured data
Rows land in JSON or CSV (
--format), with a.meta.jsonsidecar recording status, pages fetched and why the run stopped.
Webmotors data fields
41 columns per row in a predictable schema. The most useful ones:
| Field | What it holds |
|---|---|
sku | Webmotors advert id |
title | Make, model and version as listed |
price | Asking price as a number |
make | Make, e.g. VOLKSWAGEN |
model | Model, e.g. GOL |
version | Version / trim |
year_fabrication | Year built |
year_model | Model year |
odometer_km | Mileage in km |
horsepower_cv | Horsepower (cv) |
attributes | The seller's ticked facts, e.g. IPVA pago, Único dono |
fipe_pct | Price against the FIPE table, in percent |
good_deal | The site's "Bom negócio" badge |
seller_type | private or dealer |
city | City of the listing |
state | State, e.g. SP |
[
{
"source": "webmotors.com.br",
"scraped_at": "2026-09-28T09:36:37.696284+00:00",
"url": "https://www.webmotors.com.br/comprar/volkswagen/gol/16-mi-city-8v-flex-4p-manual/4-port…",
"sku": "74012141",
"title": "VOLKSWAGEN GOL 1.6 MI CITY 8V FLEX 4P MANUAL",
"price": 35900.0,
"currency": "BRL",
"vehicle": "car",
"condition": "used",
"make": "VOLKSWAGEN",
"model": "GOL",
"version": "1.6 MI CITY 8V FLEX 4P MANUAL",
"year_fabrication": 2013,
"year_model": 2014,
"odometer_km": 173000,
"transmission": "Manual",
"body_type": "Hatchback",
"color": "Preto",
"doors": 4,
"engine_litres": null,
"engine_cc": null,
"horsepower_cv": 104,
"traction": null,
"armored": false,
"attributes": [
"Aceita troca",
"IPVA pago",
"Licenciado"
],
"fipe_pct": 94,
"good_deal": true,
"photo_count": 8,
"image_url": "https://image.webmotors.com.br/_fotos/anunciousados/gigante/2026/202609/20260909/volksw…",
"auction": false,
"seller_id": "25976253",
"seller_type": "private",
"seller_kind": "Pessoa Física",
"seller_name": null,
"city": "São Paulo",
"state": "SP",
"page": 1,
"position": 1,
"mode": "search",
"sort": "relevance"
},
{
"source": "webmotors.com.br",
"scraped_at": "2026-09-28T09:36:37.696371+00:00",
"url": "https://www.webmotors.com.br/comprar/volkswagen/gol/10-mi-8v-flex-4p-manual-gv/4-portas…",
"sku": "79374446",
"title": "VOLKSWAGEN GOL 1.0 MI 8V FLEX 4P MANUAL G.V",
"price": 25649.0,
"currency": "BRL",
"vehicle": "car",
"condition": "used",
"make": "VOLKSWAGEN",
"model": "GOL",
"version": "1.0 MI 8V FLEX 4P MANUAL G.V",
"year_fabrication": 2010,
"year_model": 2011,
"odometer_km": 197000,
"transmission": "Manual",
"body_type": "Hatchback",
"color": "Preto",
"doors": 4,
"engine_litres": null,
"engine_cc": null,
"horsepower_cv": 72,
"traction": null,
"armored": false,
"attributes": [
"Aceita troca",
"Único dono",
"IPVA pago",
"… 1 more"
],
"fipe_pct": 99,
"good_deal": true,
"photo_count": 8,
"image_url": "https://image.webmotors.com.br/_fotos/anunciousados/gigante/2026/202609/20260913/volksw…",
"auction": false,
"seller_id": "33063133",
"seller_type": "private",
"seller_kind": "Pessoa Física",
"seller_name": null,
"city": "Manaus",
"state": "AM",
"page": 1,
"position": 2,
"mode": "search",
"sort": "relevance"
}
]First 2 of 12 rows from a real run — sample_output.json. Long values shortened here.
Handles what breaks naive Webmotors scrapers
Misspelt filters caught, not widened
/carros/estoque/volkswagen/gool answers HTTP 200 with every Volkswagen on the site. After page 1 the run reads back what the site applied and refuses (exit 2) when it is not what you asked for.
Sort decides the sample
Under the default "Mais relevantes" the first 47 cars had no private sellers; under "Menor preço" 44 were private. sort is a column, the sidecar records it, and diff_runs.py refuses to compare runs sorted differently.
Page past the end detected
Page 500 of a 40-page search returns page 1 with HTTP 200. Pages are planned from the count the site states, and a page holding only rows already fetched ends the run.
A browser that does not announce itself
PerimeterX refuses headless Chromium's own User-Agent and overrides that drop the client hints. The engines send a plain Chrome User-Agent naming the browser's real version, with matching hints, and retry a refused page from a fresh browser.
Scraping infrastructure included
The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.
Proxy rotation
Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.
Fingerprints
Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.
Scraping Browser API
Point --cdp-endpoint at a managed Chromium with its own exit country — no browser infrastructure to run yourself.
Honest run metadata
Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.
Diff two runs
diff_runs.py compares two complete Webmotors snapshots by id — new, removed and changed rows for monitoring.
What teams do with Webmotors data
Used-car price analysis
Compare asking prices against the FIPE table with fipe_pct, and against the site's own lowest / average / highest market price in ad mode.
Dealer inventory monitoring
Track a make, model or state's listings over time and diff added, removed and repriced adverts.
Regional market research
Scrape searches per state or city to compare supply, mileage and pricing across Brazil.
Deal finding
Filter rows by the "Bom negócio" badge, FIPE percentage and seller facts such as IPVA pago or Único dono.
Free to run. Pay only for what the site requires.
Free to run · paid add-ons optional
You need a residential exit: measured 2026-09-28, every datacentre client got a CloudFront 403, while headful or correctly configured headless Chromium on a residential connection was served — your own home connection counts. From a server, a 2Captcha residential proxy (WEBMOTORS_PROXY) or the Scraping Browser API, which was served on every attempt measured, is what this site needs. The Scraper API works here only with --cdp-url through a Scraping Browser session.
Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.
Good to know
- The default "Mais relevantes" ordering reorders within minutes, so a row can be fetched twice or missed during a run; use --sort for stable runs.
- This repo does not implement solving PerimeterX's Press & Hold challenge.
- Selenium cannot authenticate a proxy or the Scraping Browser API.
2scraper vs. hosted scraper APIs
| 2scraper | Typical hosted API | |
|---|---|---|
| Code | Open source, MIT — read and patch it | Closed |
| Cost | Free; optional services billed only when used | Subscription or per request / per result |
| Runs on | Your machine, server or CI | The vendor's infrastructure |
| Output | JSON/CSV files + run metadata | API responses |
| When the site changes | Fix it yourself or open an issue / PR | Wait for the vendor |
Webmotors scraper FAQ
Do I need a proxy?
You need a residential address. From your own home connection none is needed; from a VPS or datacentre every client was refused, so use a residential proxy or the Scraping Browser API.
Why does a run stop at about 10,000 results?
Webmotors serves at most about 10,000 results per search (213 pages of 47), even when 348,317 cars matched. The sidecar says capped_by_site: true; narrow the search to reach the rest.
Does it cover motorcycles?
Yes. Pass --category motos, or a motorcycle search URL.
How do I spell makes and models?
As the site's own addresses do, e.g. onix-plus, mercedes-benz, hb20. A make or model the site did not recognise ends the run with exit 2 instead of returning the whole catalogue.
Does it collect private sellers' personal data?
No. seller_name is null for every private seller and their description and postal code are not written; city and state are kept, and dealers' trading names are kept.
Is scraping Webmotors legal?
It reads public pages shown to every anonymous visitor, at a pace you set, and contacts no one. It is public data; check the site's terms and your local law before using it.