Valuation & ownership · Price Check & Deal Rating

Car Price Check API

Turn a seller's asking price into an explainable deal assessment with one GET. Price Check returns the modelled market value, exact dollar and percentage gap, a deterministic great/good/fair/high/overpriced label, a prediction interval, the surrounding year/make/model inventory, and — when the VIN maps to a captured offer — its price-drop timeline. It is for products that need to answer more than “what is this car worth?”: shopping assistants, dealer acquisition tools, listing-quality systems and lender review screens that must say whether this particular ask is low, typical or high and show the evidence behind that judgment.

GET/v1/vehicles/price-check15 s route budget

Get an API keyFull parameter reference

What comes back

The response centers on four price fields. askingPriceUsd echoes the caller's whole-dollar ask. marketValueUsd is the rounded model estimate. deltaUsd subtracts market value from the ask, so a negative number is below the estimate and a positive number is above it. deltaPct expresses the same relationship as a fraction rounded to four decimals; −0.0418 means 4.18% below. dealRating converts that fraction into one of five stable strings: great_price, good_price, fair_price, high_price or overpriced. interval holds lowerUsd, upperUsd and confidenceLevel; any of those diagnostics can be null when the loaded model does not provide them. marketContext is a second evidence block with segmentSize, activeListings, medianAskingPriceUsd and medianDaysOnMarket for the same make/model/year. It is deliberately separate from the model estimate so your client does not confuse a broad raw median with a feature-aware prediction. priceHistory is either null or an object containing originalPriceUsd, currentPriceUsd, totalDropUsd, priceChanges, priceDrops, daysOnMarket, firstSeenAt and lastSeenAt for the resolved Autolist offer. Identity echoes are vin (nullable), year, make and model. meta records coverage status, notes, data-through and retrieval timestamps, dataset and model versions, and the pricing-model source; source is the constant backing-service marker "carscrape". The documented 2021 Toyota Camry example asks $27,000 against a $28,177 model value, a −$1,177 / −4.18% gap labelled good_price, with 1,035 active same-year listings, a $21,955 segment median and nine median days on market.

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/price-check \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d asking_price=27000 -d make=Toyota -d model=Camry \
  -d year=2021 -d miles=45000 -d state=TX
200 · 2021 Toyota Camry at a $27,000 asking price
{
  "askingPriceUsd": 27000,
  "dealRating": "good_price",
  "deltaPct": -0.0418,
  "deltaUsd": -1177,
  "interval": {
    "confidenceLevel": 0.9,
    "lowerUsd": 21928,
    "upperUsd": 36207
  },
  "make": "Toyota",
  "marketContext": {
    "activeListings": 1035,
    "medianAskingPriceUsd": 21955,
    "medianDaysOnMarket": 9,
    "segmentSize": 1035
  },
  "marketValueUsd": 28177,
  "meta": {
    "confidence": null,
    "coverage": {
      "notes": ["Model metadata status: current."],
      "status": "partial"
    },
    "dataThrough": "2026-08-18T19:55:58+00:00",
    "datasetVersion": "3",
    "modelVersion": "hgb-logprice-v3",
    "observedAt": "2026-08-18T19:55:58+00:00",
    "retrievedAt": "2026-08-22T18:20:53Z",
    "source": "vehicles.dev pricing model"
  },
  "model": "Camry",
  "priceHistory": null,
  "source": "carscrape",
  "vin": null,
  "year": 2021
}

The complete parameter table, the response-field dictionary and the per-endpoint error codes live in the reference: full parameter reference for /v1/vehicles/price-check.

What it is actually built on

The route first resolves vehicle identity. With make, model and year, it uses those attributes directly. With a VIN, it normalizes the VIN, looks for identity in the vehicles store and falls back to a decode when necessary; a stored Autolist offer can also supply mileage when the caller omitted it. The resolved feature vector goes through the same gradient-boosted asking-price model used by Market Value, trained on 1,858,059 dealer listings and checked against its published coverage metadata. Vehicles outside the trained categorical or numeric domain return a non-retryable 422 instead of an extrapolated label. For an in-domain vehicle the service rounds the estimate, computes askingPriceUsd − marketValueUsd and divides that difference by marketValueUsd to produce deltaPct, then applies the fixed five-band ladder. Separately, it queries the normalized listing store for the same make, model and year to calculate segment size, active supply, median asking price and median positive days on market. When a stored Autolist listing was resolved by VIN, it reads that listing's chronological observation series to calculate original/current price, total drop and change counts. The public API maps the backing snake_case payload into camelCase, rounds interval bounds to whole dollars, adds the shared provenance meta envelope, and enforces a 15-second upstream budget plus account-level authentication, rate limiting and success-only billing.

