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.

FieldNotes
errorMessageHuman readable. Safe to log, not always safe to show a customer verbatim.
errorFieldWhich input caused it. May be null.
errorCodeMachine readable identifier. Branch on this.
errorNumberNumeric equivalent. Also stable.
suggestionsWhat to do about it. Often absent.
affectedEntityIdsWhich 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.

FieldNotes
actionCheckInput, RetryWithAlternative, UseDifferentEndpoint, or ContactSupport
messageWritten to be shown to an end user as-is
endpointAn 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

StatusMeaningWhat to do
200Success on most endpoints. On order creation, check errorResponse first.
201Created. Order created, or on VehicleRegistration, already cached.
202Accepted, still working. Async search only.Poll the same endpoint every 1 to 2 seconds
400Validation failedRead errorResponse.errors. Do not retry unchanged.
401Missing, invalid, rotated, or wrong-environment keySee Authentication
403Authenticated but not permittedYour account lacks the capability. Contact support.
410Gone. Order not created, cancelled, or inactive.
429Rate limitedBack off per Retry-After
5xxServer-side failureSafe 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)

NumberCodeFieldMeaningFix
4001ZipAndStateAreEmptyZipAndStateZip and state both missingSend stateShortName or zip
4002CarDetailsAreEmptyCarDetailsNo vehicle identifier at allSend a VIN, a plate, or make/model/year
4005CantParseVINVINVIN not valid17 alphanumeric characters, no I, O, or Q
4006CantParseLicensePlateLicensePlatePlate not validCheck the format, and send stateShortName with it
4009MileageIsLessThanZeroMileageNegative mileageSend a positive integer
4013StateShortNameIsInvalidStateShortNameNot a US state codeTwo-letter code, for example NY
4014MakeIsInvalidMakeMake not in the catalogResolve it with Vehicle Lookup
4015ModelIsInvalidModelModel not valid for that makeResolve it with Vehicle Lookup
4017DurationFilterCantbeEmptyDurationFilterDuration filter present but emptySend from and to, or omit the filter
4019MileageIsRequiredMileageMileage was zeroSend 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)

NumberCodeMeaningFix
20004DurationFilterRestictedValuesYour account allows only specific durationsUse an allowed value, or ask support to widen it
20005DurationFilterRestrictedRangeYour account requires from to equal toSend a single duration, for example { "from": 36, "to": 36 }
20006VinHasNotBeenRegisteredAsync search on a VIN and mileage that was never pre-registeredCall 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

NumberCodeMeaning
3000CreditCardErrorDeclined, or the card details are invalid
1009StateMismatchPlanNotAvailableThe 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.

TierPer hourPer day
Free (self-service registration)50500
Standard (commercial agreement)1,00010,000
High volume (by arrangement)5,00020,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 searchResultPlanId older than 30 days.
  • Retry only on 5xx, connection errors, and 429 after Retry-After.
  • Log errorCode and errorNumber, 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.