Errors and Rate Limits
The error envelope
Every response is wrapped. response carries the payload, errorResponse carries the problems, and
exactly one of them is populated.
{
"response": null,
"errorResponse": {
"errors": [
{
"errorMessage": "Can't parse vin: invalidvin",
"errorField": "VIN",
"errorCode": "CantParseVIN",
"errorNumber": 4005,
"suggestions": [
{
"action": "CheckInput",
"message": "Verify the VIN is exactly 17 alphanumeric characters (no I, O, or Q)."
},
{
"action": "RetryWithAlternative",
"message": "Search by make, model, and year instead.",
"endpoint": "/api/v2/Partners/PlansSearch/MakeModelYear"
}
]
}
]
}
}
The field is errorResponse, and the payload field is response. Neither is called data.
| Field | Notes |
|---|---|
errorMessage |
Human readable. Safe to log, not always safe to show a customer verbatim. |
errorField |
Which input caused it. May be null. |
errorCode |
Machine readable identifier. Branch on this. |
errorNumber |
Numeric equivalent. Also stable. |
suggestions |
What to do about it. Often absent. |
affectedEntityIds |
Which entities the error relates to, where applicable. |
Suggestions
Where present, suggestions tells you what to do next without you having to encode the rules yourself.
| Field | Notes |
|---|---|
action |
CheckInput, RetryWithAlternative, UseDifferentEndpoint, or ContactSupport |
message |
Written to be shown to an end user as-is |
endpoint |
An alternative endpoint to call, on RetryWithAlternative and UseDifferentEndpoint |
This is especially useful for AI agents and automated integrations: a RetryWithAlternative with an
endpoint is a machine-followable instruction.
A 200 is not always a success. POST /api/v2/Partners/Order/CreateWithUserDetails returns 200
with a populated errorResponse for declined payments, failed validation, expired plans, and fraud
checks. Only 201 means an order was created. Branch on the status code, or on errorResponse being
empty, never on response.ok alone. See Full API Checkout.
HTTP status codes
| Status | Meaning | What to do |
|---|---|---|
200 |
Success on most endpoints. On order creation, check errorResponse first. |
|
201 |
Created. Order created, or on VehicleRegistration, already cached. |
|
202 |
Accepted, still working. Async search only. | Poll the same endpoint every 1 to 2 seconds |
400 |
Validation failed | Read errorResponse.errors. Do not retry unchanged. |
401 |
Missing, invalid, rotated, or wrong-environment key | See Authentication |
403 |
Authenticated but not permitted | Your account lacks the capability. Contact support. |
410 |
Gone. Order not created, cancelled, or inactive. | |
429 |
Rate limited | Back off per Retry-After |
5xx |
Server-side failure | Safe to retry with backoff |
Only 5xx and connection failures are safe to retry blindly. Never retry a slow plan search: it has
not failed, and a retry starts a second independent search that quotes the vehicle twice.
Validation errors (4xxx)
| Number | Code | Field | Meaning | Fix |
|---|---|---|---|---|
| 4001 | ZipAndStateAreEmpty |
ZipAndState | Zip and state both missing | Send stateShortName or zip |
| 4002 | CarDetailsAreEmpty |
CarDetails | No vehicle identifier at all | Send a VIN, a plate, or make/model/year |
| 4005 | CantParseVIN |
VIN | VIN not valid | 17 alphanumeric characters, no I, O, or Q |
| 4006 | CantParseLicensePlate |
LicensePlate | Plate not valid | Check the format, and send stateShortName with it |
| 4009 | MileageIsLessThanZero |
Mileage | Negative mileage | Send a positive integer |
| 4013 | StateShortNameIsInvalid |
StateShortName | Not a US state code | Two-letter code, for example NY |
| 4014 | MakeIsInvalid |
Make | Make not in the catalog | Resolve it with Vehicle Lookup |
| 4015 | ModelIsInvalid |
Model | Model not valid for that make | Resolve it with Vehicle Lookup |
| 4017 | DurationFilterCantbeEmpty |
DurationFilter | Duration filter present but empty | Send from and to, or omit the filter |
| 4019 | MileageIsRequired |
Mileage | Mileage was zero | Send the actual current mileage |
4014 and 4015 are the most common errors in new integrations, and almost always mean you are
sending customer-typed text straight through. Resolve it first.
Account and plan errors (20xxx)
| Number | Code | Meaning | Fix |
|---|---|---|---|
| 20004 | DurationFilterRestictedValues |
Your account allows only specific durations | Use an allowed value, or ask support to widen it |
| 20005 | DurationFilterRestrictedRange |
Your account requires from to equal to |
Send a single duration, for example { "from": 36, "to": 36 } |
| 20006 | VinHasNotBeenRegistered |
Async search on a VIN and mileage that was never pre-registered | Call VehicleRegistration first. See make searches faster |
These are configuration talking to you, not bugs in your request. If a restriction does not match what you expected to sell, contact dev-support@chaiz.com rather than working around it.
Order errors
| Number | Code | Meaning |
|---|---|---|
| 3000 | CreditCardError |
Declined, or the card details are invalid |
| 1009 | StateMismatchPlanNotAvailable |
The plan is not available for the customer's state, or the search result has expired |
Customer detail validation returns per-field errors with a null errorCode and errorNumber:
{
"response": null,
"errorResponse": {
"errors": [
{ "errorMessage": "The City field is required.", "errorField": "UserDetails.City", "errorCode": null, "errorNumber": null }
]
}
}
errorField is the path, so UserDetails.City points straight at the offending input. Required fields
are firstName, lastName, email, address, city, state, and zip; the format rules are on
Full API Checkout. Validate in your own form first so a
customer who has already entered a card is not the one to find out.
Some endpoints use a shorter form with no errorNumber, for example errorCode: "EmptyValue" with
errorMessage: "VIN is required.". Handle a missing errorNumber rather than assuming it is present.
Rate limits
Limits are per partner account, not per IP, and they apply as two simultaneous windows: hourly and daily. Both must have room for a request to pass.
| Tier | Per hour | Per day |
|---|---|---|
| Free (self-service registration) | 50 | 500 |
| Standard (commercial agreement) | 1,000 | 10,000 |
| High volume (by arrangement) | 5,000 | 20,000 |
GET /api/v2/Partners/Register/Usage returns your current tier and limits. Ask dev-support@chaiz.com
about a higher tier before launch rather than after you start hitting 429s.
Exceeding a limit returns 429 with a Retry-After header in seconds.
Retry-After is deliberately rounded and jittered, so it is a safe lower bound rather than an exact
countdown. Honor the value you are given and add your own jitter on top. Do not attempt to compute when
the window resets; several clients all retrying on the same computed second is how you get a second
429.
Retry-After appears on 429 only. A 202 from async search does not carry one; poll on your own 1 to
2 second interval instead.
Handling errors well
Read errorResponse before anything else, and use suggestions when they are there:
const body = await response.json();
const errors = body.errorResponse?.errors;
if (errors?.length) {
for (const e of errors) {
console.error(`${e.errorCode ?? 'validation'} (${e.errorNumber ?? '-'}): ${e.errorMessage}`);
for (const s of e.suggestions ?? []) {
// s.message is written to be shown to the customer as-is
console.log(`[${s.action}] ${s.message}${s.endpoint ? ` -> ${s.endpoint}` : ''}`);
}
}
return;
}
const plans = body.response.results;
Order creation needs the status code checked explicitly, because its failure path is also a 2xx:
// order creation returns an ARRAY of wrappers, one per plan ordered
const body = await response.json();
if (response.status !== 201) {
const errors = body.flatMap(w => w.errorResponse?.errors ?? []);
return { ok: false, errors };
}
const orderIds = body.map(w => w.response); // each is a single order id string
A short checklist that removes most avoidable failures:
- Resolve make and model through Vehicle Lookup instead of passing customer text through.
- Require state and mileage in your own form, and validate the VIN is 17 characters.
- Re-search rather than reusing a
searchResultPlanIdolder than 30 days. - Retry only on
5xx, connection errors, and429afterRetry-After. -
Log
errorCodeanderrorNumber, not just the message. The message wording can change; the codes are stable.
Still stuck?
Email dev-support@chaiz.com with the full error response, the request body, and roughly when the call happened. Remove card numbers and personal data first.