Errors
Errors come back as JSON with a consistent shape, so one handler covers all of them. What differs is whether the error is worth retrying — that is the distinction to build around.
The error shape
Every failure returns the same four fields:
- Name
statusCode- Type
- integer
- Description
The HTTP status code, repeated in the body.
- Name
error- Type
- string
- Description
A short, stable label for the error class, e.g.
Unauthorized Request.
- Name
message- Type
- string
- Description
A human-readable description. This is the field that tells you which specific thing went wrong.
- Name
validation- Type
- object
- Description
Per-field validation detail when the request body was rejected. Empty for errors that are not about field validity.
Error response
{
"statusCode": 401,
"error": "Unauthorized Request",
"message": "x-api-key must be valid",
"validation": {}
}
Branch on statusCode for how to react, and read message for what to log or surface. error is the stable machine-readable label; message is more specific but more likely to be reworded over time, so avoid matching on its exact text.
Status codes
- Name
200- Description
Success. Note that a successful response can still contain zero matches — an empty result set is not an error.
- Name
400- Description
The request body was invalid. Check
validationfor the offending fields. Commonly a malformed address, a ZIP that is not five digits, or a missing geo field on a search.
- Name
401- Description
The API key was missing, malformed, or revoked. See Authentication.
- Name
429- Description
A rate limit was exceeded. See Rate limits — whether to retry depends on which limit you hit.
- Name
5xx- Description
A server-side error. Safe to retry with backoff.
What to retry
Retrying the wrong error wastes credits and can extend an outage. The rule of thumb:
| Status | Retry? | Why |
|---|---|---|
400 | No | The body is wrong. Retrying sends the same wrong body. |
401 | No | The key is wrong. Fix the credential. |
429 per-second | Yes | Back off with jitter; capacity returns in seconds. |
429 daily ceiling | No | Nothing frees up until the UTC window rolls over. Queue for the next window. |
5xx | Yes | Transient. Exponential backoff. |
Both 429 cases share a status code, so distinguish them by message — a daily ceiling names the exhausted scopes, e.g. Daily usage limit exceeded for [PropertySearch, SkipTrace] scopes.
No match versus error
Worth stating plainly, because it is a common source of bad error handling: an address that does not resolve to a property is not an error. You get a 200 with no data. Code that treats "empty" as "failed" will retry lookups that are never going to succeed.
Property Detail in particular has its own documented failure modes around ambiguous and unmatched addresses — see Error handling for Property Detail.