> ## Documentation Index
> Fetch the complete documentation index at: https://developers.investorlift.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every error code the API can return, what it means and what to do about it.

Errors are RFC 9457 `application/problem+json` bodies with a `code` you can switch on and a `detail` you can show. The `type` URL is a link to this page, anchored at the code:

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#invalid_cursor",
  "title": "Invalid cursor",
  "status": 400,
  "code": "invalid_cursor",
  "detail": "The API issued the cursor for another query. Restart from page 1.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

Switch on `code`, never on `detail` or `title`: the codes are stable and their wording is not. `errors[]` names each offending parameter on a 400 (`param`, `message`, `code`). A 429, a 503 `pool_saturated` or `ledger_unavailable`, and a 403 `plan_limit` on the name-search day cap carry `Retry-After`.

The 500 never echoes a database message: quote `request_id`. Every response the API answers also carries it as the `X-Request-Id` header. A refusal the gateway answers itself carries it as `zp-rid`: see [Request ids](/guides/concepts/request-ids).

Two producers share this catalogue. The gateway at api.investorlift.com answers its own refusals in this shape before a request reaches the API. Those refusals include a missing key, a spent plan budget and a plan's per-minute limit. Each such code says so in its section. [https://developers.investorlift.com/problems.json](https://developers.investorlift.com/problems.json) is the codes a key at api.investorlift.com can meet as JSON, generated from the same source as this page.

## What to do

