1. 2scraper
  2. Classifieds, property & travel
  3. Webmotors
Open source · MITFree to run · paid add-ons optional

Webmotors Scraper

Car and motorcycle listings from Brazil's webmotors.com.br, with FIPE comparison and the site's market-price range.

  • Playwright
  • Selenium
  • Puppeteer
  • Scraping Browser API
  • JSON
  • CSV
webmotors-scraper
# Search by make and model
$ .venv/bin/python playwright_scraper.py --make volkswagen --model gol --pages 3
✓ 12 sample rows · 41 columns in sample_output.json
2modes: search and ad
41 / 50columns per search row / advert row
47listings per search page
~10,000results the site serves per search
Available Webmotors scrapers

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 make

Motorcycle listings in the same row shape, here ordered newest model year first.

Advert pages

Input: A search run's JSON via --ads-file

Everything 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.

Quick start

From zero to Webmotors data in 3 steps

  1. 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
  2. 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
  3. Get structured data

    Rows land in JSON or CSV (--format), with a .meta.json sidecar recording status, pages fetched and why the run stopped.

Output

Webmotors data fields

41 columns per row in a predictable schema. The most useful ones:

FieldWhat it holds
skuWebmotors advert id
titleMake, model and version as listed
priceAsking price as a number
makeMake, e.g. VOLKSWAGEN
modelModel, e.g. GOL
versionVersion / trim
year_fabricationYear built
year_modelModel year
odometer_kmMileage in km
horsepower_cvHorsepower (cv)
attributesThe seller's ticked facts, e.g. IPVA pago, Único dono
fipe_pctPrice against the FIPE table, in percent
good_dealThe site's "Bom negócio" badge
seller_typeprivate or dealer
cityCity of the listing
stateState, 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.

Built for Webmotors

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.

Everything built in

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.

Use cases

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.

Access & pricing

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

2scraperTypical hosted API
CodeOpen source, MIT — read and patch itClosed
CostFree; optional services billed only when usedSubscription or per request / per result
Runs onYour machine, server or CIThe vendor's infrastructure
OutputJSON/CSV files + run metadataAPI responses
When the site changesFix it yourself or open an issue / PRWait for the vendor
FAQ

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.