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 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.