Data source
The vehicles.dev asking-price model plus the normalized US dealer-listings store. A VIN request can also read that Autolist offer's captured observation series for price history.
Route budget
15 s
Billed per
assessment, successful responses only
MCP tool
check_vehicle_price

Limits: what this endpoint will not do

Price Check compares one caller-supplied asking price with an ML estimate of dealer-market asking value. It does not observe the negotiated transaction, inspect the vehicle, certify its condition, or calculate a trade-in, wholesale, auction, private-party, tax-inclusive or out-the-door price. Treat the rating as market context for a decision, not as a promise that a car will sell for the estimate. Identity has a deliberate either/or contract: send vin, or send make, model and year together. A partial attribute identity returns 422 price_check_underspecified. A VIN that cannot be resolved returns 404 price_check_not_resolvable; malformed or out-of-domain input returns 422 valuation_out_of_domain. The public schema accepts 11–17 VIN characters, but the backing resolver requires a valid 17-character VIN, so production clients should validate the full VIN before calling. Mileage, trim, state, drivetrain, fuel, transmission, body style and condition sharpen the estimate when known. Model casing still matters to the pricing model, and state should be uppercase. The five labels use fixed percentage bands around modelled market value, not around the segment median: at least 10% below is great_price; more than 3% below is good_price; within 3% is fair_price; up to 10% above is high_price; above 10% is overpriced. Exactly −10%, −3%, +3% and +10% fall into the lower-priced band at that boundary. marketContext is a descriptive same-year/make/model slice, not a claim that every row is a comparable for this specific trim or mileage. priceHistory is narrower still: it is available only when a VIN resolves to a stored Autolist offer with observations, so null is normal for attribute requests, other listing sources and vehicles without captured history. Price Check sits outside Starter's 250 one-time included calls. A Starter key can call it at $0.015 per assessment when the workspace has prepaid credits and receives 402 insufficient_credits when its balance cannot cover the call. There is no automatic Starter overage invoice. The endpoint is live, success-only billed, and standard plans include no SLA.

What teams build with it

Buyer-side deal badges and shopping copilots

A consumer shopping assistant receives a vehicle detail page or user-entered VIN plus the advertised price. One call produces the plain-language label for the card, while deltaUsd and deltaPct support copy such as “$1,177 below our market estimate.” The interval keeps the interface honest: a point estimate can look precise, but showing the plausible range tells the buyer when the evidence is broad. If priceHistory exists, the same card can say how many reductions were observed and how long the offer has been visible. The assistant should keep safety and condition separate — a great_price result is an invitation to investigate, not proof of a clean vehicle.

Dealer acquisition triage and negotiation queues

Dealer acquisition and appraisal teams can compare an incoming seller ask with marketValueUsd before a human reviews photos, title and reconditioning needs. marketContext answers whether the nameplate has abundant active supply and how quickly similar model-year inventory moves, while the model estimate incorporates the submitted mileage, trim and geography. Teams can route overpriced leads to a negotiation queue, fair offers to a standard review, and unusually cheap offers to a risk queue. That last branch matters: cheap can indicate motivated supply, but it can also indicate damage, title problems or a fraudulent listing that this pricing endpoint cannot detect.

Seller pricing guidance and listing-quality controls

A marketplace can run Price Check when a seller chooses an asking price and again whenever that price changes. The first call gives immediate guidance without forcing the seller to interpret raw comparable rows; later calls let the product measure whether a reduction moved the listing into a better band. Keep the response fields rather than only the label: marketValueUsd and the interval support transparent guidance, activeListings and medianDaysOnMarket explain competitive pressure, and the fixed thresholds make analytics consistent across experiments. Attribute identity is enough for draft listings; switch to VIN after publication when you want captured offer history.

Lender reasonableness checks with auditable provenance

Auto lenders, warranty providers and fraud teams often review a stated purchase price before approving a workflow. Price Check provides a fast market-reasonableness signal: a materially high ask may need an invoice or collateral review, while a materially low ask may deserve identity, title or condition checks. Store the model and dataset versions from meta with the decision so an auditor can reproduce which pricing snapshot informed it. Do not treat the rating as an underwriting decision by itself. It measures advertised dealer-market value and contains no borrower, title, accident, repair, lien or realized-sale information; those checks belong in separate policy and data sources.

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(["valuation_model_unavailable", "price_check_upstream_unavailable"]);

