How to Scrape CarGurus Used Car Listings and Prices
CarGurus is one of the largest used-car marketplaces, and its listings are a rich public source of vehicle inventory, pricing, and its own deal ratings: year, make, model, mileage, price, and how good a deal it is. This guide covers what a listing holds, where the data lives, why the site is defended, and how to turn it into a clean inventory feed. The worked example uses a real CarGurus listing.
What data a CarGurus listing holds
Each listing carries the car, the price, and CarGurus's own read on the deal. On a real listing it read: a 2024 Ford Maverick XLT SuperCrew AWD, 31,205 miles, priced at 27,254 dollars (27,690 with fees) against an MSRP of 35,200, rated a GOOD_PRICE, and on the market for 73 days. The deal rating is CarGurus's own signal, set by comparing the asking price to the price it expects for that car, its Instant Market Value, which on this listing was about 28,803 dollars. Year, make, model, mileage, price, deal rating, and days on market are the fields that matter.
Where the data actually lives
CarGurus serves its search from an internal JSON API, cargurus.com/search, which returns a tiles array where each listing tile holds a data object: the make, model, year, and trim, the mileage, a price object with the current, total, expected, and MSRP figures, the deal rating, the days on market, and the seller. Reading that JSON is cleaner than parsing rendered cards. The distinctive fields are the deal rating and the expected price, which let you flag underpriced or overpriced inventory directly from the feed.
Skip the build, get the feed
Send us your CarGurus target and we will scrape a real sample and send it back, no signup.
Get a free sampleThe anti-bot reality
CarGurus sits behind Cloudflare and its bot management, so an automated request meets a challenge before the inventory, and a naive script gets a block rather than cars. Reliable collection means clearing that layer and pacing requests across the search pages, and keeping it working as the protection changes. That handling, not the parsing, is the real work, since the JSON itself is clean once you can reach it.
Turning it into an inventory feed
For inventory or price intelligence you want one row per listing, keyed on the listing id or VIN, keeping the price, mileage, deal rating, and days on market on every run. That lets you see price drops, how long a car sits before it sells, and which listings are underpriced against CarGurus's own expected value. Store each run with a date and you have a live read on the market rather than a single snapshot.
Build it yourself or have it delivered
For a handful of cars, reading the JSON from a few searches by hand works. At scale the Cloudflare layer, the paging, and the upkeep turn it into a standing maintenance job. Once you need the whole market refreshed on a schedule, a done-for-you feed is usually cheaper than the upkeep. You tell us the makes, models, or markets you care about, and we deliver a clean inventory feed and keep it running.
Where each field lives in the search JSON
CarGurus returns its results as a tiles array, and each listing tile holds a data object with the fields that matter. ontologyData carries the makeName, modelName, carYear, and trimName. mileageData holds the numeric odometer value with its unit, alongside a localizedMileage string. priceData is the rich one: current and basePrice, totalPrice with fees, an expected value, the msrp, and localized display strings for each. dealRating carries CarGurus' verdict (GOOD_PRICE and similar), and it is set by comparing the asking price to imvPrice, the Instant Market Value, so pulling both lets you compute how far under or over market each car is. daysOnMarket and the seller round it out, and the listing id is the dedupe key.
Step by step: reading a CarGurus search
CarGurus serves its search from an internal JSON API, so once you can reach it each listing is a clean object with the car, the price, and its deal rating. Here it is on a real listing, a 2024 Ford Maverick. The catch, covered right after, is Cloudflare.
import requests
# CarGurus loads its listings from an internal JSON search API.
url = "https://www.cargurus.com/search"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
data = requests.get(url, params={}, headers=headers).json() # site's own search params
for tile in data["tiles"]:
d = tile.get("data")
if d:
o = d["ontologyData"]
print(o["makeName"], o["modelName"], o["carYear"],
d["priceData"]["localizedPrice"], d["dealRating"], d["daysOnMarket"])| Vehicle | 2024 Ford Maverick XLT SuperCrew AWD |
|---|---|
| Mileage | 31,205 miles |
| Price | $27,254 (MSRP $35,200) |
| Deal rating | GOOD_PRICE |
| Days on market | 73 |
| Marketplace | CarGurus |
A sample of the clean data we deliver for one product.
The JSON is clean once you have it. The hard part is reaching it: CarGurus sits behind Cloudflare, so the plain request above meets a challenge before the inventory and often comes back as a block rather than cars. Handling that, paging across the search results, and keeping it working as the protection shifts is the real job. We run it and deliver a clean inventory feed with the price, mileage, and deal rating on every row.
Fields worth capturing from CarGurus
- Year
- Make
- Model
- Trim
- Mileage
- Price
- Expected price (market value)
- MSRP
- Deal rating
- Days on market
- Dealer / seller city
- Listing ID
- VIN
Frequently asked questions
Related service: Catalog & Assortment Tracking
Want a CarGurus feed without the build?
Send us the products or pages you need, and we will deliver a clean feed and maintain it. Start with a free sample.
Get a Free Sample