Valuation & ownership · Total Cost of Ownership

Total Cost of Ownership API

One GET returns what a vehicle costs to own over a holding period, split into depreciation, fuel, maintenance, repairs, insurance, purchase tax and fees, and financing interest. The number that matters most is not the total: it is measuredShareOfTotal, which tells you how much of that total rests on data we actually hold versus a national class-level assumption. Depreciation is ours and measured; fuel is EPA's; maintenance, repairs and insurance are planning averages you can and should replace with your own figures. Components we cannot compute are excluded from the total rather than counted as zero, and named so you know the total is a floor.

GET/v1/vehicles/total-cost-ownership15 s route budget

Get an API keyFull parameter reference

What comes back

A components object with seven entries — depreciation, fuel, maintenance, repairs, insurance, taxesAndFees and financingInterest — each carrying an amount and a basis, plus perMile or perYear on the rate-driven ones. Around it: total (the sum of the components we could compute), costPerMile, costPerYear, measuredShareOfTotal, missingComponents, vehicleClass (the reference class we inferred), purchasePrice with purchasePriceBasis telling you whether you supplied it or we fell back to the model year's median asking price, vehicleAgeYears, depreciationConfidenceGrade inherited from the depreciation curve, and the usual meta envelope whose coverage notes restate the assumption split in prose. coverageStatus is always "partial" and never "complete", because maintenance, repairs and insurance are always assumptions no matter what else resolves.

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/total-cost-ownership \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d year=2023 -d make=Honda -d model=Accord -d years=5 -d annual_miles=12000 -d state=TX
200 · five-year ownership estimate for a 2023 Honda Accord
{
  "components": {
    "depreciation": { "amount": 12040.0, "basis": "measured" },
    "financingInterest": { "amount": null, "basis": "unavailable" },
    "fuel": { "amount": 7000.0, "basis": "published" },
    "insurance": { "amount": 7750.0, "basis": "reference", "perYear": 1550.0 },
    "maintenance": { "amount": 5700.0, "basis": "reference", "perMile": 0.095 },
    "repairs": { "amount": 2100.0, "basis": "reference", "perMile": 0.035 },
    "taxesAndFees": { "amount": 2600.0, "basis": "reference" }
  },
  "costPerMile": 0.619,
  "costPerYear": 7438.0,
  "measuredShareOfTotal": 0.513,
  "missingComponents": ["financingInterest"],
  "source": "carscrape",
  "total": 37190.0,
  "vehicleClass": "medium_sedan",
  "years": 5
}

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

What it is actually built on

This is a composite, and it is explicit about which half of it we actually measured. Depreciation comes from our own dealer-asking-price retention curves: we take the ratio between the retention at the car's current age and its age at the end of your holding period, which gives the share of today's value it keeps, and multiply the loss by the purchase price. Fuel comes from two live EPA calls — one to resolve the trim configuration, one to fetch the figures — and rather than trusting EPA's annual cost directly we back out the fuel price it implies (annual cost × combined MPG ÷ 15,000 miles) so the figure can be rescaled to your actual annual mileage instead of EPA's assumption. Purchase tax and fees run through the same state table the tax endpoint uses. Financing interest, when you supply an apr, comes from the same amortization the loan endpoint uses. Maintenance, repairs and insurance are looked up from a class table keyed by a vehicle class we infer from body style and fuel, with electrification taking priority over body shape because it moves upkeep more. Every one of those paths tags its output with a basis, and the response reports what fraction of the total is not an assumption.

Data source
A composite. Depreciation comes from our own dealer-asking-price curves; fuel from the EPA combined rating with the fuel price implied by the EPA annual cost; taxes and fees from the state rate table; financing from the loan calculator. Maintenance, repairs and insurance are class-level planning assumptions, not observations of the vehicle.
Route budget
15 s
Billed per
model estimate, successful responses only
MCP tool
get_total_cost_ownership

Limits: what this endpoint will not do

The honest framing is the product here: a five-year total is only as good as its weakest component, and this endpoint tells you which one that is. Depreciation is ours and is measured, but it inherits the depreciation endpoint's scope — model-level, dealer asking prices, 223 make/model pairs across model years 2013–2026, so anything outside that set has no curve and you must pass purchase_price. Fuel is EPA's combined rating scaled to your annual mileage, which means real-world economy and local fuel prices will differ. Maintenance, repairs and insurance are national class-level planning assumptions and nothing more — we hold no per-vehicle maintenance or claims data, and inventing per-VIN precision there would be exactly the overstatement the rest of this API was cleaned up to avoid. Insurance in particular is driven far more by driver history, age, coverage limits and ZIP than by vehicle class, so treat the default as a placeholder and pass your own the moment you have a real quote. Taxes and fees are state-level only and carry the same caveats as the tax endpoint. Financing interest appears only when you pass an apr. Anything that cannot be computed is null, listed in missingComponents, and excluded from the total rather than counted as zero, so the total understates rather than guesses — always read measuredShareOfTotal before presenting a figure as a number rather than an estimate. The endpoint is available now and successful responses use the published catalog rate. Standard plans include no SLA.