export async function checkVehiclePrice(params: Record<string, string>, attempt = 0): Promise<CheckVehiclePriceResponse> {
  const response = await fetch(
    `${API}/v1/vehicles/price-check?${new URLSearchParams(params).toString()}`,
    {
      headers: {
        Accept: "application/json",
        Authorization: `Bearer ${process.env.VEHICLES_API_KEY ?? ""}`
      },
      // The route budget is 15 s; allow it plus headroom.
      signal: AbortSignal.timeout(20_000)
    }
  );

  if (response.ok) return (await response.json()) as CheckVehiclePriceResponse;

  // 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 checkVehiclePrice(params, attempt + 1);
  }
  throw new Error(`${response.status} ${problem.code}: ${problem.detail ?? ""}`);
}

How to check it is behaving

The route enforces a 15-second upstream budget, so a slow dependency returns a problem document instead of holding your request open. Exactly 2 codes are marked retryable here — valuation_model_unavailable and price_check_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 assessment. 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 Price Check API pricing by plan
PlanPlatform feeRate limitPer assessment50,000 / month
Starter$0 / mo5 rps$0.015$750$750 usage + platform fee
Pro$299 / mo10 rps$0.015$1,049$750 usage + platform fee
Scale$599 / mo50 rps$0.01$1,099$500 usage + platform fee

Outside the Starter plan's one-time included calls — it requires available prepaid credits at the published Starter rate. 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

How is Price Check different from the Market Value endpoint?

Market Value answers “what is this vehicle likely being advertised for?” Price Check answers the next product question: “given an asking price, how does this offer compare?” It calls the same pricing model, then calculates the dollar and percentage difference, assigns a five-band deal label, reads supply and days-on-market context for the matching year/make/model, and optionally attaches a listing's captured price changes. Use Market Value when you only need an estimate. Use Price Check when your UI, agent or review workflow starts with a seller's number and must explain it.

What exactly do the five deal ratings mean?

The label is computed from deltaPct = (asking price − modelled market value) / modelled market value. A result at or below −10% is great_price. Above −10% through −3% is good_price. Above −3% through +3% is fair_price. Above +3% through +10% is high_price. Anything above +10% is overpriced. The boundaries are intentionally deterministic, so the same asking price and market value cannot drift between labels. Remember that “great” describes the price relationship only; it says nothing about title, damage, mechanical condition, fraud or whether the vehicle physically exists.

Should I identify the car by VIN or by make, model and year?

Use a VIN when you want the service to resolve identity and try to attach that listing's captured history. The resolver first looks for year, make, model and trim in the vehicle store; it can fall back to a VIN decode when identity is absent. For a stored Autolist offer it can also use current mileage when you omitted miles and return original versus current price, total drop, change count, drop count, days on market and first/last seen timestamps. Use make + model + year when no VIN exists or when you only need an assessment for a hypothetical offer. In either mode asking_price remains required and is never silently replaced with the stored current price.

Why is priceHistory null even though I supplied a VIN?

A null priceHistory means the service did not find the specific history seam it supports: a stored Autolist listing for the resolved VIN with positive price observations. It does not mean the seller never changed the price, and it does not mean the vehicle has never been listed. Attribute-only requests always return null because there is no individual offer to follow. VINs associated only with another source can also return null. Build the UI so marketValueUsd, dealRating and marketContext remain useful on their own, then show history as optional evidence when present rather than treating null as a request failure.

Why can marketValueUsd differ from medianAskingPriceUsd?

They answer different questions. marketValueUsd is the feature-aware ML estimate for the requested vehicle, using year, mileage, trim, state and other supplied attributes. medianAskingPriceUsd is the middle stored asking price across the broader same-make/model/year segment, without asserting that every row matches the requested trim, mileage or condition. activeListings counts rows in that segment currently marked active; segmentSize includes the full matching slice read by the service; medianDaysOnMarket summarizes positive stored days-on-market values. Use the model estimate for the rating and the segment fields to explain supply, velocity and the surrounding inventory.

Can Starter call it, and what does production volume cost?

Price Check is success-only: validation failures, unresolved VINs, out-of-domain vehicles, rate-limit rejections and upstream failures release the billing reservation and cost nothing. It is not one of Starter's 250 included-call endpoints. A free Starter workspace can call it at $0.015 per successful assessment when it has prepaid credits. Pro costs $0.015 per assessment and Scale costs $0.01. At 50,000 successful checks, Pro data fees are $750 plus the $299 platform fee ($1,049 total); Scale data fees are $500 plus $599 ($1,099 total).

Where to go next

Most integrations chain two or three of these. The ones that pair with car price check api most often:

The reference entry for this endpoint — Price Check & Deal Rating in the API documentation — carries the parameter constraints, defaults, response fields and the full error table with retryable flags. Agents can reach the same endpoint as the check_vehicle_price tool on the vehicles-dev-mcp MCP server, and the machine-readable contract is published at https://api.vehicles.dev/openapi.json.

Browse the rest of the catalog on the Vehicle data APIs hub.