| Status and code                                                                                                                                                                              | What it means                                                                                                                                                                                                                                                                                                      | What to do                                                                                                                 |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| 400 [`validation_error`](#validation_error), [`unknown_parameter`](#unknown_parameter), [`geometry_required`](#geometry_required), [`geometry_conflict`](#geometry_conflict)                 | the request is malformed, and `errors[]` names the parameter                                                                                                                                                                                                                                                       | fix the request                                                                                                            |
| 400 [`invalid_cursor`](#invalid_cursor)                                                                                                                                                      | the data refreshed or the filters changed since page 1                                                                                                                                                                                                                                                             | restart from page 1                                                                                                        |
| 401 [`unauthorized`](#unauthorized)                                                                                                                                                          | no key, or a bad, revoked or deleted key                                                                                                                                                                                                                                                                           | check the key                                                                                                              |
| 403 [`quota_exceeded`](#quota_exceeded), [`subscription_required`](#subscription_required)                                                                                                   | your requests spent the plan's credits for the period, or the key has no plan. The gateway at api.investorlift.com answers these from its own count, and the API itself from its credit ledger                                                                                                                     | upgrade or subscribe in the developer console, or wait for the period to reset                                             |
| 403 [`payment_overdue`](#payment_overdue)                                                                                                                                                    | the subscription's last payment failed and the seven-day grace period passed. The gateway at api.investorlift.com blocks the key and charges nothing for a refused request                                                                                                                                         | update the card under Manage Billing in the developer console. The key works again within a minute of a successful payment |
| 403 [`plan_limit`](#plan_limit)                                                                                                                                                              | the request is over what the plan allows: the geometry, the daily name-search cap, an MCP page over the plan's largest. The body names the limit                                                                                                                                                                   | narrow the request, or upgrade in the developer console                                                                    |
| 404 / 410 [`not_found`](#not_found), [`gone`](#gone)                                                                                                                                         | an unknown id, or a retired investor, agent or lender id with `superseded_by` null on the 410                                                                                                                                                                                                                      | search by name. A merged id is not an error: the new profile comes back with `meta.resolved_from`                          |
| 422 [`outside_coverage`](#outside_coverage)                                                                                                                                                  | the point is farther from every loaded market's coverage rectangle than the market's point tolerance, `meta.coverage[].point_tolerance_miles`. Or the viewport misses them all. Or the ZIP, city or county on a lender list is outside the counties the lender registry covers, `meta.coverage[].lenders.counties` | tell the user that the API has no data here yet                                                                            |
| 422 [`csv_cap_exceeded`](#csv_cap_exceeded)                                                                                                                                                  | a CSV export, the loans of a lender from the Growth plan, has more rows than the cap in `X-Row-Cap`. The count runs before the first row streams                                                                                                                                                                   | narrow the `recorded_from` and `recorded_to` window or the filters, or page the JSON list                                  |
| 422 [`listings_unavailable`](#listings_unavailable), [`wholesale_unavailable`](#wholesale_unavailable), [`str_unavailable`](#str_unavailable), [`lenders_unavailable`](#lenders_unavailable) | the market has no such tables published yet                                                                                                                                                                                                                                                                        | read `meta.coverage[]` first. The API still serves the rest of the row                                                     |
| 429 [`rate_limited`](#rate_limited)                                                                                                                                                          | rate limited, and `Retry-After` says how long                                                                                                                                                                                                                                                                      | wait and retry                                                                                                             |
| 503 / 504 [`database_unavailable`](#database_unavailable), [`pool_saturated`](#pool_saturated), [`ledger_unavailable`](#ledger_unavailable), [`statement_timeout`](#statement_timeout)       | the database or the credit ledger is busy or unreachable, or a query timed out. `pool_saturated` and `ledger_unavailable` carry `Retry-After`                                                                                                                                                                      | retry later. A refresh or a failover can be in progress                                                                    |

## Every code

One row per code a key at api.investorlift.com can meet, generated from the service. The codes only the internal host answers are under [Partners and staff](#partners-and-staff). Codes are stable. Titles and wording can change. Each links to its own section below.

| Code                                              | Status | Title                              | What to do                                                                                                                                                                                          |
| ------------------------------------------------- | ------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`dataset_unavailable`](#dataset_unavailable)     | 400    | Dataset unavailable                | Remove the datasets named in errors\[]. The search guide lists every block a row can carry.                                                                                                         |
| [`geometry_conflict`](#geometry_conflict)         | 400    | Geometry conflict                  | Send one geometry only: lat and lng with radius\_miles, bbox alone, property\_id with radius\_miles, or zip or city. On the coverage route, pass one place: lat and lng, county, zip or market.     |
| [`geometry_required`](#geometry_required)         | 400    | Geometry required                  | Pass one geometry: lat and lng with radius\_miles, bbox, property\_id, zip (a list) or city.                                                                                                        |
| [`invalid_cursor`](#invalid_cursor)               | 400    | Invalid cursor                     | Restart from page 1 without cursor; the data refreshed or the query changed.                                                                                                                        |
| [`invalid_id`](#invalid_id)                       | 400    | Invalid id                         | Pass the id exactly as it was served, prefix included, for example inv\_ followed by 12 hex characters.                                                                                             |
| [`market_required`](#market_required)             | 400    | Market required                    | The id exists in each listed market, or the list ranks inside one; pass market.                                                                                                                     |
| [`quicklist_unavailable`](#quicklist_unavailable) | 400    | Quicklist unavailable              | Remove the quicklists named in errors\[]. The quicklists guide lists every name this API computes.                                                                                                  |
| [`sort_requires_point`](#sort_requires_point)     | 400    | Sort requires a reference point    | Add lat and lng or property\_id, or drop sort=distance and take the default date order.                                                                                                             |
| [`unknown_parameter`](#unknown_parameter)         | 400    | Unknown parameter                  | Remove the parameter, or write the list as comma-separated values or repeated keys, for example kind=flip,hold.                                                                                     |
| [`validation_error`](#validation_error)           | 400    | Validation error                   | Fix the parameters named in errors\[] and send the request again.                                                                                                                                   |
| [`unauthorized`](#unauthorized)                   | 401    | Unauthorized                       | Send Authorization: Bearer with a current key. A revoked or mistyped key gets the same answer.                                                                                                      |
| [`payment_overdue`](#payment_overdue)             | 403    | Payment overdue                    | Update the card under Manage Billing in the developer console. The key works again within a minute of a successful payment.                                                                         |
| [`plan_limit`](#plan_limit)                       | 403    | Plan limit                         | Narrow the request to what the body names as the limit, or upgrade the plan in the developer console.                                                                                               |
| [`quota_exceeded`](#quota_exceeded)               | 403    | Quota exceeded                     | Upgrade the plan in the developer console, or wait for the billing period to reset.                                                                                                                 |
| [`subscription_required`](#subscription_required) | 403    | Subscription required              | Subscribe to a plan in the developer console. The Free plan is enough to call the API.                                                                                                              |
| [`not_found`](#not_found)                         | 404    | Not found                          | Check the path and the id; the entity is in no loaded market, or the route does not exist.                                                                                                          |
| [`not_acceptable`](#not_acceptable)               | 406    | Not acceptable                     | Send Accept: application/json, or no Accept header at all. The API serves CSV only where a route documents it: a lender's loans from the Growth plan, the rest for partners and staff.              |
| [`gone`](#gone)                                   | 410    | Gone                               | This id is retired and has no successor; look the investor, agent or lender up by name.                                                                                                             |
| [`addresses_unavailable`](#addresses_unavailable) | 422    | Addresses unavailable              | Resolve by lat / lng or apn, or retry once coverage\[].address\_as\_of carries a date.                                                                                                              |
| [`agents_unavailable`](#agents_unavailable)       | 422    | Agents unavailable                 | Not published for this market yet. The API still serves the listing agents on deal and property rows, without ids.                                                                                  |
| [`ambiguous_address`](#ambiguous_address)         | 422    | Ambiguous address                  | Pass unit=, or the ZIP instead of the city, or pick the parcel from candidates\[] and pass its property\_id.                                                                                        |
| [`ambiguous_apn`](#ambiguous_apn)                 | 422    | Ambiguous APN                      | Pick the parcel from candidates\[] and pass its property\_id.                                                                                                                                       |
| [`auction_unavailable`](#auction_unavailable)     | 422    | Auction counts unavailable         | Not published for this market yet. Drop buys\_at\_auction, buys\_reo and bought\_auction\_kind to list every row, or wait for the market's rebuild.                                                 |
| [`cash_sale_unavailable`](#cash_sale_unavailable) | 422    | Cash sale proxy unavailable        | Not measured for this market: drop cash-buyer and sale.cash\_sale. Where auction\_counted is true, bought\_auction\_kind, buys\_at\_auction and buys\_reo are the measured cash signals.            |
| [`csv_cap_exceeded`](#csv_cap_exceeded)           | 422    | CSV cap exceeded                   | Narrow the filters, or the recorded\_from and recorded\_to window on a lender's loans, until the set is under the row cap. Or page the JSON list instead.                                           |
| [`dated_refused`](#dated_refused)                 | 422    | Dated data refused                 | Drop require\_current to accept the dated blocks (stamped in meta.dated\[]), or remove the dated filters, sort and datasets named in errors\[].                                                     |
| [`history_unavailable`](#history_unavailable)     | 422    | History unavailable                | No history for this parcel: it is outside the ZIP codes the history lake covers (meta.coverage\[].parcel.history.zips).                                                                             |
| [`lenders_unavailable`](#lenders_unavailable)     | 422    | Lenders unavailable                | Not published for this market yet, or its registry predates the borrower match or the place rankings the request asked for. The API still serves the financing block on parcels where the slice is. |
| [`listings_unavailable`](#listings_unavailable)   | 422    | Listings unavailable               | Not published for this market yet. The API still serves deal, investor and property rows with the listing block null, and the comps from the deed registry alone.                                   |
| [`outside_coverage`](#outside_coverage)           | 422    | Outside coverage                   | No data at this location. Read the loaded markets and their areas from markets\[] when present, and from meta.coverage\[] on any list response.                                                     |
| [`parcels_unavailable`](#parcels_unavailable)     | 422    | Parcel products unavailable        | Not published for this market yet. The API still serves the deal, investor and property rows.                                                                                                       |
| [`str_unavailable`](#str_unavailable)             | 422    | Short-term rental data unavailable | Not published for this market yet. The API still serves deal, investor and property rows with their short\_term\_rental and str blocks null.                                                        |
| [`wholesale_unavailable`](#wholesale_unavailable) | 422    | Wholesale transactions unavailable | Not published for this market yet; deal, investor and property rows are still served with their wholesale blocks null.                                                                              |
| [`rate_limited`](#rate_limited)                   | 429    | Rate limited                       | Retry after retry\_after seconds.                                                                                                                                                                   |
| [`internal_error`](#internal_error)               | 500    | Internal error                     | Retry once. If it occurs again, report the request\_id.                                                                                                                                             |
| [`database_unavailable`](#database_unavailable)   | 503    | Database unavailable               | Retry with backoff. The database was unreachable and the API did not run the request.                                                                                                               |
| [`ledger_unavailable`](#ledger_unavailable)       | 503    | Ledger unavailable                 | Retry after retry\_after seconds. The API charged nothing and served nothing.                                                                                                                       |
| [`pool_saturated`](#pool_saturated)               | 503    | Pool saturated                     | Retry in a second. The service is at capacity and did not run the request.                                                                                                                          |
| [`statement_timeout`](#statement_timeout)         | 504    | Statement timeout                  | Narrow the radius or add filters. The query exceeded 10 seconds.                                                                                                                                    |

## 400 Bad request

<h3 id="dataset_unavailable">
  `dataset_unavailable`
</h3>

**400 Dataset unavailable.** A name in datasets is not a block this API serves. Either the name is unknown, or no delivery carries the dataset (contact, demographic, foreclosure, batchrank). The body's errors\[] names each one.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#dataset_unavailable",
  "title": "Dataset unavailable",
  "status": 400,
  "code": "dataset_unavailable",
  "detail": "demographic is not a block this API serves (demographics).",
  "instance": "/v1/properties/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "errors": [
    {
      "param": "datasets",
      "message": "demographic needs demographics",
      "code": "dataset_unavailable"
    }
  ]
}
```

**What to do.** Remove the datasets named in errors\[]. The search guide lists every block a row can carry.

<h3 id="geometry_conflict">
  `geometry_conflict`
</h3>

**400 Geometry conflict.** The geometry parameters contradict each other. Conflicts: radius\_miles with bbox, property\_id with a point or a bbox, a lone lat or lng, or zip beside city. A place (zip or city) beside radius\_miles, bbox or property\_id is also a conflict. A place on a route that ranks around a point (buyers/match) is also a conflict. A point beside a place is the reference point, not a conflict. On the coverage route, two place groups at once are a conflict, and so is a lone lat or lng. The place groups of the coverage route are lat and lng, county, zip and market.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#geometry_conflict",
  "title": "Geometry conflict",
  "status": 400,
  "code": "geometry_conflict",
  "detail": "You cannot combine radius_miles with bbox. Send one geometry only.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Send one geometry only: lat and lng with radius\_miles, bbox alone, property\_id with radius\_miles, or zip or city. On the coverage route, pass one place: lat and lng, county, zip or market.

<h3 id="geometry_required">
  `geometry_required`
</h3>

**400 Geometry required.** The request needs one geometry: lat and lng (with radius\_miles), bbox, property\_id, zip or city.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#geometry_required",
  "title": "Geometry required",
  "status": 400,
  "code": "geometry_required",
  "detail": "The request needs one geometry: lat and lng with radius_miles, bbox, property_id, zip (a list) or city.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Pass one geometry: lat and lng with radius\_miles, bbox, property\_id, zip (a list) or city.

<h3 id="invalid_cursor">
  `invalid_cursor`
</h3>

**400 Invalid cursor.** The API cannot decode the cursor, or issued it for another query, sort, weight set or dataset version. Restart from page 1.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#invalid_cursor",
  "title": "Invalid cursor",
  "status": 400,
  "code": "invalid_cursor",
  "detail": "The API issued the cursor for another query. Restart from page 1.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Restart from page 1 without cursor; the data refreshed or the query changed.

<h3 id="invalid_id">
  `invalid_id`
</h3>

**400 Invalid id.** Every id has a prefix. The forms: deal\_\<32 hex>, prop\_\<32 hex>, inv\_\<12 hex>, agt\_\<12 hex>, wl\_\<32 hex> (an Investorlift listing), wsr\_\<12 hex> (a wholesaler), len\_\<12 hex> (a lender). The prefix is part of the id.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#invalid_id",
  "title": "Invalid id",
  "status": 400,
  "code": "invalid_id",
  "detail": "An investor id is inv_ followed by 12 hex characters. The prefix is part of the id.",
  "instance": "/v1/investors/0a20a550f33b",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Pass the id exactly as it was served, prefix included, for example inv\_ followed by 12 hex characters.

<h3 id="market_required">
  `market_required`
</h3>

**400 Market required.** The investor or agent id exists in more than one loaded market. Or you asked for a lender list (GET /v1/lenders) while the API has several markets loaded, and named no market. A lender list ranks inside one market. Pass market=. The body lists the markets.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#market_required",
  "title": "Market required",
  "status": 400,
  "code": "market_required",
  "detail": "inv_0a20a550f33b exists in 2 loaded markets. Pass market=. The codes are in markets[].",
  "instance": "/v1/investors/inv_0a20a550f33b",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "markets": [
    "phx",
    "hou"
  ],
  "errors": [
    {
      "param": "market",
      "message": "one of phx, hou",
      "code": "market_required"
    }
  ]
}
```

**What to do.** The id exists in each listed market, or the list ranks inside one; pass market.

<h3 id="quicklist_unavailable">
  `quicklist_unavailable`
</h3>

**400 Quicklist unavailable.** A quicklist name in quicklists, any\_quicklists or not\_quicklists is not one this API can compute. Either the name is unknown, or no delivery carries its dataset. Examples: notice-of-default, preforeclosure, active-auction, has-hoa and for-sale-by-owner. The body's errors\[] names each one and the dataset that unlocks it.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#quicklist_unavailable",
  "title": "Quicklist unavailable",
  "status": 400,
  "code": "quicklist_unavailable",
  "detail": "notice-of-default needs pre-foreclosure notices, which no delivery carries.",
  "instance": "/v1/properties/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "errors": [
    {
      "param": "quicklists",
      "message": "notice-of-default needs pre-foreclosure notices",
      "code": "quicklist_unavailable"
    }
  ]
}
```

**What to do.** Remove the quicklists named in errors\[]. The quicklists guide lists every name this API computes.

<h3 id="sort_requires_point">
  `sort_requires_point`
</h3>

**400 Sort requires a reference point.** sort=distance needs lat and lng, or property\_id. With bbox alone, or with a zip or city without a point, the default sort is date\_desc.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#sort_requires_point",
  "title": "Sort requires a reference point",
  "status": 400,
  "code": "sort_requires_point",
  "detail": "sort=distance needs a reference point: add lat and lng, or property_id.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Add lat and lng or property\_id, or drop sort=distance and take the default date order.

<h3 id="unknown_parameter">
  `unknown_parameter`
</h3>

**400 Unknown parameter.** The query carries a parameter the endpoint does not define. Bracketed list syntax such as kind\[] counts as an unknown parameter. A list is comma-separated values or repeated keys.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#unknown_parameter",
  "title": "Unknown parameter",
  "status": 400,
  "code": "unknown_parameter",
  "detail": "The query carries a parameter this endpoint does not define: kind[]. Write the list as kind=flip,hold or as repeated keys.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "errors": [
    {
      "param": "kind[]",
      "message": "unknown parameter",
      "code": "unknown_parameter"
    }
  ]
}
```

**What to do.** Remove the parameter, or write the list as comma-separated values or repeated keys, for example kind=flip,hold.

<h3 id="validation_error">
  `validation_error`
</h3>

**400 Validation error.** A parameter failed validation. errors\[] names each offending parameter.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#validation_error",
  "title": "Validation error",
  "status": 400,
  "code": "validation_error",
  "detail": "radius_miles must be 20 or less.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "errors": [
    {
      "param": "radius_miles",
      "message": "must be 20 or less",
      "code": "too_big"
    }
  ]
}
```

**What to do.** Fix the parameters named in errors\[] and send the request again.

## 401 Unauthorized

<h3 id="unauthorized">
  `unauthorized`
</h3>

**401 Unauthorized.** The request carries no bearer key, or the key is malformed or unknown.

*Both the gateway at api.investorlift.com and the API itself return it. The gateway's own check runs first.*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "code": "unauthorized",
  "detail": "Send Authorization: Bearer with a current key.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Send Authorization: Bearer with a current key. A revoked or mistyped key gets the same answer.

## 403 Forbidden

<h3 id="payment_overdue">
  `payment_overdue`
</h3>

**403 Payment overdue.** The subscription's last payment failed and the grace period passed. The gateway blocks the key until you update the card under Manage Billing in the console. A refused request charges nothing.

*The gateway at api.investorlift.com returns it, not the API itself. A partner who calls an internal host never sees it.*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#payment_overdue",
  "title": "Payment overdue",
  "status": 403,
  "code": "payment_overdue",
  "detail": "The subscription's payment is overdue and the grace period passed. Update the card under Manage Billing in the developer console.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Update the card under Manage Billing in the developer console. The key works again within a minute of a successful payment.

<h3 id="plan_limit">
  `plan_limit`
</h3>

**403 Plan limit.** The request exceeds what this plan allows. The limits: geometry, the daily name-search cap, an MCP page over the plan's largest, the change series' weeks over /mcp, monitors or export rows. The response names the limit. Upgrade in the console.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#plan_limit",
  "title": "Plan limit",
  "status": 403,
  "code": "plan_limit",
  "detail": "The free plan searches within 5 miles of a point, a viewport up to 10 miles across, or one ZIP code. Your zip parameter lists 2 ZIP codes. Upgrade in the developer console for city-wide and multi-ZIP searches.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "tier": "free",
  "limit": "geometry",
  "max_radius_miles": 5,
  "max_bbox_diagonal_miles": 10,
  "max_zips": 1
}
```

**What to do.** Narrow the request to what the body names as the limit, or upgrade the plan in the developer console.

<h3 id="quota_exceeded">
  `quota_exceeded`
</h3>

**403 Quota exceeded.** Your requests spent the plan's credits for the billing period: the allowance, or on Growth and Scale the overage ceiling. On the Free plan, spent lifetime credits get the same refusal, with stop budget in the body. The body carries used and line. Every route that charges credits answers it until the period resets or the plan changes. A re-read of a record you already hold gets the same refusal, from the origin before it prices the page and from the gateway's check. The routes priced at 0 and the MCP handshake keep answering.

*Both the gateway at api.investorlift.com and the API itself return it. The gateway's own check runs first.*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#quota_exceeded",
  "title": "Quota exceeded",
  "status": 403,
  "code": "quota_exceeded",
  "detail": "Your requests spent the plan's credits for this billing period (5000 of 5000). Wait for the period to reset on the subscription's billing date, or upgrade in the developer console.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "used": 5000,
  "line": 5000
}
```

**What to do.** Upgrade the plan in the developer console, or wait for the billing period to reset.

<h3 id="subscription_required">
  `subscription_required`
</h3>

**403 Subscription required.** The key has no active plan subscription: the gateway found none, or forwarded no subscription for a route that charges credits.

*Both the gateway at api.investorlift.com and the API itself return it. The gateway's own check runs first.*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#subscription_required",
  "title": "Subscription required",
  "status": 403,
  "code": "subscription_required",
  "detail": "This key has no active plan subscription. Subscribe in the developer console.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Subscribe to a plan in the developer console. The Free plan is enough to call the API.

## 404 Not found

<h3 id="not_found">
  `not_found`
</h3>

**404 Not found.** No such route, or no such deal, investor, agent, lender or parcel in any loaded market.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#not_found",
  "title": "Not found",
  "status": 404,
  "code": "not_found",
  "detail": "No such deal in any loaded market.",
  "instance": "/v1/deals/deal_00000000000000000000000000000000",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Check the path and the id; the entity is in no loaded market, or the route does not exist.

## 406 Not acceptable

<h3 id="not_acceptable">
  `not_acceptable`
</h3>

**406 Not acceptable.** The Accept header names a representation the endpoint does not produce. No Accept header, */* and application/\* mean JSON. text/csv works only where a route documents it.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#not_acceptable",
  "title": "Not acceptable",
  "status": 406,
  "code": "not_acceptable",
  "detail": "Accept text/html names no representation this endpoint produces (application/json).",
  "instance": "/v1/deals/summary",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Send Accept: application/json, or no Accept header at all. The API serves CSV only where a route documents it: a lender's loans from the Growth plan, the rest for partners and staff.

## 410 Gone

<h3 id="gone">
  `gone`
</h3>

**410 Gone.** A retired investor, agent or lender id. superseded\_by is null. Search by name instead (/v1/investors/search, /v1/agents/search or /v1/lenders/search).

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#gone",
  "title": "Gone",
  "status": 410,
  "code": "gone",
  "detail": "The registry retired investor inv_003c1db2782c in run 8 (RETIRED). No current investor holds its names. Use /v1/investors/search instead.",
  "instance": "/v1/investors/inv_003c1db2782c",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "superseded_by": null,
  "reason": "RETIRED",
  "market": "phx"
}
```

**What to do.** This id is retired and has no successor; look the investor, agent or lender up by name.

## 422 Unprocessable

<h3 id="addresses_unavailable">
  `addresses_unavailable`
</h3>

**422 Addresses unavailable.** You called GET /v1/properties/resolve with address, but no market has a published address table: meta.coverage\[].address\_as\_of is null on every market.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#addresses_unavailable",
  "title": "Addresses unavailable",
  "status": 422,
  "code": "addresses_unavailable",
  "detail": "No market has a published address table: meta.coverage[].address_as_of is null on every market. Resolve by lat / lng or apn until the sql/22j step runs.",
  "instance": "/v1/properties/resolve",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Resolve by lat / lng or apn, or retry once coverage\[].address\_as\_of carries a date.

<h3 id="agents_unavailable">
  `agents_unavailable`
</h3>

**422 Agents unavailable.** You called a /v1/agents route for a market with no published agent registry. Its meta.coverage\[].agents\_data\_end is null, so the API can find or profile no agent. An empty answer reads as "no such agent", so the API refuses. Wait for the market's agent registry. The API still serves the listing agents on deal and property rows, without ids.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#agents_unavailable",
  "title": "Agents unavailable",
  "status": 422,
  "code": "agents_unavailable",
  "detail": "This market has no published agent registry. See meta.coverage[].agents_data_end.",
  "instance": "/v1/agents/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Not published for this market yet. The API still serves the listing agents on deal and property rows, without ids.

<h3 id="ambiguous_address">
  `ambiguous_address`
</h3>

**422 Ambiguous address.** The address matches several parcels: the units of one building, or twins of the line in the ZIP or city. The body's candidates\[] lists them with their units.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#ambiguous_address",
  "title": "Ambiguous address",
  "status": 422,
  "code": "ambiguous_address",
  "detail": "7522 E Cholla St in ZIP 85254 names 3 parcels. Pass unit= to pick one. candidates[] lists them. Call GET /v1/properties/{property_id} with the one you mean.",
  "instance": "/v1/properties/resolve",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "truncated": false,
  "candidates": [
    {
      "id": "prop_0a1b2c3d4e5f60718293a4b5c6d7e8f9",
      "unit": "1",
      "n_deals": 2
    }
  ]
}
```

**What to do.** Pass unit=, or the ZIP instead of the city, or pick the parcel from candidates\[] and pass its property\_id.

<h3 id="ambiguous_apn">
  `ambiguous_apn`
</h3>

**422 Ambiguous APN.** The APN matches several parcels that are not the same parcel. candidates\[] lists them.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#ambiguous_apn",
  "title": "Ambiguous APN",
  "status": 422,
  "code": "ambiguous_apn",
  "detail": "APN 131-28-253 matches 2 parcels that are not the same parcel. candidates[] lists them. Call the parcel route with the one you mean.",
  "instance": "/v1/properties/resolve",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "candidates": [
    {
      "id": "prop_0a1b2c3d4e5f60718293a4b5c6d7e8f9",
      "apn": "131-28-253",
      "address_short": "7522 E Cholla St"
    }
  ]
}
```

**What to do.** Pick the parcel from candidates\[] and pass its property\_id.

<h3 id="auction_unavailable">
  `auction_unavailable`
</h3>

**422 Auction counts unavailable.** You gave buys\_at\_auction, buys\_reo or bought\_auction\_kind for a market with no published foreclosure-auction and REO purchase counts. Its meta.coverage\[].auction\_counted is false: its registry predates the counts. So "bought at auction or not" has no answer, and an empty page reads as "nobody buys at auction". Drop the parameter, or wait for the market's registry rebuild. The API still serves the investor and deal rows, with investor.auction and bought\_auction\_kind null.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#auction_unavailable",
  "title": "Auction counts unavailable",
  "status": 422,
  "code": "auction_unavailable",
  "detail": "This market has no published foreclosure-auction counts: its registry predates them. So the API cannot answer buys_at_auction, buys_reo and bought_auction_kind there, and meta.coverage[].auction_counted is false for it. Drop the parameter to list every row.",
  "instance": "/v1/investors",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Not published for this market yet. Drop buys\_at\_auction, buys\_reo and bought\_auction\_kind to list every row, or wait for the market's rebuild.

<h3 id="cash_sale_unavailable">
  `cash_sale_unavailable`
</h3>

**422 Cash sale proxy unavailable.** You gave the cash-buyer quicklist or the sale.cash\_sale filter for a market where meta.coverage\[].parcel.sale\_mortgage\_measured is false. Its delivery records a purchase mortgage on fewer than one priced last sale in five, so cash\_sale\_proxy is null on every parcel. So "cash or not" has no answer: an empty page reads as "no cash buyers" and a full one as "every priced sale was cash". Drop the quicklist or the filter. Where meta.coverage\[].auction\_counted is true, the measured cash signals are the auction block, bought\_auction\_kind, buys\_at\_auction and buys\_reo.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#cash_sale_unavailable",
  "title": "Cash sale proxy unavailable",
  "status": 422,
  "code": "cash_sale_unavailable",
  "detail": "The delivery records a purchase mortgage on fewer than one priced last sale in five of phx. So meta.coverage[].parcel.sale_mortgage_measured is false and cash_sale_proxy is null on every parcel there.",
  "instance": "/v1/properties/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "errors": [
    {
      "param": "quicklists",
      "message": "cash sale proxy not measured for phx",
      "code": "cash_sale_unavailable"
    }
  ]
}
```

**What to do.** Not measured for this market: drop cash-buyer and sale.cash\_sale. Where auction\_counted is true, bought\_auction\_kind, buys\_at\_auction and buys\_reo are the measured cash signals.

<h3 id="csv_cap_exceeded">
  `csv_cap_exceeded`
</h3>

**422 CSV cap exceeded.** The CSV export exceeds the 50,000-row cap (X-Row-Cap). The API counts the filtered set before the first row streams. Narrow the geometry or filters, or on the loans of a lender the recorded\_from and recorded\_to window.

Response headers: `X-Row-Cap: 50000`.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#csv_cap_exceeded",
  "title": "CSV cap exceeded",
  "status": 422,
  "code": "csv_cap_exceeded",
  "detail": "The filtered set has more than 50,000 rows; narrow the recorded_from and recorded_to window or the filters.",
  "instance": "/v1/lenders/len_d2028f0766fc/loans",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "cap": 50000,
  "n_rows_at_least": 50001
}
```

**What to do.** Narrow the filters, or the recorded\_from and recorded\_to window on a lender's loans, until the set is under the row cap. Or page the JSON list instead.

<h3 id="dated_refused">
  `dated_refused`
</h3>

**422 Dated data refused.** The request set require\_current: true and also names a dated block (valuation, financing or liens) in a filter, the sort or datasets. A dated block is a snapshot valued at the slice date in meta.coverage\[].parcel.financing.as\_of. Drop require\_current to get the dated blocks with their meta.dated\[] stamp, or drop the dated filters, sort and datasets.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#dated_refused",
  "title": "Dated data refused",
  "status": 422,
  "code": "dated_refused",
  "detail": "require_current is true and the request reads dated blocks: filters.financing, datasets valuation. The financing, lien and valuation data is a snapshot valued at 2026-06-25.",
  "instance": "/v1/properties/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "as_of": "2026-06-25",
  "errors": [
    {
      "param": "filters.financing",
      "message": "reads the dated financing slice",
      "code": "dated"
    },
    {
      "param": "datasets",
      "message": "valuation is a dated block",
      "code": "dated"
    }
  ]
}
```

**What to do.** Drop require\_current to accept the dated blocks (stamped in meta.dated\[]), or remove the dated filters, sort and datasets named in errors\[].

<h3 id="history_unavailable">
  `history_unavailable`
</h3>

**422 History unavailable.** The parcel lies outside the ZIP codes the history lake covers for its market (meta.coverage\[].parcel.history.zips), or the market has no history tables. So no timeline or listing cycle exists for the parcel. An empty timeline reads as "nothing changed", so the API refuses.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#history_unavailable",
  "title": "History unavailable",
  "status": 422,
  "code": "history_unavailable",
  "detail": "The parcel is in ZIP 85257. The history lake covers 85251 for market phx (meta.coverage[].parcel.history.zips).",
  "instance": "/v1/properties/prop_e93c776c53354a88de4e58448a6bf21b/history",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "zips": [
    "85251"
  ]
}
```

**What to do.** No history for this parcel: it is outside the ZIP codes the history lake covers (meta.coverage\[].parcel.history.zips).

<h3 id="lenders_unavailable">
  `lenders_unavailable`
</h3>

**422 Lenders unavailable.** You called a /v1/lenders route for a market with no published lender registry, so the API can find, rank or profile no lender. The registry tables are absent, or the market has no financing slice to build them from, so meta.coverage\[].lenders is null there. An empty answer reads as "no such lender", so the API refuses.

GET /v1/lenders/\{id}/borrowers and the deed-link and borrower-match filters answer it too, before any query, on a registry that predates both. Those filters are purpose, outcome, deal\_kind, investor\_id and investor\_only, and such a registry has meta.coverage\[].lenders.purchase\_measured, investor\_lending\_measured or borrowers\_measured false. Every phase-5 parameter and route answers it too on a registry built before the place rankings, or on a host without the h3 extensions. Phase 5 is a period other than 24m, a geometry, foreclosed, cell, GET /v1/lenders/\{id}/rankings and /cells, and its fields are null there. Wait for the market's lender build. The API still serves the financing block on parcels where the slice is.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#lenders_unavailable",
  "title": "Lenders unavailable",
  "status": 422,
  "code": "lenders_unavailable",
  "detail": "Market hou has no published lender registry (meta.coverage[].lenders is null there). The API still serves the financing block on parcels where the slice is.",
  "instance": "/v1/lenders/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "markets": [
    "hou"
  ]
}
```

**What to do.** Not published for this market yet, or its registry predates the borrower match or the place rankings the request asked for. The API still serves the financing block on parcels where the slice is.

<h3 id="listings_unavailable">
  `listings_unavailable`
</h3>

**422 Listings unavailable.** You gave listing\_status, or asked the comps route for source=mls, for a market with no published listing tables. Its meta.coverage\[].listings\_data\_end is null, so "listed or not" has no answer. An empty page reads as "nothing listed", so the API refuses. Drop the parameter, or ask the comps for source=deed or both, or wait for the market's listing feed.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#listings_unavailable",
  "title": "Listings unavailable",
  "status": 422,
  "code": "listings_unavailable",
  "detail": "You gave listing_status for a market with no published listing tables. See meta.coverage[].listings_data_end.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Not published for this market yet. The API still serves deal, investor and property rows with the listing block null, and the comps from the deed registry alone.

<h3 id="outside_coverage">
  `outside_coverage`
</h3>

**422 Outside coverage.** The point (or parcel centroid) lies farther from every loaded market's coverage bbox than its point tolerance, or the bbox intersects none of them. The tolerance is meta.coverage\[].point\_tolerance\_miles on any list response: 20 miles for a metro, 2 for a county market. The deal, investor, wholesale-listing and short-term-rental lists answer it for a zip or city that no parcel of a loaded market carries. The body then carries zips\_unknown or city, and the loaded markets. A lender list (GET /v1/lenders) answers it too when the named zip, city or county lies outside the counties its market's lender registry covers. Those counties are meta.coverage\[].lenders.counties, and the body carries counties\_covered. A ZIP of an unloaded county is not a place with no lending.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#outside_coverage",
  "title": "Outside coverage",
  "status": 422,
  "code": "outside_coverage",
  "detail": "The point 40.712776, -74.005974 is outside every loaded market's point tolerance (loaded: phx, hou). See meta.coverage[].bbox and point_tolerance_miles on any list response.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** No data at this location. Read the loaded markets and their areas from markets\[] when present, and from meta.coverage\[] on any list response.

<h3 id="parcels_unavailable">
  `parcels_unavailable`
</h3>

**422 Parcel products unavailable.** You called POST /v1/properties/search, a financing, permits, history or listing-history route, or GET /v1/markets/\{market}/changes, for a market with no published parcel product tables. Its meta.coverage\[].parcel is null, so the API can find no parcel. An empty answer reads as "nothing matches", so the API refuses. Wait for the market's parcel build.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#parcels_unavailable",
  "title": "Parcel products unavailable",
  "status": 422,
  "code": "parcels_unavailable",
  "detail": "Market hou has no published parcel product tables (meta.coverage[].parcel is null). The location matches no market that carries them.",
  "instance": "/v1/properties/search",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "markets": [
    "hou"
  ]
}
```

**What to do.** Not published for this market yet. The API still serves the deal, investor and property rows.

<h3 id="str_unavailable">
  `str_unavailable`
</h3>

**422 Short-term rental data unavailable.** You gave the str\_status, str\_business\_use, str\_operator or str\_holdings\_min filter, or called GET /v1/str-parcels, for a market with no published short-term rental tables. Its meta.coverage\[].str\_as\_of is null, so "licensed or not" has no answer. An empty page reads as "no licence", so the API refuses. Drop the parameter, or wait for the market's STR build. The API still serves the deal, investor and property rows, with their short\_term\_rental and str blocks null.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#str_unavailable",
  "title": "Short-term rental data unavailable",
  "status": 422,
  "code": "str_unavailable",
  "detail": "This market has no published short-term rental tables. See meta.coverage[].str_as_of.",
  "instance": "/v1/str-parcels",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Not published for this market yet. The API still serves deal, investor and property rows with their short\_term\_rental and str blocks null.

<h3 id="wholesale_unavailable">
  `wholesale_unavailable`
</h3>

**422 Wholesale transactions unavailable.** You called the /v1/wholesale-listings, /v1/wholesalers or `/v1/investors/{id}/wholesale-purchases` route, or the source=investorlift or bought\_on\_investorlift filter, for a market with no published Investorlift wholesale tables. Its meta.coverage\[].wholesale\_as\_of is null, so the API can find no listing or wholesaler. An empty answer reads as "nothing listed", so the API refuses. Wait for the market's wholesale tables. The API still serves the deal, investor and property rows, with their wholesale blocks null.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#wholesale_unavailable",
  "title": "Wholesale transactions unavailable",
  "status": 422,
  "code": "wholesale_unavailable",
  "detail": "This market has no published Investorlift wholesale tables. See meta.coverage[].wholesale_as_of.",
  "instance": "/v1/wholesale-listings",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Not published for this market yet; deal, investor and property rows are still served with their wholesale blocks null.

## 429 Too many requests

<h3 id="rate_limited">
  `rate_limited`
</h3>

**429 Rate limited.** The request exceeded the per-key, per-IP or per-X-On-Behalf-Of budget. Retry-After says when to retry.

*Both the gateway at api.investorlift.com and the API itself return it. The gateway's own check runs first.*

Response headers: `Retry-After: 12`.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#rate_limited",
  "title": "Rate limited",
  "status": 429,
  "code": "rate_limited",
  "detail": "The request exceeded the per-key budget. Retry in 12 seconds.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "retry_after": 12,
  "bucket": "key"
}
```

**What to do.** Retry after retry\_after seconds.

## 500 Internal error

<h3 id="internal_error">
  `internal_error`
</h3>

**500 Internal error.** Unexpected failure. The body carries the request\_id to quote. The API echoes nothing from the database.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#internal_error",
  "title": "Internal error",
  "status": 500,
  "code": "internal_error",
  "detail": "The request failed. Quote request_id when you report it.",
  "instance": "/v1/buyers/match",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Retry once. If it occurs again, report the request\_id.

## 503 Service unavailable

<h3 id="database_unavailable">
  `database_unavailable`
</h3>

**503 Database unavailable.** The service failed to reach or keep a connection to the database.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#database_unavailable",
  "title": "Database unavailable",
  "status": 503,
  "code": "database_unavailable",
  "detail": "The service failed to reach the database and did not run the request.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Retry with backoff. The database was unreachable and the API did not run the request.

<h3 id="ledger_unavailable">
  `ledger_unavailable`
</h3>

**503 Ledger unavailable.** The credit ledger is unreachable. Retry after the interval in Retry-After. The API charged nothing.

Response headers: `Retry-After: 5`.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#ledger_unavailable",
  "title": "Ledger unavailable",
  "status": 503,
  "code": "ledger_unavailable",
  "detail": "The credit ledger is unreachable. The API charged nothing. Retry in 5 seconds.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "retry_after": 5
}
```

**What to do.** Retry after retry\_after seconds. The API charged nothing and served nothing.

<h3 id="pool_saturated">
  `pool_saturated`
</h3>

**503 Pool saturated.** No pooled connection came free within 2 s, or the pod reached its in-flight cap. Retry-After: 1.

Response headers: `Retry-After: 1`.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#pool_saturated",
  "title": "Pool saturated",
  "status": 503,
  "code": "pool_saturated",
  "detail": "No pooled connection was free. The service did not run the request.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Retry in a second. The service is at capacity and did not run the request.

## 504 Gateway timeout

<h3 id="statement_timeout">
  `statement_timeout`
</h3>

**504 Statement timeout.** The query exceeded the 10 s statement timeout. Narrow the geometry or filters.

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#statement_timeout",
  "title": "Statement timeout",
  "status": 504,
  "code": "statement_timeout",
  "detail": "The query exceeded the 10 second statement timeout. Narrow the geometry or filters.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Narrow the radius or add filters. The query exceeded 10 seconds.

## Partners and staff

The internal host alone answers the codes below. A `zpka_` key at api.investorlift.com never meets them. The gateway is the reason. It names the developer in `X-On-Behalf-Of` itself. It presents a key that asks for no contact field. It routes neither the probes nor the routes of the internal host (the pins route). Each keeps its own section, so an error body's `type` URL lands here whichever host sent it.

| Code                                              | Status | Title                   | What to do                                                                                                                                                                 |
| ------------------------------------------------- | ------ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`on_behalf_of_required`](#on_behalf_of_required) | 400    | X-On-Behalf-Of required | Send X-On-Behalf-Of with an opaque user or org id on every request made with this key.                                                                                     |
| [`scope_required`](#scope_required)               | 403    | Scope required          | This key does not carry the scope the request needs. Investorlift sets scopes for partners and staff, and a deals-only key gets the same answers with contact fields null. |
| [`pin_cap_exceeded`](#pin_cap_exceeded)           | 422    | Pin cap exceeded        | Shrink the geometry or add filters, or ask for cells instead of pins at this zoom level.                                                                                   |
| [`not_ready`](#not_ready)                         | 503    | Not ready               | Wait and retry. The pod is in startup or lost its database.                                                                                                                |

<h3 id="on_behalf_of_required">
  `on_behalf_of_required`
</h3>

**400 X-On-Behalf-Of required.** A key that carries a contact scope (contact or mcp\_contact) must send X-On-Behalf-Of: \<opaque Investorlift user or org id> on every request.

*Internal host only: a `zpka_` key at api.investorlift.com never meets this code. The gateway is the reason. It names the developer in `X-On-Behalf-Of` itself. It presents a key that asks for no contact field. It routes neither the probes nor the routes of the internal host (the pins route).*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#on_behalf_of_required",
  "title": "X-On-Behalf-Of required",
  "status": 400,
  "code": "on_behalf_of_required",
  "detail": "This key carries a contact scope: send X-On-Behalf-Of on every request.",
  "instance": "/v1/buyers/match",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Send X-On-Behalf-Of with an opaque user or org id on every request made with this key.

<h3 id="scope_required">
  `scope_required`
</h3>

**403 Scope required.** The key lacks the scope this endpoint or representation needs (deals or contact).

*Internal host only: a `zpka_` key at api.investorlift.com never meets this code. The gateway is the reason. It names the developer in `X-On-Behalf-Of` itself. It presents a key that asks for no contact field. It routes neither the probes nor the routes of the internal host (the pins route).*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#scope_required",
  "title": "Scope required",
  "status": 403,
  "code": "scope_required",
  "detail": "This request needs the contact scope.",
  "instance": "/v1/deals",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** This key does not carry the scope the request needs. Investorlift sets scopes for partners and staff, and a deals-only key gets the same answers with contact fields null.

<h3 id="pin_cap_exceeded">
  `pin_cap_exceeded`
</h3>

**422 Pin cap exceeded.** The geometry holds more deals than cap. The body carries n\_deals and cap, and suggestion is "cells".

*Internal host only: a `zpka_` key at api.investorlift.com never meets this code. The gateway is the reason. It names the developer in `X-On-Behalf-Of` itself. It presents a key that asks for no contact field. It routes neither the probes nor the routes of the internal host (the pins route).*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#pin_cap_exceeded",
  "title": "Pin cap exceeded",
  "status": 422,
  "code": "pin_cap_exceeded",
  "detail": "The geometry holds 61,204 deals after the filters, above the pin cap of 20,000. Use /v1/deals/cells at this zoom level, or narrow the geometry or filters.",
  "instance": "/v1/deals/pins",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3",
  "n_deals": 61204,
  "cap": 20000,
  "suggestion": "cells"
}
```

**What to do.** Shrink the geometry or add filters, or ask for cells instead of pins at this zoom level.

<h3 id="not_ready">
  `not_ready`
</h3>

**503 Not ready.** The readiness check failed: no database, absent table privileges, or empty coverage.

*Internal host only: a `zpka_` key at api.investorlift.com never meets this code. The gateway is the reason. It names the developer in `X-On-Behalf-Of` itself. It presents a key that asks for no contact field. It routes neither the probes nor the routes of the internal host (the pins route).*

```json theme={null}
{
  "type": "https://developers.investorlift.com/guides/concepts/errors#not_ready",
  "title": "Not ready",
  "status": 503,
  "code": "not_ready",
  "detail": "The readiness check failed: the pod is in startup or lost its database.",
  "instance": "/ready",
  "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
}
```

**What to do.** Wait and retry. The pod is in startup or lost its database.


## Related topics

- [Errors over MCP](/mcp/errors.md)
- [Quickstart](/guides/quickstart.md)
- [The twenty-six tools](/mcp/tools.md)
- [API reference](/api-reference/introduction.md)
- [Request ids](/guides/concepts/request-ids.md)