What teams build with it

Cost-to-own comparison in a car shopping tool

A shopping site comparing two cars on sticker price is comparing the wrong number. Call this for each candidate with the same years and annual_miles and show the components side by side — depreciation is usually the largest line and the one buyers ignore. Because the response labels assumptions, you can render the measured components confidently and mark the rest as "estimated", which is both more honest and more persuasive than a single unexplained total.

Fleet procurement scoring with in-house upkeep data

A fleet or corporate mobility team scoring candidate vehicles gets a consistent basis across every nameplate on the shortlist, and can replace the reference maintenance and insurance rates with its own historical per-mile figures. Once those are supplied, measuredShareOfTotal rises and the output stops being a planning average and starts being a projection grounded in the fleet's own experience.

Payment and residual context in lending and leasing

A lender or lease originator can quote a payment and, in the same view, the true cost of holding the vehicle for the term — including the depreciation that determines residual risk. Passing apr, down_payment and loan_months makes financing interest a computed component that reconciles exactly with the loan endpoint, so the two surfaces never disagree in front of a customer.

Grounded cost answers in an AI car-buying assistant

An AI assistant answering "should I buy this?" needs to distinguish what it knows from what it assumes, or it will state planning averages as facts. The MCP tool exposes basis and measuredShareOfTotal for exactly that reason: the agent can say which parts are measured, which are estimated, and ask the user for their real insurance premium to sharpen the answer.

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(["ownership_upstream_unavailable"]);

export async function getTotalCostOwnership(params: Record<string, string>, attempt = 0): Promise<GetTotalCostOwnershipResponse> {
  const response = await fetch(
    `${API}/v1/vehicles/total-cost-ownership?${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 GetTotalCostOwnershipResponse;

  // 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 getTotalCostOwnership(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 one code is marked retryable here — ownership_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 model estimate. 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.

Total Cost of Ownership API pricing by plan
PlanPlatform feeRate limitPer model estimate50,000 / month
Starter$0 / mo5 rps$0.06$3,000$3,000 usage + platform fee
Pro$299 / mo10 rps$0.045$2,549$2,250 usage + platform fee
Scale$599 / mo50 rps$0.035$2,349$1,750 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

How much of the total is real data and how much is assumption?

Read components[].basis. Depreciation is "measured" because it comes from our own curves. Fuel is "published" because it is EPA's. Financing interest is "computed" arithmetic. Maintenance, repairs and insurance are "reference" — national class-level planning assumptions, not observations of the car in front of you. measuredShareOfTotal rolls that up into one number: the fraction of the total that is not an assumption. On a typical gasoline sedan with no financing, roughly half the total is measured or published and the rest is reference, which is the honest state of the art without a maintenance-claims dataset.

What happens when a component cannot be computed?

It is excluded from the total and named in missingComponents, with its basis set to "unavailable". It is never silently treated as zero, because a missing component and a genuinely zero one are different claims and only the second belongs in a sum. The practical consequence is that a total with missing components is a floor, not an estimate of the true figure — surface the missing list rather than hiding it, or the user reads a smaller number as a better deal.

Can I replace the assumptions with my own figures?

Yes, and you should whenever you have real numbers. maintenance_per_mile and repair_per_mile take USD per mile; insurance_per_year takes an annual premium. Anything you supply flips that component's basis to "caller_supplied" and lifts measuredShareOfTotal accordingly. If your product already collects an insurance quote or has fleet maintenance history, passing it turns this from a planning estimate into something much closer to a real projection.

Is financing included?

Only when you pass an apr. Without it, financingInterest is null and appears in missingComponents rather than being assumed to be zero — because "paid cash" and "we do not know the rate" are different situations. Pass apr, down_payment and loan_months together and the interest is computed from the same amortization the loan endpoint uses, so the two agree.

What does it cost?

Total Cost of Ownership shares Starter's 1,000-call monthly allowance, then costs $0.06 per successful call from available credit. Pro is $0.045 and Scale is $0.035. It is priced alongside the fuel-only Ownership Costs endpoint it supersedes, and like that one it makes live EPA round trips, so cache on your side keyed by year/make/model plus your holding assumptions. Failed calls are not billed.

Where to go next

Most integrations chain two or three of these. The ones that pair with total cost of ownership api most often:

The reference entry for this endpoint — Total Cost of Ownership 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 get_total_cost_ownership 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.