How to Get Used-Car Pricing Data: A Developer's Guide
Accurate used-car pricing is the foundation of almost every automotive product — dealer appraisal tools, residual-value models, import/export calculators and consumer “is this a good deal?” features all start with the same question: what is this car actually worth right now?
The answer lives in live marketplace data. This guide shows how to collect reliable used-car pricing data across markets — listing prices, price history and the signals that reveal a fair market value — through one unified API.
What “pricing data” actually means
A single asking price tells you very little. Useful pricing data is a small bundle of fields:
- Listed price — the current asking price on the marketplace
- Price history — how that price has moved since the car was listed
- Days on market — how long the listing has been live
- Vehicle identity — make, model, trim, model year, mileage, fuel type, transmission
- Condition — inspection sheet and accident/repair history (where the marketplace provides it)
- Context — dealer vs private seller, location, certification status
A price only becomes meaningful next to mileage, model year and condition. That is why pricing intelligence is really a normalization problem: the same 2021 model can appear on five marketplaces under five different field names.
The traditional approach (and why it doesn’t scale)
Most teams start by scraping one marketplace directly:
- Build a scraper for the target site
- Handle its JavaScript rendering and private app API
- Add proxies when you get rate-limited or geo-blocked
- Re-fix the scraper every time the site’s HTML changes
- Repeat for every additional marketplace
Each marketplace is a separate engineering project with its own anti-bot stack. By the time you support three or four sources, you are maintaining more scraping infrastructure than product. And inconsistent field names across sources mean you still have to normalize everything yourself.
The unified-API approach
Carapis exposes every marketplace through one REST endpoint with one schema. You query /v2/listings with a source and filters, and you get back normalized records — the same field names whether the data came from Encar in Korea or Mobile.de in Germany.
Pull current listings for a model
import requests
API_KEY = "your_carapis_key"
resp = requests.get(
"https://api.carapis.com/v2/listings",
params={
"source": "encar",
"make": "Hyundai",
"model": "Grandeur",
"year_min": 2020,
"limit": 100,
},
headers={"Authorization": f"Bearer {API_KEY}"},
)
listings = resp.json()["results"]
print(f"Found {len(listings)} listings")Compute a fair market value
With normalized listings you can build a simple market-value estimate — the median price for comparable cars, controlled for mileage band:
from statistics import median
def market_value(listings, target_mileage, mileage_tolerance=20000):
"""Median price of comparable cars within a mileage band."""
comparable = [
car["price"]
for car in listings
if car.get("price")
and car.get("mileage")
and abs(car["mileage"] - target_mileage) <= mileage_tolerance
]
if len(comparable) < 5:
return None # not enough comps to be reliable
return median(comparable)
value = market_value(listings, target_mileage=60000)
print(f"Estimated market value: {value:,.0f}" if value else "Not enough comps")Spot below-market listings
Once you have a market value, flagging good deals is straightforward:
def find_deals(listings, fair_value, min_discount=0.10):
deals = []
for car in listings:
price = car.get("price")
if not price:
continue
discount = (fair_value - price) / fair_value
if discount >= min_discount:
deals.append({
"make": car["make"],
"model": car["model"],
"year": car["year"],
"mileage": car.get("mileage"),
"price": price,
"discount": discount,
})
deals.sort(key=lambda d: d["discount"], reverse=True)
return deals
for deal in find_deals(listings, value)[:10]:
print(
f"{deal['year']} {deal['make']} {deal['model']} — "
f"{deal['price']:,} ({deal['discount']:.0%} below market)"
)Why price history matters
A static price is a snapshot; price history is a signal. A listing that has dropped twice in three weeks usually means a motivated seller and room to negotiate. Carapis returns a priceHistory field on platforms that expose it, so you can track movement without re-polling and diffing yourself:
for car in listings:
history = car.get("priceHistory") or []
if len(history) >= 2:
first, last = history[0]["price"], history[-1]["price"]
if last < first:
drop = (first - last) / first
print(f"{car['make']} {car['model']}: {drop:.0%} price drop")Cross-market pricing
The real advantage of a unified API shows up when you compare across markets. The same model often trades at very different prices in Korea, Germany and Japan — which is exactly the spread that import/export businesses live on. Because every source returns the same schema, you can run one comparison loop over many marketplaces:
SOURCES = ["encar", "mobile-de", "che168"]
for source in SOURCES:
resp = requests.get(
"https://api.carapis.com/v2/listings",
params={"source": source, "make": "Toyota", "model": "Camry", "limit": 50},
headers={"Authorization": f"Bearer {API_KEY}"},
)
prices = [c["price"] for c in resp.json()["results"] if c.get("price")]
if prices:
print(f"{source}: median {median(prices):,.0f} ({len(prices)} listings)")Best practices
- Always control for mileage and year. A median price without a mileage band is misleading.
- Require a minimum number of comps. Five or more comparable listings before you trust an estimate.
- Use price history, not single snapshots. Movement reveals motivation.
- Factor in condition. On marketplaces with inspection sheets, a clean record is worth a premium — don’t treat all listings as equal.
- Cache aggressively. Pricing distributions shift over days, not seconds.
Conclusion
Reliable used-car pricing isn’t about one number — it’s about a normalized stream of listings, price history and condition data you can turn into market values, deal flags and cross-market spreads. With a unified API you skip the per-marketplace scraping treadmill and spend your time on the model, not the plumbing.
Get started → • View API reference →
Related resources: