One GET against our normalized US car-listings store answers what is actually for sale, and what sellers are asking — filtered by make, model, year range, price range, mileage ceiling and state, with the total match count returned alongside the page. It is built for pricing teams, marketplaces and analytics products that need live asking-price comparables rather than a book value, and it is one of the eleven endpoints the free Starter plan's included monthly calls cover.
A 200 returns a small envelope — count, limit, offset, total and source "carscrape" — wrapping a results array of listing rows. total is the full match count ignoring pagination, which is the field most integrations actually care about: it tells you how thin or deep the comp set is before you show a number to a user. Each row is passed through from the store with upstream snake_case keys, including vin, year, make, model, trim, body_style, fuel, drivetrain, transmission, color, price, miles, condition, title_status, seller_type, dealer_name, city, state, zip, lat, lng, segment, is_active, days_on_market, data_quality, vdp_url, source and vin_valid. A real row looks like a 2026 Ford F-150 Platinum at $77,101 with 9 miles at a dealer in Watertown, CT, days_on_market 3, data_quality 1.0, source "autolist", and a vdp_url deep-link back to the marketplace page. Note that is_active and vin_valid are raw 0/1 integers, not booleans, and several row fields are legitimately null. Paginate with limit (1–500, default 50) and offset against total, or follow nextCursor for a stable walk; sort on price, miles, year, days_on_market, last_seen_at or first_seen_at. Rows are typed in the OpenAPI contract in camelCase — listingId, dealerId, carfaxUrl, accidentCount, ownerCount, oneOwner, usageType, primaryImage, photoCount, firstSeenAt, lastSeenAt, sourceCreatedAt and soldObservedAt included — so generated SDKs describe the detail the store holds. Pass facets=true for value counts over the same filtered set.
The data is our own normalized US car-listings store, scraped from autolist, truecar, iseecars and dealer JSON-LD sites, then joined against a canonical vehicles table so that make, model, body style, fuel, drivetrain and transmission are consistent across sources rather than reflecting each site's own naming. The store currently holds 1,721,693 listings covering 1,719,030 distinct vehicles, with 4,686,766 price observations recorded across crawl passes (those observations also feed valuation training and marketplace analysis). Every row carries a data_quality score — the share of expected fields that parsed successfully — so you can filter out sloppy rows instead of trusting everything equally, and a source marker so you always know which scrape produced it. The route is a thin, validated pass-through to that store with a 5-second upstream budget; unknown query parameters are rejected rather than ignored, so a typo fails loudly with a 400 instead of silently widening your search.
Data source
Our normalized US car-listings store, scraped from autolist, truecar, iseecars and dealer JSON-LD sites and joined against the canonical vehicles table.
Route budget
5 s
Billed per
successful API call, successful responses only
MCP tool
search_listings
Limits: what this endpoint will not do
United States only. The route always applies the mainstream, priced segment: auction sources (bringatrailer, carsandbids) and call-for-price rows are never returned. The direct consequence is that sold=true currently matches nothing — realized sale prices only exist in the auction segment we exclude, so this endpoint tells you what sellers are asking, never what a car actually sold for. ZIP search is unavailable and zip is null on nearly all rows because our sources do not expose it; geo filtering is by state, or by near_lat/near_lon plus radius_mi, which is a bounding-box approximation rather than a true great-circle radius. title_status is null for nearly all rows for the same reason, and miles is null on some. days_on_market counts days since we first observed the listing, not the dealer's own listing age, so it under-reports for anything that was live before we started watching it. make and model are exact and case-sensitive against our canonical naming — "F-150", not "f150" — while state is upper-cased server-side, so "ct" and "CT" behave identically. An empty result is not a 404: it returns 200 with total 0, count 0, results [], and it is a billable successful call. Pagination is limit/offset for a single page, or keyset cursors via nextCursor for a stable walk of the whole set; there are no Link headers. Geo filtering now accepts near_lat/near_lon with radius_mi as a bounding-box approximation, alongside the state filter. The endpoint is available now and successful responses use the published catalog rate. Standard plans include no SLA.
What teams build with it
Competitive repricing for a dealer group or dealer SaaS
For each unit on the lot, query make, model, year_min/year_max and state, sorted by price ascending, and read total plus the first page. You get the live asking-price distribution for that vehicle in that state and the days_on_market of everything sitting above and below you. Re-run the same cohort on a schedule to detect movement in the comp set, and use Vehicle Valuation for a modelled number to anchor against.
Bootstrapping search on a car-shopping marketplace
The row carries everything a results page needs — price, miles, trim, color, city/state, dealer name and a vdp_url to hand off the click — so a new marketplace can ship browse and search before signing a single dealer feed. Pair with Car Images for the hero photo. Filter source to a specific scrape source, or min_quality to only surface rows where most expected fields parsed cleanly.
Collateral comps for a lender, insurer or instant-offer product
Before quoting on a used vehicle, pull the current listings for the exact year/make/model in the applicant's state with a mileage_max near the odometer reading. total gives you a defensible liquidity signal (how many comparable units are on the market right now) and the price spread gives you a floor and a ceiling. Use total 0 as an explicit thin-market flag rather than silently falling back to a national average.
Supply and pricing analytics for a research dashboard
Poll fixed filter combinations on a schedule and store total and the price percentiles over time. Because days_on_market counts days since we first observed the listing, a rising median days-on-market for a given model is a real inventory-aging signal within our observation window. active=false isolates listings that have disappeared since the last crawl.
Integrating it properly
The reference ships the bare curl. This is the shape worth deploying: a typed client that branches on the RFC 9457 code slug rather than the human-readable title, and retries only the codes this endpoint marks retryable.
TypeScript
const API = "https://api.vehicles.dev";
// Only these codes are marked retryable for this endpoint. Everything else is terminal.
const RETRYABLE = new Set(["listings_upstream_unavailable"]);
export async function searchListings(params: Record<string, string>, attempt = 0): Promise<SearchListingsResponse> {
const response = await fetch(
`${API}/v1/vehicles/listings?${new URLSearchParams(params).toString()}`,
{
headers: {
Accept: "application/json",
Authorization: `Bearer ${process.env.VEHICLES_API_KEY ?? ""}`
},
// The route budget is 5 s; allow it plus headroom.
signal: AbortSignal.timeout(10_000)
}
);
if (response.ok) return (await response.json()) as SearchListingsResponse;
// Failures are application/problem+json. Branch on `code`, never on the human-readable title.
const problem = (await response.json()) as { code: string; detail?: string };
if (RETRYABLE.has(problem.code) && attempt < 3) {
await new Promise((resolve) => setTimeout(resolve, 250 * 2 ** attempt));
return searchListings(params, attempt + 1);
}
throw new Error(`${response.status} ${problem.code}: ${problem.detail ?? ""}`);
}
How to check it is behaving
The route enforces a 5-second upstream budget, so a slow dependency returns a problem document instead of holding your request open. Exactly one code is marked retryable here — listings_upstream_unavailable — and everything else documented for this endpoint is terminal, so backoff on anything else just burns your rate limit. Billing is success-only: failed calls release their reservation, so you can measure error rates and hold-out accuracy against your own data without paying for the failures. Usage is visible per endpoint in the dashboard.
What this one endpoint costs
Priced per successful API call. The platform fee buys throughput and endpoint access; the unit price covers the data work. Worked at 50,000 successful calls a month so you can see where the plans cross over — the full price matrix is in the reference.
Car Listings API pricing by plan
Plan
Platform fee
Rate limit
Per successful API call
50,000 / month
Starter
$0 / mo
5 rps
$0.002
$100$100 usage + platform fee
Pro
$299 / mo
10 rps
$0.0015
$374$75 usage + platform fee
Scale
$599 / mo
50 rps
$0.001
$649$50 usage + platform fee
Eligible for the Starter plan's included monthly calls; past those it draws on your credit balance. Data fees are prepaid from credits on every plan: when included calls and credits cannot cover a call you get 402 insufficient_credits rather than an overage bill. Published rates are active and apply only to successful responses. Standard plans include no SLA.
Questions engineers actually ask
Is a search that matches nothing still billed?
Yes. A search with no matches returns HTTP 200 with total 0, count 0 and an empty results array. That is a successful call and it is metered. Billing is success-only, so 4xx and 5xx responses release the reservation and cost nothing — but a well-formed search over a segment we do not cover is a success from the API's point of view. If you are fanning out speculative queries, cap them client-side.
Why does sold=true return zero results?
Because the only realized sale prices in our dataset come from auction sources, and the route always restricts to the mainstream priced segment, which excludes those sources. The parameter exists and validates, but with the current segment filter it cannot match anything. Treat this endpoint as asking-price data. If you need transaction prices, this API does not have them today and we would rather say so than let you discover it in production.
Does casing matter in make and model?
For make and model, yes — they are matched exactly and case-sensitively against our canonical naming, so "Ford"/"F-150" work and "ford"/"f150" return nothing. state is the exception: it is upper-cased server-side. The safest pattern is to run a VIN through the VIN Decoder API first and use the canonical make and model it returns as your listings filter values.
What does it cost, and is it usable on the free plan?
Yes, it is one of the endpoints eligible for Starter's 1,000 included monthly calls, then $0.002 per successful call (your one-time $10 signup credit buys about 5,000 more). Pro is $0.0015 per call, Scale $0.001. Worked example: 50,000 searches on Pro is $75 of usage on top of the $299 platform fee. The per-call gap between Pro and Scale is small enough that Scale only wins on volume alone at roughly 600,000 calls a month — pick Scale for the 50 rps limit rather than for listings savings at lower volume.
Can I search by ZIP code or within a radius of a point?
No. Location filtering is a two-character US state code. Rows do carry lat and lng, and city, so you can do radius filtering yourself on a returned page — but you cannot push a radius into the query, and pulling a whole state to filter it client-side gets expensive fast at 500 rows per page.
Can I republish the listing rows or resell them as a dataset?
Standard plans cover use inside your own product, not redistribution of raw records or resale as a standalone dataset. Bulk delivery and redistribution rights require a separate agreement — email us before you build on the assumption. The vdp_url deep link back to the source marketplace is there deliberately; attributing and linking through is the pattern we expect.
Where to go next
Most integrations chain two or three of these. The ones that pair with car listings api most often:
Car Images APIThe photo gallery, advertised photo count and source listing link for a VIN — with an explicit flag for whether the gallery is complete. The lowest-priced data endpoint.
Vehicle Valuation APIAn ML-predicted current asking price in whole USD for a year/make/model, with the model's 3.7% median holdout error reported on every response.
VIN Decoder APINormalized year, make, model, trim, body and powertrain from a VIN. Verified-store-first, with a live NHTSA vPIC fallback for everything else.