MLS Detail Errors — A Complete Index
Every error /v3/MLSDetail can return, organized by category. POST /v3/MLSDetail resolves a single record by one of four lookups; when more than one is supplied, precedence is mls_number → listing_id → assessor_id → address. Each error includes a human-readable message, and the 400/404 bodies echo your original input so you can correlate the error against exactly what you sent.
Tag scheme
Every error type below is tagged three ways so both humans and machines can branch on it:
- Tag — a short human-readable label (e.g. Board Required).
- Error Code — a stable machine string returned in
errorCode(e.g.MLS_NUMBER_NEEDS_BOARD). - HTTP — the exact status code.
Status legend: ✅ = code is emitted by the API today · 🟨 = proposed — the scenario returns the correct HTTP status now, but the structured
errorCodeis not yet emitted (raw Joi message only). The 🟨 rows are the implementation target for the MLS Detail error-handling pass.
How to read an error response
{
"statusCode": 404,
"input": { "...": "your original request payload" },
"data": {},
"recordCount": 0,
"errorMessage": "...",
"errorCode": "MLS_NOT_FOUND"
}Validation (400) errors currently follow the standard Boom shape (statusCode, error, message) and include the original input; adding the errorCode tags marked 🟨 below is the planned enhancement.
Where errors come from
| Layer | Trigger | Becomes |
|---|---|---|
Route validation (Joi failAction) | bad payload, no lookup, unknown key, incomplete address parts | 400 — all errors at once (abortEarly:false); unknown keys get a "did you mean" suggestion |
| Scope / plan gate | key lacks MLS scope / plan / balance | 403 / 402 |
| Lookup miss | valid request, no matching record | 404 MLS_NOT_FOUND (lookup-aware message) — unless the cross-sold fallback applies (see §4) |
| Uncaught | anything else | 500 INTERNAL_ERROR (generic, no detail leaked) |
1. Request Shape Errors (Validation)
Fired when the payload itself is malformed. All validation errors are returned together in one response, so fix everything the message lists in a single pass.
| Tag | Error Code | HTTP | Status |
|---|---|---|---|
| Missing Lookup | MISSING_LOOKUP | 400 | 🟨 |
| Board Required | MLS_NUMBER_NEEDS_BOARD | 400 | 🟨 |
| Address Too Short | INVALID_ADDRESS | 400 | 🟨 |
| Incomplete Address | INCOMPLETE_ADDRESS | 400 | 🟨 |
| Unknown Field | UNKNOWN_FIELD | 400 | 🟨 |
| Invalid Field Section | INVALID_FIELD_SECTION | 400 | 🟨 |
| Invalid Field Selector | INVALID_FIELD_SELECTOR | 400 | 🟨 |
| Invalid Type | VALIDATION_FAILED | 400 | 🟨 |
Missing Lookup — MISSING_LOOKUP (400)
MISSING_LOOKUP (400)At least one of assessor_id, listing_id, address, mls_number, or house is required.
{ "public": true }→ "value" must contain at least one of [assessor_id, listing_id, address, mls_number, house]
Board Required — MLS_NUMBER_NEEDS_BOARD (400)
MLS_NUMBER_NEEDS_BOARD (400)MLS numbers are not unique across boards, so mls_number requires mls_board_code.
{ "mls_number": "1234567" }→ "mls_number" missing required peer "mls_board_code"
Address Too Short — INVALID_ADDRESS (400)
INVALID_ADDRESS (400)address must be a full street address (minimum 10 characters).
{ "address": "123 Main" }→ address must contain full street address including city, state and zip
Incomplete Address — INCOMPLETE_ADDRESS (400)
INCOMPLETE_ADDRESS (400)Two conditional rules govern component-based lookups:
- If
houseis provided → also requiresstreet, plus (ziporcity), plus (cityandstate). - If any of
city/state/zipis provided → also requiresstreet(no bare city/zip lookups).
{ "house": "123", "city": "Austin" }→ errors requiring street and state
Unknown Field — UNKNOWN_FIELD (400)
UNKNOWN_FIELD (400){ "listng_id": 123 }→ "listng_id" is not allowed (did you mean "listing_id"?)
Invalid Field Section — INVALID_FIELD_SECTION (400)
INVALID_FIELD_SECTION (400){ "listing_id": 123, "field_sections": ["adress"] }→ "adress" is not a valid field section — did you mean "address"?
Invalid Field Selector — INVALID_FIELD_SELECTOR (400)
INVALID_FIELD_SELECTOR (400)Leaf-aware suggestion against the same field catalog as MLS Search:
{ "listing_id": 123, "fields": ["listingDetals.beds"] }→ "listingDetals.beds" is not a selectable field — did you mean: "listingDetails.beds"? See the v3 MLS field catalog in the docs.
Invalid Type — VALIDATION_FAILED (400)
VALIDATION_FAILED (400)listing_id must be a number, state a 2-character code, etc. — standard Joi type messages.
2. Authentication & Plan Errors
These fire before the lookup is attempted.
| Tag | Error Code | HTTP | Status |
|---|---|---|---|
| Key Not Found | AUTH_KEY_NOT_FOUND | 401 | ✅ |
| Key Inactive | AUTH_KEY_INACTIVE | 401 | ✅ |
| Scope Unauthorized | AUTH_SCOPE_UNAUTHORIZED | 403 | ✅ |
| MLS Plan Required | MLS_PLAN_REQUIRED | 403 | ✅ |
| MLS Subscription Inactive | MLS_SUBSCRIPTION_INACTIVE | 402 | ✅ |
| Insufficient Balance | WALLET_INSUFFICIENT_BALANCE | 402 | ✅ |
| Rate Limited | RATE_LIMIT_EXCEEDED | 429 | ✅ |
| Daily Cap Reached | DAILY_USAGE_EXCEEDED | 429 | ✅ |
| Test Key Cap Reached | TEST_KEY_USAGE_EXCEEDED | 429 | ✅ |
Fix: check your plan status in the billing console, or confirm the correct key/scope is in use.
3. Not Found
A valid request that matched no record. The message is lookup-aware so you can self-diagnose the miss.
| Tag | Error Code | HTTP | Status |
|---|---|---|---|
| No Record Found | MLS_NOT_FOUND | 404 | ✅ |
| Lookup used | Message |
|---|---|
mls_number (+ mls_board_code) | No MLS listing found for mls_number "X" on board "Y". Verify both — MLS numbers are not unique across boards. |
listing_id | No MLS listing found for listing_id 123. |
assessor_id | No MLS listing found for assessor_id 123. |
address | No MLS listing matched "...". Try listing_id or assessor_id, or the property may simply not be in MLS. |
The 404 body includes input, data: {}, recordCount: 0, statusCode: 404, and errorCode: "MLS_NOT_FOUND".
4. Cross-Sold Fallback (200, not an error)
If the account holds both an active MLS plan and public-record access, and public_record_fallback is not set to false, an MLS miss returns the public record (PropertyDetail shape) instead of a 404:
{
"statusCode": 200,
"dataSource": "public_record",
"recordCount": 1,
"data": { "...": "public-record fields" }
}This is billed as one public-record credit, not an MLS credit. Set public_record_fallback: false to disable it and receive the 404 instead.
5. Public-Record Block Omitted (warning, not an error)
Requesting the public.* block (via public: true or a public.* path in fields) without a public-record plan does not error — the block is silently omitted and a warning is attached so you don't mistake it for missing data:
"warnings": ["public-record data (public.*) was requested but omitted: your plan does not include public-record access ..."]6. Server Errors
| Tag | Error Code | HTTP | Status |
|---|---|---|---|
| Internal Error | INTERNAL_ERROR | 500 | ✅ |
No internal detail is leaked; a requestId is included when available — include it when contacting support.
Quick Reference: All Error Codes by Status
| HTTP | Tag → Error Code |
|---|---|
| 400 | Missing Lookup → MISSING_LOOKUP 🟨 · Board Required → MLS_NUMBER_NEEDS_BOARD 🟨 · Address Too Short → INVALID_ADDRESS 🟨 · Incomplete Address → INCOMPLETE_ADDRESS 🟨 · Unknown Field → UNKNOWN_FIELD 🟨 · Invalid Field Section → INVALID_FIELD_SECTION 🟨 · Invalid Field Selector → INVALID_FIELD_SELECTOR 🟨 · Invalid Type → VALIDATION_FAILED 🟨 |
| 401 | Key Not Found → AUTH_KEY_NOT_FOUND ✅ · Key Inactive → AUTH_KEY_INACTIVE ✅ |
| 402 | MLS Subscription Inactive → MLS_SUBSCRIPTION_INACTIVE ✅ · Insufficient Balance → WALLET_INSUFFICIENT_BALANCE ✅ |
| 403 | Scope Unauthorized → AUTH_SCOPE_UNAUTHORIZED ✅ · MLS Plan Required → MLS_PLAN_REQUIRED ✅ |
| 404 | No Record Found → MLS_NOT_FOUND ✅ |
| 429 | Rate Limited → RATE_LIMIT_EXCEEDED ✅ · Daily Cap Reached → DAILY_USAGE_EXCEEDED ✅ · Test Key Cap Reached → TEST_KEY_USAGE_EXCEEDED ✅ |
| 500 | Internal Error → INTERNAL_ERROR ✅ |