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

# When a call fails

> The five failures a developer meets first, each with its body, its recovery and what to send to support.

Every error is one RFC 9457 body with a `code` you can switch on. This page shows the five codes a new key meets
first. Each step says when the code occurs, shows the body, quotes the recovery sentence of the
[Errors](/guides/concepts/errors) page and names the one thing to check.

<Steps>
  <Step title="401 unauthorized: no key, or a wrong key">
    The request carries no `Authorization` header, or you mistyped the key, or the console revoked or deleted it. The
    gateway answers before the request reaches the API, so the response carries the id as `zp-rid`.

    ```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"
    }
    ```

    The Errors page says: "Send Authorization: Bearer with a current key. A revoked or mistyped key gets the same answer."

    Check one thing: the header reads `Authorization: Bearer zpka_...` with the whole key. [Your keys](/keys) says how to
    copy it again and how to roll it.
  </Step>

  <Step title="403 quota_exceeded: you spent the period's credits">
    Your requests spent the plan's credits for the billing period. On Growth and Scale the answer comes at the overage
    ceiling instead. Every route that charges credits answers it until the period resets or the plan changes.

    ```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
    }
    ```

    The Errors page says: "Upgrade the plan in the developer console, or wait for the billing period to reset."

    Check one thing: `X-Credits-Remaining` on your last charged response. It counts down to 0, or to the ceiling on Growth
    and Scale. [Plans and limits](/guides/plans-and-limits#at-a-limit) says what a credit is and when the period resets.
  </Step>

  <Step title="429 rate_limited: too many requests this minute or today">
    You spent a request budget. The gateway checks the plan's requests a minute and a day first. The API keeps budgets
    of its own, and the body's `bucket` names the budget that refused. The `Retry-After` header carries the seconds
    to wait.

    ```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"
    }
    ```

    The Errors page says: "Retry after retry\_after seconds."

    Check one thing: wait `Retry-After` seconds, then send the same request again. Do not retry at once.
    [Rate limits](/guides/concepts/rate-limits) has the retry rule.
  </Step>

  <Step title="422 outside_coverage: no data at this location">
    The point, viewport, ZIP or city lies outside every loaded market. The API refuses, and does not serve an empty page:
    an empty page means zero measured. For a ZIP or a city the API cannot place, the body also carries `markets[]`. The
    field lists the codes of the loaded markets.

    ```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 lies outside every loaded market's point tolerance. Read the loaded markets and their boxes from markets[] when present, and from meta.coverage[].bbox and point_tolerance_miles on any list response.",
      "instance": "/v1/deals",
      "request_id": "6f1c2a8e-3b7d-4f21-9a0c-2e5b81d7a4f3"
    }
    ```

    The Errors page says: "No data at this location. Read the loaded markets and their areas from markets\[] when present,
    and from meta.coverage\[] on any list response."

    Check one thing: [Which data is available where](/coverage/markets) lists every loaded market with its counties and
    ZIP codes. `GET /v1/coverage` answers the same question for one point, ZIP or county at 0 credits.
  </Step>

  <Step title="400 invalid_cursor: the page cursor is stale">
    The cursor came from another query, another sort or an earlier data version. The API cannot read it, so it refuses the
    page and does not serve the wrong rows.

    ```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"
    }
    ```

    The Errors page says: "Restart from page 1 without cursor. The data refreshed or the query changed."

    Check one thing: `meta.coverage[].dataset_version` on your first page. If it moved, the data refreshed and every cursor
    of the old version is stale. [Pagination](/guides/concepts/pagination) has the cursor rule.
  </Step>
</Steps>

## What to send to support

Use the support channel your plan names on [Plans and limits](/guides/plans-and-limits#support). That table also
gives the first-response target of each plan. The Free plan has no support channel yet. On a Free key, read the
[Errors](/guides/concepts/errors) page and keep the request id. Send four things:

* The `request_id` from the body, or the `X-Request-Id` header. A refusal the gateway answers itself carries the id as
  `zp-rid`.
* The UTC time of the request.
* The `code`.
* The request as you sent it, without the key.

[Request ids](/guides/concepts/request-ids) says why the id matters on a 200 too.

## Where to go next

<CardGroup cols={3}>
  <Card title="Errors" icon="triangle-exclamation" href="/guides/concepts/errors">
    Every code the API can return, with its body and what to do.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/guides/concepts/rate-limits">
    The per-minute and per-day limits, the 429 body and the one retry rule.
  </Card>

  <Card title="Plans and limits" icon="scale-balanced" href="/guides/plans-and-limits">
    What a credit is, the plans, the refusals at a limit and support.
  </Card>
</CardGroup>


## Related topics

- [Quickstart](/guides/quickstart.md)
- [Walkthroughs](/guides/walkthroughs.md)
- [Errors](/guides/concepts/errors.md)
- [Changelog](/changelog.md)
- [Read which data is available where](/api-reference/endpoints/coverage.md)
