Tony Wang6 min readHow to Scrape Used Car Listings in 2026 (API & Python)
Scrape used car listings in 2026 across CarMax, Autotrader, and Cars.com — pricing, mileage, dealer, and history data — DIY, no-code, or a structured API.
The fastest way to scrape used car listings in 2026 is to call a structured API that returns normalized JSON across the major sites — instead of building and maintaining a separate parser for each one's JavaScript-heavy inventory pages. CarMax, Autotrader, and Cars.com are the three biggest used-car marketplaces in the US, none has a self-serve public API, and each defends its pages with real anti-bot infrastructure. This guide covers all three approaches across all three sites, what each returns, where DIY breaks, and the legal basics.
Why scrape used car listings?
Vehicle listing data across CarMax, Autotrader, and Cars.com powers:
- Pricing intelligence — track how a model's asking price moves with mileage, trim, and market, and spot underpriced listings.
- Inventory and market research — measure how many of a given make/model are on the market and where.
- Deal-finding tools — build a "flag good deals" feature using the same pricing signals the sites themselves compute.
- Dealer analytics — track a dealer's inventory turnover, pricing strategy, and rating over time.
- Depreciation and demand modeling — combine listing data across sites to model how a vehicle's value moves with age and mileage.
Is it legal to scrape used car listings?
Option 1: DIY in Python (and why it breaks)
All three sites render listings from client-side JSON rather than plain HTML, so a DIY scraper has to reach the underlying API each page calls:
import requests
# Cars.com renders from a public GraphQL endpoint
resp = requests.post(
"https://graph.cars.com/graphql/api",
json={"query": "...", "variables": {"stockType": "used", "zip": "78701"}},
headers={"User-Agent": "Mozilla/5.0 (compatible; research-bot/1.0)"},
)
It demos and then breaks:
- The ToS is explicit. Autotrader's terms name "robots, screen scrapers, or spiders" directly — this isn't an ambiguous case.
- Real anti-bot at the edge. A plain, unauthenticated request to CarMax is blocked before it even reaches the page (its edge returned an Akamai-style "Access Denied" to a routine
robots.txtfetch during this guide's research), and Cars.com fronts with a Cloudflare challenge on the same request. Naiverequestscalls get blocked fast; a browser-based scraper needs constant upkeep as those defenses update. - Three different backends, three different shapes. CarMax runs its mobile API on Azure Front Door with a
stock_numberprimary key; Autotrader nestsmake/model/trimas objects with separatecode/namefields and serves both new and used inventory from one endpoint; Cars.com runs a public GraphQL API keyed by a UUIDlisting_id. There's no shared schema — you write and maintain three parsers, not one. - No official third-party API. Real inventory feeds move through dealer/DMS integrations (vAuto, HomeNet) via FTP/CSV or partner APIs — gated to licensed dealerships, not available to a third-party developer building a price-intelligence product.
Option 2: No-code tools
Marketplace scraper actors exist for each site individually and suit one-off pulls, but you're back to three separate tools with three separate schemas for a cross-site view, and they inherit the same anti-bot fragility as DIY.
Option 3: A structured used-car API
For a repeatable, cross-site workflow, CarMax, Autotrader, and Cars.com scraping APIs each return normalized JSON with no page parsing or edge-defense upkeep. Search CarMax by make and price range:
curl "https://api.crawlora.net/api/v1/carmax/search?make=Toyota&max_price=25000&zip=78701" \
-H "x-api-key: $CRAWLORA_API_KEY"
Pull detail across all three in Python — each search returns the id you need for its own detail call:
import requests
h = {"x-api-key": "YOUR_API_KEY"}
base = "https://api.crawlora.net/api/v1"
carmax_hits = requests.get(f"{base}/carmax/search", headers=h, params={"make": "Toyota", "max_price": 25000}).json()["data"]["vehicles"]
carmax_detail = requests.get(f"{base}/carmax/vehicle/{carmax_hits[0]['stock_number']}", headers=h).json()["data"]
autotrader_hits = requests.get(f"{base}/autotrader/search", headers=h, params={"make": "Toyota", "zip": "78701"}).json()["data"]["vehicles"]
autotrader_detail = requests.get(f"{base}/autotrader/vehicle/{autotrader_hits[0]['id']}", headers=h).json()["data"]
carsdotcom_hits = requests.get(f"{base}/carsdotcom/search", headers=h, params={"zip": "78701", "stock_type": "used"}).json()["data"]["vehicles"]
carsdotcom_detail = requests.get(f"{base}/carsdotcom/vehicle/{carsdotcom_hits[0]['listing_id']}", headers=h).json()["data"]
A CarMax search response is normalized JSON (real fields — check the docs):
{
"code": 200,
"msg": "OK",
"data": {
"total_count": 91136,
"vehicles": [
{
"stock_number": 28187774,
"vin": "1FMJK1K89REA27006",
"year": 2024,
"make": "Ford",
"model": "Expedition Max",
"mileage": 41406,
"price_info": { "price": 45998, "is_price_reduced": true, "previous_price": 46998 },
"store": { "id": 7124, "name": "Canoga Park" },
"prior_uses": ["Leased Vehicle", "Rental"],
"url": "https://www.carmax.com/car/28187774"
}
]
}
}
CarMax's vehicle detail adds history (accident and owner counts) and warranties; Autotrader's detail carries vehicle_history_flags (e.g. NO_SALVAGE_TITLE, NO_ACCIDENTS_REPORTED) and both msrp/sale_price for new inventory; Cars.com's detail is the most pricing-forward of the three — it ships a computed deal_rating:
{
"deal_rating": {
"rating": "fair",
"good_price_min": 17591,
"good_price_max": 19566,
"predicted_price": 17950,
"predicted_price_difference": -4042
}
}
Store one row per listing per site, keyed by VIN where present (the one field that's directly comparable across all three), and re-run on a schedule to track price changes and new inventory.
What you can collect
Public listing data per site: search results (year, make, model, trim, mileage, price, exterior/interior color, transmission, fuel type, dealer/store); full vehicle detail (specs, features, warranties where CarMax lists them, deal_rating on Cars.com, vehicle_history_flags on Autotrader); CarMax store locations and "shop by brand" taxonomy; and Autotrader dealer profiles (name, rating, address, current inventory count). Public listing facts only — not a full vehicle-history report.
Limitations and common challenges
- No shared schema across sites. Normalize at ingestion — match on VIN where available, since stock numbers and listing ids are site-specific.
- Real anti-bot on all three. Expect edge-level challenges (Akamai-class on CarMax, Cloudflare on Cars.com) on naive requests.
- New and used are mixed on Autotrader. Filter on
listing_type/conditionif you only want used inventory. - Prices and inventory turn over fast. A dealer sells or re-prices a car in days, not months — re-pull on a schedule rather than trusting a snapshot.
- Public data only. This is listing-page data, not a paid vehicle-history report, and not a way to reach dealer or buyer personal contact details.
Where this gets used
- Pricing intelligence — track asking price against mileage and trim, and flag underpriced listings using the same
deal_rating-style signal Cars.com exposes. - Market research — measure inventory depth and turnover by make, model, and region.
- Dealer analytics — watch a specific dealer's pricing and inventory over time.
Sources
Start collecting
Try it first, free: run any public URL through the Free Web Scraper, or check whether a site blocks bots with the Anti-Bot Checker — no signup.
Test the search endpoints in the Playground, check the schema in the API docs, and review pricing. For the other side of the transaction, how to scrape job postings covers the same "no self-serve API, integrate per vendor" problem for a completely different market. For real estate's version of the same portal-by-portal pattern, see how to scrape real estate listings. See also how to choose a web scraping API and is web scraping legal.
Part of our how-to-scrape guide series — every platform we cover, in one index.
Frequently asked questions
Do CarMax, Autotrader, and Cars.com have official APIs?
No self-serve public API for any of the three. Real inventory feeds flow through dealer/DMS partnerships (vAuto, HomeNet) via FTP/CSV or partner APIs, gated to licensed dealerships — not available to a third-party developer. A structured API is the practical route for cross-site price and inventory research.
Is scraping used car listings legal?
Listing facts (year, make, model, price, mileage, VIN) aren't copyrightable, but each site's terms restrict automated access — Autotrader's terms explicitly name robots, screen scrapers, and spiders. Stick to public listing facts, respect rate limits, and never attempt to pull a full paid vehicle-history report through a listing page. Not legal advice.
How do I compare a listing's price across sites?
Match on VIN — it's the one field directly comparable across CarMax, Autotrader, and Cars.com, since stock numbers and listing ids are site-specific. Search each site, resolve VIN from the results, then join on it.
Which site tells me if a listing is a good deal?
Cars.com ships a computed deal_rating (fair/good/great) on vehicle detail, with a predicted_price and a good_price_min/max range you can use directly. CarMax and Autotrader don't compute this, but you can build the same signal from price-vs-mileage-vs-trim across their listings.
Can I get a dealer's full inventory?
Yes on two of the three. Autotrader's /autotrader/dealer/{id} returns the dealer profile plus a first page of current inventory; CarMax's /carmax/stores and /carmax/store/{id} return physical store locations. Cars.com's search accepts a zip/radius filter instead of a direct dealer lookup.
Does this include vehicle history reports like Carfax?
No. This returns what each site's own listing page shows publicly — accident/owner counts and prior-use flags where the site itself surfaces them (CarMax's history object, Autotrader's vehicle_history_flags) — not a full Carfax or AutoCheck report, which are separately licensed, paid products.
Are new-vehicle listings included?
Autotrader mixes new and used inventory in one search endpoint — filter on listing_type or condition if you only want used. CarMax and Cars.com's used inventory in this guide's endpoints are used-only by design.