Plan Search Reference

Search and Hand Off uses this call, and Send a Lead runs the same search on our side. This page covers it in full: how to identify a vehicle, what you can put in a request, what comes back, and how to make it faster.

If you have not picked a path yet, start with Choose Your Integration.

Which endpoint to call

There are more search endpoints than you need. In practice:

Endpoint Use it when
POST /api/v2/Partners/PlansSearch Default choice. Accepts any vehicle identifier and works out which one you sent. One integration handles all three.
POST /api/v2/Partners/PlansSearch/Vin You only ever search by VIN and want the request validated strictly against that.
POST /api/v2/Partners/PlansSearch/MakeModelYear You only ever search by make, model, and year.
POST /api/v2/Partners/PlansSearch/LicensePlate You only ever search by plate and state.
POST /api/v2/Partners/PlansSearchWithUserInfo/Vin You have the customer's details and want a pre-filled checkout. VIN only. See Search and Hand Off.
POST /api/v2/Partners/PlansSearchAsync/* You need non-blocking searches or instant cached results, and Chaiz has enabled it for you. See make searches faster.

The generic and the specific endpoints return the same shape. The specific ones just reject a request that carries the wrong identifier, which can be useful as a guardrail.

The synchronous route is /PlansSearch/Vin and the asynchronous one is /PlansSearchAsync/VIN. Note the different capitalization. It is inconsistent, and we are keeping it that way rather than breaking existing integrations.

Identify the vehicle

Three ways, in descending order of accuracy.

Prefer VIN wherever you can get one. It prices the exact trim, and it removes a step from checkout later. Make/model/year works, but the customer will be asked for a VIN before the sale completes, and that is where you lose people.

Always required, whichever you use: the customer's stateShortName (two-letter US code) and the current mileage. Pricing depends on both, so neither is optional in practice.

If you send more than one identifier, precedence is VIN, then license plate plus state, then make/model/year. Send exactly one to avoid surprises.

Resolving loose make and model text

make and model must match our catalog. Free text a customer typed usually will not. Resolve it first:

curl "https://uat-api.chaiz.com/api/v2/Partners/Vehicles/Lookup?make=merc&model=f150&year=2019" \
  -H "Authorization: Bearer YOUR_API_KEY"

Returns up to five matches ordered by confidence, plus suggestions when the input is ambiguous. Responses are cached and come back in under 50 ms.

Confidence Meaning Example
HIGH Exact match, ignoring case ford resolves to FORD
MEDIUM Matched after normalizing punctuation f150 resolves to F-150
LOW Partial or contains match merc could be MERCURY or MERCEDES-BENZ

Treat LOW as "ask the customer to confirm" rather than as an answer. Check yearsAvailable on the match before searching a year we do not carry.

To build your own dropdowns instead, GET /api/v2/Partners/Vehicles/Makes and GET /api/v2/Partners/Vehicles/Makes/{makeId}/Models return the full catalog.

Request anatomy

{
  "searchCriteria": { "vin": "5XXG14J25MG015777", "stateShortName": "NY", "mileage": 42000 },
  "filters": { "duration": { "from": 24, "to": 60 }, "returnResults": 10 },
  "tracking": { "queryParams": "utm_source=partner&utm_campaign=your-campaign" },
  "responseDetailLevel": "Full",
  "resultOrder": "Recommended",
  "planVariant": "Recommended",
  "showProviderInfo": true
}

filters, tracking, responseDetailLevel, resultOrder, planVariant, and showProviderInfo are root-level fields, siblings of searchCriteria, not properties inside it. Nesting them inside searchCriteria is the single most common request mistake, and it fails silently: the fields are ignored and you get an unfiltered search.

Field Required Notes
searchCriteria Yes The vehicle, state, and mileage
filters No Narrow the results. See below.
tracking No queryParams and referrer, for attribution. See Search and Hand Off.
responseDetailLevel No Full (default) or Essential. See response shapes.
resultOrder No Recommended (default), Rating, or None
planVariant No Recommended (default), Highest, Longest, or All. See one row per plan.
showProviderInfo No Include provider detail in the results
partner No Leave it out. Resolved from your token, and a mismatch is rejected.
searchId, userId No For continuing an existing search session. Omit unless you have been told to use them.

Filters

All optional, all under filters.

Filter Type Notes
providers array of strings Only these providers, by name: "NAAC", "Omega", "CAPS", "ServiceContract"
excludeProviders array of strings Everything except these
numberOfProviders integer Cap on distinct providers in the results
coverageTypes array of strings See the vocabulary note below
coverageType enum Single value. Different vocabulary from coverageTypes. See below.
categoryTypes array of strings Only plans covering these components: Engine, TurboSuper, TransferCase, Steering, Transmission, DriveAxle, Suspension, AirCon, Brakes, Electricals, Cooling, Fuel, Hybrid, Body, Consumables, Exhaust, General, Interior, Services, Assist
planChaizRating range Chaiz quality rating, 0 to 10. { "from": 7, "to": 10 }
duration range, months { "from": 24, "to": 60 }
miles range, thousands { "from": 50, "to": 150 } is 50,000 to 150,000 miles. 999 means unlimited.
monthlyPrice range, USD { "from": 50, "to": 150 }
totalPrice range, USD { "from": 1000, "to": 5000 }
returnResults integer Maximum rows returned. It counts rows, so with planVariant: "All" one plan can use several of them. Default depends on your account.

Provider names are real catalog values, and which of them you can see depends on your account. Filtering to a provider you are not configured for returns nothing rather than an error.

The two coverage filters do not speak the same language

coverageTypes (the array) accepts either the friendly aliases "basic", "basicplus", "best" or the canonical names "Powertrain", "PowertrainPlus", "MostComprehensive".

coverageType (the singular) accepts only the canonical names. The aliases do not work there.

An unrecognized value in coverageTypes is silently dropped, not rejected, so a typo looks like a filter that had no effect. Prefer the canonical names everywhere and you avoid the whole problem.

Alias Canonical name Meaning
basic Powertrain Core powertrain components
basicplus PowertrainPlus Powertrain plus additional systems
best MostComprehensive Exclusionary, the broadest coverage

Results report coverage as the number: 1 Powertrain, 2 PowertrainPlus, 3 MostComprehensive. Both filters also accept those numbers.

Response shapes

responseDetailLevel controls how much comes back.

Full (default) Essential
Coverage breakdown, component scores, add-ons, provider detail, highlights Yes No
planContractUrl Yes No
Maintenance program Full detail, as maintenanceProgram Yes/no only, as hasMaintenanceProgram
Core fields: price, duration, deductible, rating, handoff URLs Yes Yes
Payload size Large Small

Use Full when you are rendering a comparison UI where customers want the detail. Use Essential when you only need price, name, rating, and a link, which keeps responses small and fast to parse.

One row per plan

A plan can be sold with several terms: different durations and mileage tiers. planVariant decides which of these variants you get back. The default is Recommended: one row for each planId.

planVariant What you get
Recommended (default) One row per plan. The shortest duration wins. If durations tie, the mileage closest to the baseline for that duration wins. If that ties, the lowest monthly price wins. If that ties, the higher mileage wins.
Highest One row per plan: the variant with the highest mileage.
Longest One row per plan: the variant with the longest duration.
All Every variant, as before.

The baseline mileage for each duration:

Duration Baseline mileage
12 months 15,000
24 months 30,000
36 months 60,000
48 months 75,000
60 months 100,000
72 months 125,000

Rules to know:

  • The rows are picked first. The limits apply after that, each with its own unit:
    • numberOfProviders (or your account's provider limit when you do not send it) counts providers. It keeps the providers whose best plan scores highest.
    • Your account's result limit counts distinct plans, on every surface. With All it still counts plans: one plan with 3 variants is one plan and returns 3 rows.
    • filters.returnResults is applied last and counts rows. With All one plan can use several of them.
  • If your account groups results to one row per provider (for example bestValuePerProvider), that grouping picks the row. planVariant then has no visible effect.
  • The order of the results does not change. resultOrder works as before.
  • Your account can set a default for planVariant. A value in the request overrides it.
  • Send "planVariant": "All" to get the same rows you got before this option existed.

Fields you will use most

Field Notes
searchResultPlanId This plan at this price, embedded in the handoff URLs. Search again when it is older than 30 days.
planId Essential only. Shared across the duration and mileage variants of the same plan, so it is what you dedupe on. Not returned on the default Full shape; see below.
totalPrice, downpayment, monthlyPrice Already reflect vehicle, mileage, state, and your account. Do not recompute. Plans of 36 months or less can be paid monthly. Longer plans are paid in full and return 0 for monthlyPrice and downpayment, so show totalPrice for those.
duration, miles, deductible Contract terms
planChaizRating, planRatingVerdict Chaiz quality score out of 10, and its label
displayProviderName, planName What to show the customer
providerLogoUrl Full only. The provider's logo
resultLabels Badges such as BEST_VALUE and LOWEST_PRICE
maintenanceProgram Full only. The bundled maintenance program, or null when the plan has none. See below.
hasMaintenanceProgram Essential only. Whether the plan bundles a maintenance program.
planSummaryUrl, planCheckoutUrl Handoff destinations. See Search and Hand Off.

By default you get one row per planId, so a plan appears once. With planVariant: "All", the same plan appears several times at different durations and mileage tiers. That is not duplication in the results; it is the same product on different terms. See one row per plan.

Maintenance programs

Some plans bundle a prepaid maintenance program at no extra cost, covering routine servicing such as oil changes, brake pads, battery replacement and cooling system service. The two response shapes answer this differently, so check the right field for the shape you requested:

  • On Full, read maintenanceProgram. It is null when the plan does not include one; when it does, it carries a description, an items array of { label, info } describing each covered service and its reimbursement limit, and a documentUrl. There is no boolean on this shape.
  • On Essential, read the hasMaintenanceProgram boolean. The program's contents are not included; request Full for those.
"maintenanceProgram": {
  "description": "Covers essential repairs to keep your car running smoothly",
  "items": [
    { "label": "3 oil changes", "info": "$40 towards a standard oil change." },
    { "label": "1 battery or light replacement", "info": null }
  ],
  "documentUrl": "https://assets.chaiz.com/files/maintenance-program.pdf"
}

documentUrl is null when the administrator has not published a program document. That does not mean the plan lacks a maintenance program: the program's presence is signaled by maintenanceProgram being non-null, never by documentUrl. Likewise, info is null on items that have no limit or condition to state.

Deduping variants: you no longer need to dedupe. The default planVariant returns one row per plan on both response shapes. If you send planVariant: "All" and want to group variants yourself, use planId. It is returned only on the Essential shape. On the default Full shape, group on displayProviderName plus planName plus coverageType instead.

Make searches faster

A cold search takes 2 to 8 seconds because Chaiz is collecting live rates from multiple warranty administrators. That is unavoidable on the synchronous path.

If you know which vehicles a customer might ask about before they ask (a listing page, a saved vehicle, an inventory feed), you can move that wait off the customer's request entirely.

Cached and asynchronous search is opt-in per account. If it is not enabled for you, these endpoints return an error regardless of a valid request. Email dev-support@chaiz.com to have it turned on, and do not build against it until it is.

Pre-registering a vehicle

POST /api/v2/Partners/VehicleRegistration with a VIN and mileage starts the quoting work in the background so a later search returns immediately.

Status Meaning
200 Registration accepted, quoting started
201 Already cached and ready to search. The body echoes vin and mileage.
202 Already in progress, nothing more to do
400 The request did not validate, or quoting failed. response is null.

The cache is keyed on VIN plus mileage, so a meaningful mileage change means the entry no longer matches; re-register when the odometer moves by roughly 5,000 miles. Entries last 30 days. POST /api/v2/Partners/VehicleRegistration/CleanVehicleCache clears one explicitly if you know the underlying data changed.

Searching asynchronously

POST /api/v2/Partners/PlansSearchAsync/VIN (or /MakeModelYear, /LicensePlate, or the bare route) never blocks for the full quoting round trip.

Status Meaning What to do
200 Done. The body holds your plans. Use the results. This is the terminal success state.
202 Still quoting Poll the same endpoint with the same body every 1 to 2 seconds
400 Validation failed, or quoting errored. response is null. Read the errors; do not poll

Asynchronous search completes with 200, not 201. Only VehicleRegistration returns 201, and it means something different there (already cached). If you are polling for a 201 from a search endpoint you will poll forever.

There is no Retry-After header on a 202. Use your own 1 to 2 second interval, and set a sane overall ceiling so a stuck search does not hang your request.

Responses carry an X-Cache header of HIT or MISS, which is useful for confirming your pre-registration strategy is actually working.

Just the starting price

POST /api/v2/Partners/PlansSearchAsync/GetStartPrice returns only the starting price for a vehicle, for a "protection from $X" teaser without pulling a full result set. It follows the same 200 / 202 / 400 contract.

The starting price is the lowest first payment among plans of 36 months or less. When a vehicle only has longer plans, which are paid in full, there is no starting price.

When no price is available, including when a vehicle only has plans longer than 36 months, this returns an empty string rather than a number or null. Handle the empty case explicitly.

Next steps