Web Scraping for Automotive & Dealerships: How AI Agents Track Inventory, Pricing, Reviews & Market Data in 2026
The global automotive industry generates over $3 trillion annually. In the United States alone, more than 15 million new vehicles and 40 million used vehicles change hands each year, making the used car market worth over $800 billion. Dealerships, lenders, insurers, and automotive startups all depend on accurate, real-time market data to price vehicles, source inventory, and outmaneuver competitors.
Yet the data that powers these decisions โ dealer inventory listings, market valuations, auction results, customer reviews โ is scattered across dozens of platforms. Traditional automotive data providers like vAuto (Cox Automotive), DealerSocket, and MarketCheck charge $1,000โ$10,000+ per month for access. Meanwhile, much of this data is publicly available on sites like AutoTrader, Cars.com, CarGurus, KBB, and Edmunds.
In this guide, you'll build a complete automotive intelligence system using Python, the Mantis WebPerception API, and GPT-4o โ covering dealer inventory tracking, competitive pricing analysis, review monitoring, auction intelligence, and AI-powered market predictions.
Why Automotive Businesses Need Web Scraping
The automotive market is one of the most data-intensive industries in the world. Every vehicle is unique (year, make, model, trim, mileage, condition, options, history), which makes pricing both critical and complex:
- Inventory intelligence โ What are nearby dealers stocking? How long are vehicles sitting on their lots? What's the days-to-turn for specific models?
- Competitive pricing โ Is your 2024 RAV4 priced above or below market? What are the 10 closest comparable vehicles listed at?
- Market value tracking โ KBB, Edmunds, NADA, and CarGurus Instant Market Value all provide different estimates. Which one reflects reality?
- Auction data โ What are wholesale prices at Manheim and Copart? What's the buy-vs-retail spread on specific models?
- Review & reputation monitoring โ Google Reviews, DealerRater, and Cars.com reviews directly impact foot traffic and lead conversion
- NHTSA recalls & safety โ New recalls affect inventory value and create service revenue opportunities
- EV market shifts โ Tracking Tesla, Rivian, and legacy OEM EV inventory, incentives, and depreciation curves
Build Automotive Intelligence Agents with Mantis
Scrape dealer inventory, vehicle pricing, reviews, and auction data from any automotive site with one API call. AI-powered extraction handles every listing format.
Get Free API Key โArchitecture: The 6-Step Automotive Intelligence Pipeline
- Dealer inventory scraping โ Monitor competitor lots, new listings, and days-on-market across AutoTrader, Cars.com, CarGurus, and dealer websites
- Pricing & market value tracking โ Pull KBB, Edmunds, NADA values and compare against actual listing prices
- Review & reputation monitoring โ Track Google Reviews, DealerRater, Cars.com dealer reviews for sentiment shifts
- Auction intelligence โ Monitor Manheim Market Report, Copart, and IAAI for wholesale pricing trends
- GPT-4o market analysis โ Generate pricing recommendations, predict depreciation, identify arbitrage opportunities
- Alert delivery โ Route underpriced vehicles, competitor price changes, new negative reviews, and recall alerts to your team via Slack
Step 1: Define Your Automotive Data Models
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
from enum import Enum
class ListingSource(str, Enum):
AUTOTRADER = "autotrader"
CARSCOM = "cars.com"
CARGURUS = "cargurus"
DEALER_WEBSITE = "dealer_website"
FACEBOOK_MARKETPLACE = "facebook_marketplace"
CRAIGSLIST = "craigslist"
class VehicleListing(BaseModel):
"""A vehicle listing from a dealer or marketplace."""
vin: Optional[str]
year: int
make: str
model: str
trim: Optional[str]
mileage: int
price: float
original_msrp: Optional[float]
exterior_color: Optional[str]
interior_color: Optional[str]
transmission: Optional[str]
drivetrain: Optional[str] # AWD, FWD, RWD, 4WD
fuel_type: Optional[str] # gasoline, diesel, electric, hybrid, plug-in hybrid
engine: Optional[str]
body_style: Optional[str] # sedan, SUV, truck, coupe, van
dealer_name: str
dealer_city: Optional[str]
dealer_state: Optional[str]
days_on_market: Optional[int]
listing_source: ListingSource
carfax_one_owner: Optional[bool]
accidents_reported: Optional[int]
source_url: str
scraped_at: datetime
class DealerInventory(BaseModel):
"""Aggregated dealer inventory snapshot."""
dealer_name: str
dealer_id: Optional[str]
total_vehicles: int
new_count: int
used_count: int
certified_count: int
avg_price: float
avg_mileage: float
avg_days_on_market: Optional[float]
top_makes: List[str]
price_range: str # "$15,000 - $85,000"
scraped_at: datetime
class MarketValue(BaseModel):
"""Market value estimate from pricing sources."""
vin: Optional[str]
year: int
make: str
model: str
trim: Optional[str]
mileage: int
condition: str # excellent, good, fair, rough
kbb_private_party: Optional[float]
kbb_dealer_retail: Optional[float]
kbb_trade_in: Optional[float]
edmunds_tmv: Optional[float]
cargurus_imv: Optional[float] # Instant Market Value
avg_market_price: Optional[float] # average of available estimates
price_vs_market: Optional[str] # "below market", "at market", "above market"
scraped_at: datetime
class AuctionResult(BaseModel):
"""Wholesale auction result from Manheim, Copart, or IAAI."""
vin: Optional[str]
year: int
make: str
model: str
trim: Optional[str]
mileage: int
condition_grade: Optional[float] # Manheim 1.0-5.0 scale
sale_price: float
buy_now_price: Optional[float]
auction_house: str # "manheim", "copart", "iaai"
auction_location: Optional[str]
sale_date: datetime
damage_type: Optional[str] # for salvage: "front end", "flood", etc.
title_status: Optional[str] # clean, salvage, rebuilt
retail_estimate: Optional[float]
wholesale_to_retail_spread: Optional[float]
source_url: str
Step 2: Scrape Dealer Inventory & Competitor Listings
from mantis import MantisClient
import asyncio
mantis = MantisClient(api_key="your-mantis-api-key")
async def scrape_dealer_inventory(
dealer_urls: List[dict], # [{"dealer": "ABC Motors", "url": "https://..."}]
max_pages: int = 5
) -> List[VehicleListing]:
"""
Scrape vehicle listings from dealer websites and aggregator platforms.
Mantis handles JS rendering for React/Angular dealer sites.
"""
listings = []
for dealer in dealer_urls:
for page in range(1, max_pages + 1):
url = f"{dealer['url']}?page={page}" if page > 1 else dealer["url"]
result = await mantis.scrape(
url=url,
extract={
"vehicles": [{
"year": "number",
"make": "string",
"model": "string",
"trim": "string or null",
"mileage": "number",
"price": "number",
"exterior_color": "string or null",
"transmission": "string or null",
"drivetrain": "string or null",
"fuel_type": "string or null",
"engine": "string or null",
"body_style": "string or null",
"vin": "string or null",
"days_on_market": "number or null",
"carfax_one_owner": "boolean or null",
"detail_url": "string"
}],
"total_results": "number or null"
}
)
for v in result.get("vehicles", []):
listing = VehicleListing(
vin=v.get("vin"),
year=v.get("year", 0),
make=v.get("make", ""),
model=v.get("model", ""),
trim=v.get("trim"),
mileage=v.get("mileage", 0),
price=v.get("price", 0),
exterior_color=v.get("exterior_color"),
transmission=v.get("transmission"),
drivetrain=v.get("drivetrain"),
fuel_type=v.get("fuel_type"),
engine=v.get("engine"),
body_style=v.get("body_style"),
dealer_name=dealer["dealer"],
days_on_market=v.get("days_on_market"),
listing_source=detect_source(dealer["url"]),
carfax_one_owner=v.get("carfax_one_owner"),
source_url=v.get("detail_url", url),
scraped_at=datetime.now()
)
listings.append(listing)
# Stop if no more results
if len(result.get("vehicles", [])) == 0:
break
return listings
async def scrape_market_search(
make: str, model: str, year_min: int, year_max: int,
zip_code: str = "10001", radius_miles: int = 100
) -> List[VehicleListing]:
"""
Search major automotive marketplaces for comparable vehicles.
Builds competitive pricing intelligence for specific models.
"""
search_urls = [
f"https://www.autotrader.com/cars-for-sale/all-cars/{make}/{model}?zip={zip_code}&searchRadius={radius_miles}&startYear={year_min}&endYear={year_max}",
f"https://www.cars.com/shopping/results/?stock_type=all&makes[]={make}&models[]={make}-{model}&year_min={year_min}&year_max={year_max}&zip={zip_code}&maximum_distance={radius_miles}",
f"https://www.cargurus.com/Cars/inventorylisting/viewDetailsFilterViewInventoryListing.action?zip={zip_code}&showNegotiable=true&sortDir=ASC&sourceContext=carGurusHomePageModel&distance={radius_miles}&entitySelectingHelper.selectedEntity={make.lower()}-{model.lower()}"
]
all_listings = []
for url in search_urls:
result = await mantis.scrape(
url=url,
extract={
"vehicles": [{
"year": "number",
"make": "string",
"model": "string",
"trim": "string or null",
"mileage": "number",
"price": "number",
"dealer_name": "string",
"dealer_city": "string or null",
"dealer_state": "string or null",
"days_on_market": "number or null",
"detail_url": "string"
}]
}
)
for v in result.get("vehicles", []):
listing = VehicleListing(
year=v.get("year", 0),
make=v.get("make", make),
model=v.get("model", model),
trim=v.get("trim"),
mileage=v.get("mileage", 0),
price=v.get("price", 0),
dealer_name=v.get("dealer_name", ""),
dealer_city=v.get("dealer_city"),
dealer_state=v.get("dealer_state"),
days_on_market=v.get("days_on_market"),
listing_source=detect_source(url),
source_url=v.get("detail_url", url),
scraped_at=datetime.now()
)
all_listings.append(listing)
return all_listings
Tracking Days-on-Market & Inventory Aging
import sqlite3
def analyze_inventory_aging(
listings: List[VehicleListing],
db_path: str = "automotive_intel.db"
) -> dict:
"""
Track how long vehicles sit on dealer lots.
Aging inventory = motivated sellers = negotiation opportunities.
"""
conn = sqlite3.connect(db_path)
aging_analysis = {
"fresh_listings": [], # < 7 days
"normal_aging": [], # 7-30 days
"aging_inventory": [], # 30-60 days
"stale_inventory": [], # 60+ days โ likely price drops coming
"newly_removed": [] # was listed, now gone โ sold or pulled
}
for listing in listings:
dom = listing.days_on_market or 0
if dom < 7:
aging_analysis["fresh_listings"].append(listing)
elif dom < 30:
aging_analysis["normal_aging"].append(listing)
elif dom < 60:
aging_analysis["aging_inventory"].append(listing)
else:
aging_analysis["stale_inventory"].append(listing)
# Store for historical tracking
conn.execute("""
INSERT OR REPLACE INTO inventory_tracking
(vin, dealer, price, days_on_market, scraped_at)
VALUES (?, ?, ?, ?, ?)
""", (listing.vin, listing.dealer_name, listing.price,
dom, listing.scraped_at.isoformat()))
# Check for removed listings (previously tracked, now missing)
tracked_vins = set(l.vin for l in listings if l.vin)
previously_tracked = conn.execute("""
SELECT DISTINCT vin, dealer, price FROM inventory_tracking
WHERE scraped_at > datetime('now', '-7 days')
""").fetchall()
for vin, dealer, last_price in previously_tracked:
if vin and vin not in tracked_vins:
aging_analysis["newly_removed"].append({
"vin": vin, "dealer": dealer,
"last_price": last_price, "likely_sold": True
})
conn.commit()
conn.close()
return {
"summary": {
"total_tracked": len(listings),
"fresh_pct": round(len(aging_analysis["fresh_listings"]) / max(len(listings), 1) * 100, 1),
"stale_pct": round(len(aging_analysis["stale_inventory"]) / max(len(listings), 1) * 100, 1),
"avg_dom": round(sum(l.days_on_market or 0 for l in listings) / max(len(listings), 1), 1),
"likely_sold_this_week": len(aging_analysis["newly_removed"])
},
"details": aging_analysis
}
Step 3: Market Value Comparison & Pricing Intelligence
async def get_market_values(
vehicles: List[dict] # [{"year": 2023, "make": "Toyota", "model": "RAV4", "trim": "XLE", "mileage": 25000}]
) -> List[MarketValue]:
"""
Pull market value estimates from KBB, Edmunds, and CarGurus.
Cross-reference multiple sources for accurate pricing.
"""
values = []
for vehicle in vehicles:
ymm = f"{vehicle['year']} {vehicle['make']} {vehicle['model']}"
# KBB values
kbb_url = f"https://www.kbb.com/{'_'.join([vehicle['make'].lower(), vehicle['model'].lower(), str(vehicle['year'])])}/"
kbb_result = await mantis.scrape(
url=kbb_url,
extract={
"private_party_value": "number or null",
"dealer_retail_value": "number or null",
"trade_in_value": "number or null",
"fair_market_range_low": "number or null",
"fair_market_range_high": "number or null"
}
)
# Edmunds TMV
edmunds_url = f"https://www.edmunds.com/{vehicle['make'].lower()}/{vehicle['model'].lower()}/{vehicle['year']}/appraisal/"
edmunds_result = await mantis.scrape(
url=edmunds_url,
extract={
"true_market_value": "number or null",
"private_party_value": "number or null",
"dealer_retail_value": "number or null"
}
)
# CarGurus IMV
cargurus_url = f"https://www.cargurus.com/Cars/price-trends/{vehicle['make'].lower()}-{vehicle['model'].lower()}-d{vehicle['year']}"
cargurus_result = await mantis.scrape(
url=cargurus_url,
extract={
"instant_market_value": "number or null",
"price_trend_30d": "string or null",
"avg_listing_price": "number or null"
}
)
# Compute average across available sources
available_values = [
v for v in [
kbb_result.get("dealer_retail_value"),
edmunds_result.get("true_market_value"),
cargurus_result.get("instant_market_value")
] if v is not None
]
avg_value = sum(available_values) / len(available_values) if available_values else None
mv = MarketValue(
year=vehicle["year"],
make=vehicle["make"],
model=vehicle["model"],
trim=vehicle.get("trim"),
mileage=vehicle.get("mileage", 0),
condition=vehicle.get("condition", "good"),
kbb_private_party=kbb_result.get("private_party_value"),
kbb_dealer_retail=kbb_result.get("dealer_retail_value"),
kbb_trade_in=kbb_result.get("trade_in_value"),
edmunds_tmv=edmunds_result.get("true_market_value"),
cargurus_imv=cargurus_result.get("instant_market_value"),
avg_market_price=avg_value,
scraped_at=datetime.now()
)
values.append(mv)
return values
def identify_pricing_opportunities(
listings: List[VehicleListing],
market_values: List[MarketValue]
) -> dict:
"""
Find underpriced and overpriced vehicles by comparing
listing prices against market value estimates.
"""
opportunities = {"underpriced": [], "overpriced": [], "at_market": []}
value_lookup = {
f"{mv.year}-{mv.make}-{mv.model}": mv
for mv in market_values
}
for listing in listings:
key = f"{listing.year}-{listing.make}-{listing.model}"
mv = value_lookup.get(key)
if not mv or not mv.avg_market_price:
continue
diff_pct = ((listing.price - mv.avg_market_price) / mv.avg_market_price) * 100
entry = {
"listing": listing,
"market_value": mv.avg_market_price,
"diff_pct": round(diff_pct, 1),
"potential_savings": round(mv.avg_market_price - listing.price, 0)
}
if diff_pct < -8:
opportunities["underpriced"].append(entry)
elif diff_pct > 8:
opportunities["overpriced"].append(entry)
else:
opportunities["at_market"].append(entry)
# Sort underpriced by biggest discount
opportunities["underpriced"].sort(key=lambda x: x["diff_pct"])
return opportunities
Step 4: Review & Reputation Monitoring
async def scrape_dealer_reviews(
dealers: List[dict] # [{"name": "ABC Motors", "google_url": "...", "dealerrater_url": "..."}]
) -> dict:
"""
Monitor dealer reputation across Google Reviews, DealerRater,
and Cars.com. Catch negative trends before they impact leads.
"""
all_reviews = {}
for dealer in dealers:
dealer_reviews = {"google": [], "dealerrater": [], "carscom": []}
# Google Reviews
if dealer.get("google_url"):
google = await mantis.scrape(
url=dealer["google_url"],
extract={
"overall_rating": "number",
"total_reviews": "number",
"reviews": [{
"rating": "number",
"text": "string",
"author": "string",
"date": "string",
"response": "string or null"
}]
}
)
dealer_reviews["google"] = google.get("reviews", [])
# DealerRater
if dealer.get("dealerrater_url"):
dr = await mantis.scrape(
url=dealer["dealerrater_url"],
extract={
"overall_rating": "number",
"total_reviews": "number",
"recommended_pct": "number or null",
"reviews": [{
"rating": "number",
"title": "string",
"text": "string",
"department": "string or null",
"employee_mentioned": "string or null",
"date": "string"
}]
}
)
dealer_reviews["dealerrater"] = dr.get("reviews", [])
all_reviews[dealer["name"]] = dealer_reviews
return all_reviews
async def analyze_dealer_reputation(reviews: dict) -> dict:
"""Use GPT-4o to analyze dealer review patterns and trends."""
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "system",
"content": """Analyze these dealer reviews and provide:
1. REPUTATION SCORE: Overall assessment (1-10) with trend direction
2. STRENGTHS: What customers consistently praise
3. WEAKNESSES: Recurring complaints (sales pressure, service quality, pricing transparency)
4. DEPARTMENT BREAKDOWN: Sales vs. Service vs. Finance ratings
5. COMPETITIVE POSITIONING: How this dealer compares to others in the market
6. RESPONSE QUALITY: Are they responding to reviews? How effectively?
7. RED FLAGS: Any patterns suggesting serious issues (bait-and-switch, hidden fees, etc.)
8. RECOMMENDATIONS: Top 3 improvements to boost reviews
Be specific. Quote actual review language when relevant."""
}, {
"role": "user",
"content": f"Dealer reviews to analyze:\n{json.dumps(reviews, indent=2)}"
}],
temperature=0.2
)
return {"analysis": response.choices[0].message.content}
Step 5: Auction Intelligence & Wholesale Pricing
async def track_auction_results(
search_criteria: List[dict]
) -> List[AuctionResult]:
"""
Monitor wholesale auction prices from Copart and IAAI.
Essential for dealers sourcing inventory and lenders
calculating residual values.
"""
results = []
for criteria in search_criteria:
# Copart โ salvage and clean title auctions
copart_url = f"https://www.copart.com/lotSearchResults/?free=true&query={criteria['make']}+{criteria['model']}&searchCriteria=%7B%22query%22%3A%5B%22{criteria['make']}+{criteria['model']}%22%5D%7D"
copart_data = await mantis.scrape(
url=copart_url,
extract={
"lots": [{
"lot_number": "string",
"year": "number",
"make": "string",
"model": "string",
"vin": "string",
"mileage": "number",
"current_bid": "number",
"buy_now_price": "number or null",
"damage_type": "string or null",
"title_status": "string",
"sale_date": "string or null",
"location": "string"
}]
}
)
for lot in copart_data.get("lots", []):
result = AuctionResult(
vin=lot.get("vin"),
year=lot.get("year", 0),
make=lot.get("make", criteria["make"]),
model=lot.get("model", criteria["model"]),
mileage=lot.get("mileage", 0),
sale_price=lot.get("current_bid", 0),
buy_now_price=lot.get("buy_now_price"),
auction_house="copart",
auction_location=lot.get("location"),
sale_date=datetime.now(),
damage_type=lot.get("damage_type"),
title_status=lot.get("title_status"),
source_url=copart_url
)
results.append(result)
return results
def calculate_wholesale_retail_spread(
auction_results: List[AuctionResult],
market_values: List[MarketValue]
) -> dict:
"""
Calculate the wholesale-to-retail spread for sourcing decisions.
Higher spread = more profitable inventory acquisition.
"""
spreads = {}
value_lookup = {
f"{mv.year}-{mv.make}-{mv.model}": mv.avg_market_price
for mv in market_values if mv.avg_market_price
}
for result in auction_results:
key = f"{result.year}-{result.make}-{result.model}"
retail_value = value_lookup.get(key)
if not retail_value or result.sale_price <= 0:
continue
spread = retail_value - result.sale_price
spread_pct = (spread / retail_value) * 100
# Estimate profit after reconditioning
recon_cost_estimate = 1500 # average reconditioning
net_profit = spread - recon_cost_estimate
spreads[key] = {
"wholesale_price": result.sale_price,
"retail_value": retail_value,
"gross_spread": round(spread, 0),
"spread_pct": round(spread_pct, 1),
"est_recon_cost": recon_cost_estimate,
"est_net_profit": round(net_profit, 0),
"title_status": result.title_status,
"worth_sourcing": net_profit > 2000 and result.title_status == "clean"
}
return spreads
Step 6: AI-Powered Market Analysis & Alerts
from openai import OpenAI
openai_client = OpenAI()
async def generate_automotive_intelligence(
listings: List[VehicleListing],
market_values: List[MarketValue],
auction_results: List[AuctionResult],
reviews: dict,
recall_alerts: List[dict] = None
) -> dict:
"""
Generate a comprehensive automotive market intelligence briefing.
"""
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=[{
"role": "system",
"content": """You are an automotive market analyst. Produce an actionable
intelligence briefing covering:
1. INVENTORY INTELLIGENCE
- New listings of interest (underpriced, rare trims, low mileage)
- Aging inventory at competitor dealers (negotiation opportunities)
- Vehicles removed (likely sold โ validates demand)
2. PRICING TRENDS
- Models appreciating or depreciating faster than average
- Price positioning vs. market (are we competitive?)
- Recommended price adjustments for our inventory
3. WHOLESALE OPPORTUNITIES
- Best auction deals this week (highest spread potential)
- Models to avoid (high recon risk, slow sellers)
- Emerging wholesale trends
4. MARKET SIGNALS
- EV vs ICE demand shifts
- Seasonal patterns approaching
- OEM incentive changes affecting used values
5. REPUTATION SNAPSHOT
- Review trend direction
- New negative reviews requiring response
- Competitive reputation comparison
6. RECALL ALERTS
- New NHTSA recalls affecting inventory or customer vehicles
- Service revenue opportunities from recalls
7. TOP 5 ACTIONS THIS WEEK
- Specific, prioritized, with expected impact
Data-driven analysis. Include dollar amounts and percentages."""
}, {
"role": "user",
"content": f"""Inventory: {len(listings)} vehicles tracked
Market values: {len(market_values)} models valued
Auction results: {len(auction_results)} lots monitored
Reviews: {json.dumps({k: len(v.get('google', []) + v.get('dealerrater', [])) for k, v in reviews.items()})}
Recalls: {json.dumps(recall_alerts or [])}
Sample listings: {json.dumps([l.model_dump() for l in listings[:20]], default=str)}
Sample values: {json.dumps([v.model_dump() for v in market_values[:10]], default=str)}
Sample auctions: {json.dumps([a.model_dump() for a in auction_results[:10]], default=str)}"""
}],
temperature=0.2
)
return {
"briefing": response.choices[0].message.content,
"generated_at": datetime.now().isoformat()
}
async def deliver_automotive_alerts(
opportunities: dict,
reviews: dict,
slack_webhook: str
):
"""Route automotive alerts by urgency and type."""
import httpx
msg_parts = []
# Underpriced vehicles โ buy opportunities
if opportunities.get("underpriced"):
msg_parts.append("๐ *Underpriced Vehicles Detected*\n")
for opp in opportunities["underpriced"][:5]:
l = opp["listing"]
msg_parts.append(
f"โข {l.year} {l.make} {l.model} โ ${l.price:,.0f} "
f"({opp['diff_pct']}% below market) at {l.dealer_name}\n"
f" Potential savings: ${abs(opp['potential_savings']):,.0f} | {l.source_url}\n"
)
# Stale inventory โ negotiation targets
if opportunities.get("stale"):
msg_parts.append("\nโฐ *Stale Inventory (60+ days)*\n")
for item in opportunities["stale"][:5]:
msg_parts.append(
f"โข {item.year} {item.make} {item.model} โ "
f"{item.days_on_market} days at {item.dealer_name}\n"
)
if msg_parts:
await httpx.AsyncClient().post(slack_webhook, json={
"text": "".join(msg_parts),
"unfurl_links": False
})
Advanced: NHTSA Recall Monitoring
async def monitor_nhtsa_recalls(
makes_models: List[dict], # [{"make": "Toyota", "model": "RAV4", "years": [2022, 2023, 2024]}]
) -> List[dict]:
"""
Monitor NHTSA for new recalls affecting your inventory
or customer vehicles. Creates service revenue opportunities.
"""
recalls = []
for vehicle in makes_models:
for year in vehicle["years"]:
url = f"https://api.nhtsa.gov/recalls/recallsByVehicle?make={vehicle['make']}&model={vehicle['model']}&modelYear={year}"
result = await mantis.scrape(
url=url,
extract={
"recalls": [{
"nhtsa_campaign_number": "string",
"component": "string",
"summary": "string",
"consequence": "string",
"remedy": "string",
"report_date": "string",
"units_affected": "number or null"
}]
}
)
for recall in result.get("recalls", []):
recalls.append({
"year": year,
"make": vehicle["make"],
"model": vehicle["model"],
**recall,
"affects_inventory": True,
"service_opportunity": True
})
return recalls
Cost Comparison: AI Agents vs. Automotive Data Platforms
| Platform | Monthly Cost | Best For |
|---|---|---|
| vAuto (Cox Automotive) | $2,000โ$8,000 | Inventory management, market pricing, appraisal tools |
| DealerSocket | $1,000โ$5,000 | CRM, inventory management, digital marketing |
| CarGurus Dealer Tools | $500โ$3,000 | Listing optimization, IMV pricing, lead generation |
| MarketCheck API | $1,000โ$10,000 | Automotive data API for developers |
| Black Book / J.D. Power | $500โ$5,000 | Wholesale values, residual forecasting |
| Manheim Market Report | $200โ$1,000 | Wholesale auction analytics |
| AI Agent + Mantis | $29โ$299 | Custom inventory tracking, pricing, reviews, auctions โ fully tailored |
Honest caveat: Platforms like vAuto and DealerSocket offer direct integrations with DMS systems (CDK, Reynolds & Reynolds), OEM incentive feeds, and proprietary transaction data from millions of wholesale and retail sales. Their value is strongest for large dealer groups managing 500+ vehicles across multiple rooftops. For independent dealers, automotive startups, and lenders wanting custom market intelligence for specific segments, an AI agent approach delivers 80โ90% of the pricing intelligence at 5โ10% of the cost โ especially for competitive monitoring and review tracking that enterprise tools often handle poorly.
Use Cases by Automotive Segment
1. Franchise Dealerships โ Competitive Pricing & Market Share
Monitor competing franchise and independent dealers within your PMA (Primary Market Area). Track their inventory mix, pricing relative to market, days-on-market, and promotional activity. Identify when competitors receive new allocations of high-demand models. Use review monitoring to benchmark your CSI scores against local competition. Essential for GMs and used car managers making daily pricing decisions on 200โ500 vehicle inventories.
2. Independent Used Car Dealers โ Sourcing & Pricing
Find underpriced vehicles across AutoTrader, Cars.com, Facebook Marketplace, and Craigslist before other buyers spot them. Track wholesale-to-retail spreads at Manheim and Copart to identify profitable sourcing opportunities. Monitor aging inventory to know when to price-drop vs. wholesale out. For independent dealers working on tighter margins, automated market intelligence is the difference between a $2,000 front-end gross and breaking even.
3. Auto Lenders & Insurance Companies
Track real-time market values for collateral valuation, residual forecasting, and claims processing. Monitor depreciation curves by make, model, and trim to adjust lending terms. Detect market shifts (e.g., used truck prices dropping after new model launches) that affect portfolio risk. Build automated valuation models (AVMs) using scraped listing data as training inputs.
4. Automotive Startups & Tech Companies
Product companies building car-buying apps, valuation tools, or dealer analytics platforms need massive datasets of vehicle listings, pricing, and reviews. Instead of licensing from MarketCheck or DataOne at $10K+/mo, build custom data pipelines tailored to your specific product needs. Power recommendation engines, price prediction models, and market trend visualizations.
Compliance & Best Practices
- Vehicle listings are public data โ prices and inventory published on dealer websites and marketplaces are public by nature
- Platform ToS vary โ AutoTrader, Cars.com, and CarGurus have terms about automated access; use rate limiting and respect robots.txt
- VIN data โ VINs are public identifiers printed on every vehicle; decoding VINs is standard industry practice
- NHTSA data is public โ recall and complaint data is freely available via NHTSA's API
- Review scraping โ publicly posted reviews on Google, DealerRater, and Cars.com are generally scrapeable; don't collect reviewer PII
- Auction data โ Manheim requires dealer licensing for full access; Copart and IAAI public listings are accessible
- Rate limiting is essential โ space requests to avoid IP blocks; Mantis handles this automatically
Getting Started
- Define your market โ which makes, models, and geographic areas matter most to your business?
- Set up Mantis API access โ sign up for a free API key (100 calls/month free)
- Start with competitor inventory โ scrape the 5โ10 dealers in your PMA to understand the competitive landscape
- Add market values โ cross-reference KBB, Edmunds, and CarGurus values to calibrate your pricing
- Monitor reviews weekly โ track Google and DealerRater reviews for your dealership and top 3 competitors
- Automate alerts โ route underpriced vehicles, stale inventory, and negative reviews to Slack for immediate action