# Errors

> HTTP status codes, machine-readable error envelopes, and retry strategies.

Every non-`2xx` response returns a JSON object with an `error` key containing a stable `code` string and a human-readable `message`. Branch on `error.code` in your application logic.

```json 400 Bad Request
{
  "error": {
    "code": "invalid_ip",
    "message": "'999.0.0.1' is not a valid IPv4 or IPv6 address."
  }
}
```

## Status codes and error codes

| HTTP | Code | Meaning | Retry? |
| --- | --- | --- | --- |
| `400` | `invalid_ip` | The IP address in the path or batch array is malformed. | No |
| `400` | `invalid_cidr` | The CIDR prefix in `/v1/network/{cidr}` is malformed. | No |
| `400` | `invalid_fields` | An unknown `field_families` value was requested. | No |
| `400` | `batch_too_large` | The batch array exceeds 100 addresses. | No, split into chunks of 100 |
| `401` | `missing_api_key` | No API key was supplied. | No |
| `401` | `invalid_api_key` | The API key is unknown or revoked. | No |
| `403` | `forbidden_field_family` | A requested field family is not included in the key’s plan. | No |
| `404` | `not_found` | The path does not match any endpoint. | No |
| `429` | `rate_limited` | Per-second burst limit exceeded. Wait `Retry-After` seconds. | Yes, with backoff |
| `429` | `quota_exceeded` | Monthly quota reached and prepaid wallet auto-top-up is disabled. | After refill or reset |
| `500` | `internal_error` | Unexpected server fault. | Yes, with backoff |
| `503` | `unavailable` | Node is draining or temporarily overloaded. | Yes, with backoff |

## Retry discipline

Retry only `429` (`rate_limited`), `500`, `502`, `503` and `504`, plus network timeouts. All [official server SDKs](https://ipscope.io/docs/sdks) implement exponential backoff with jitter and honour the `Retry-After` header out of the box.
