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

# Errors

> Error codes, what they mean, and how to handle them.

Errors return a consistent envelope:

```json theme={null}
{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "You have exhausted your monthly credit quota.",
    "docs_url": "https://docs.areahub.com/errors/QUOTA_EXCEEDED"
  }
}
```

Always branch on `error.code`, not on the message text. Messages may be reworded; codes are stable.

## Error codes

| Status | Code                  | What happened                                            | What to do                                                                 |
| ------ | --------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------- |
| `401`  | `API_KEY_MISSING`     | No `X-API-Key` header                                    | Add the header                                                             |
| `401`  | `API_KEY_INVALID`     | Key doesn't exist, is revoked, or expired                | Check the key; contact us if you think it should be valid                  |
| `403`  | `INSUFFICIENT_SCOPE`  | Valid key, but not authorized for this topic or endpoint | Request expanded scopes from your account manager                          |
| `429`  | `RATE_LIMIT_EXCEEDED` | Too many credits consumed this minute                    | Wait until `X-RateLimit-Reset`, then retry                                 |
| `429`  | `QUOTA_EXCEEDED`      | Monthly credit quota exhausted, overages disabled        | Wait for billing period reset, upgrade, or enable overages                 |
| `400`  | `INVALID_COORDINATES` | `lat`/`lng` missing or not parseable as numbers          | Send valid decimal degrees                                                 |
| `400`  | `UNSUPPORTED_TOPIC`   | Topic slug isn't one of the 37                           | Check the [Topics Reference](/reference/topics)                            |
| `502`  | `UPSTREAM_ERROR`      | Our internal data service failed                         | Retry with backoff. If it persists, contact support with your `request_id` |

## Retry guidance

<AccordionGroup>
  <Accordion title="Safe to retry">
    * **`429`** — wait until `X-RateLimit-Reset`, then retry.
    * **`502`** — retry with exponential backoff (1s, 2s, 4s…). Cap at \~5 attempts.

    Neither of these is charged, so retrying costs you nothing.
  </Accordion>

  <Accordion title="Do not retry">
    * **`401`**, **`403`**, **`400`** — these will fail identically every time. Fix the request.
  </Accordion>
</AccordionGroup>

## Partial failures are not errors

A bundle request can return `200` while individual topics inside it failed:

```json theme={null}
{
  "data": {
    "topics": {
      "wildfire": { "value": { "rating": "Very Low" }, "details": {} },
      "brownfield": { "value": null, "details": null, "error": "unavailable" }
    }
  },
  "meta": { "credit_cost": 35 }
}
```

A topic with `"error": "unavailable"` means that specific dataset couldn't be reached for this request. **The overall request still succeeded and is still charged in full.**

Always check for an `error` field on each topic before reading `value`:

```python theme={null}
for slug, topic in response["data"]["topics"].items():
    if topic.get("error"):
        logger.warning(f"{slug} unavailable for this location")
        continue
    process(slug, topic["value"])
```

## Getting help

Every response includes a `meta.request_id`. Include it when contacting support — it lets us find the exact request in our logs.

```json theme={null}
"meta": {
  "request_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "timestamp": "2026-07-14T16:31:55.009Z"
}
```
