# host.sh docs: API error codes, reasons and how to fix them

> Every error the host.sh IP lookup API returns: HTTP status, machine-readable reason, what causes it and how to fix it, with a request that triggers it.

An error is an HTTP status of 400 or more with a JSON body (except [413](https://www.host.sh/docs/errors/index.md#413-request-entity-too-large), which is an HTML page from the web server): an `errors` array holding one object with the numeric `code` (the same as the status), a short `message`, a machine-readable `reason` and a `detail` sentence. Match on `reason`; `message` and `detail` are for people and can change.

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_LANGUAGE","detail":"lang must be one of en, de, es, fr, ja, pt-BR, ru, zh-CN"}]}
```

In a [batch](https://www.host.sh/docs/batch/index.md) an invalid address gets the same object as its item in `results`, and the batch itself still answers `200`.

## All errors

| Status | Reason | Cause |
|---|---|---|
| 400 | `BAD_REQUEST` | A JSON body that isn't valid JSON |
| 400 | `INVALID_IP_ADDRESS` | `ip` (or the address in the path) is not an IPv4 or IPv6 address |
| 400 | `INVALID_LANGUAGE` | `lang` is not one of the supported languages |
| 400 | `INVALID_FORMAT` | `format` is not `json` or `text`, or `format=text` without exactly one single-value field |
| 400 | `INVALID_CALLBACK` | `callback` is not a JavaScript identifier |
| 400 | `NO_IPS` | A batch with no addresses |
| 400 | `TOO_MANY_IPS` | A batch with more than 100 addresses |
| 401 | `TOKEN_MISSING` | A batch without a token |
| 403 | `TOKEN_INVALID` | The token is unknown, disabled or has no permission for this endpoint |
| 403 | `TOKEN_REQUIRED` | A tokenless request asked for something other than your own IP |
| 404 | (none) | No such endpoint |
| 405 | `METHOD_NOT_ALLOWED` | The endpoint doesn't take this HTTP method |
| 413 | (none) | The request body is over 64 KB; the answer is an HTML page, not JSON |
| 429 | `TOO_MANY_REQUESTS` | Over the tokenless rate limit |
| 5xx | (none) | A server error; retry later |

## 400 Bad request

### BAD_REQUEST

The body was sent as `Content-Type: application/json` but isn't valid JSON. Check the quoting, or let your HTTP library encode the body.

```sh
curl -H "Content-Type: application/json" -d '{bad' https://api.host.sh/v2/lookup/
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"BAD_REQUEST","detail":"The request could not be parsed"}]}
```

A batch body that starts with `[` or `{` but isn't valid JSON gets the same reason with the detail `The body is not valid JSON`.

### INVALID_IP_ADDRESS

`ip`, or the address in `/v2/lookup/{ip}`, is not an IPv4 or IPv6 address. Host names are not looked up; resolve them first. Leading and trailing spaces are ignored.

```sh
curl -H "Authorization: Bearer YOUR_TOKEN" "https://api.host.sh/v2/lookup/?ip=1.2.3"
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_IP_ADDRESS","detail":"ip must be an IPv4 or IPv6 address"}]}
```

Without a token, `ip=` gets [TOKEN_REQUIRED](https://www.host.sh/docs/errors/index.md#token-required) first.

### INVALID_LANGUAGE

`lang` must be one of `en`, `de`, `es`, `fr`, `ja`, `pt-BR`, `ru` or `zh-CN`, written exactly like that.

```sh
curl "https://api.host.sh/v2/lookup/?lang=xx"
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_LANGUAGE","detail":"lang must be one of en, de, es, fr, ja, pt-BR, ru, zh-CN"}]}
```

### INVALID_FORMAT

`format` must be `json` or `text`, and `format=text` needs `fields` with exactly one path to a single value (not an object). There are three details:

```sh
curl "https://api.host.sh/v2/lookup/?format=xml"
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_FORMAT","detail":"format must be json or text"}]}
```

```sh
curl "https://api.host.sh/v2/lookup/?format=text"
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_FORMAT","detail":"format=text needs exactly one field, e.g. fields=geo.country.code"}]}
```

```sh
curl "https://api.host.sh/v2/lookup/?format=text&fields=geo.country"
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_FORMAT","detail":"format=text needs a field with a single value, not an object"}]}
```

### INVALID_CALLBACK

A JSONP `callback` must be a JavaScript identifier, or identifiers joined by dots such as `app.onIp`: letters, digits, `_` and `$`, not starting with a digit, not a reserved word like `function`, at most 64 characters.

```sh
curl "https://api.host.sh/v2/lookup/?callback=1abc"
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_CALLBACK","detail":"callback must be a JavaScript identifier"}]}
```

### NO_IPS

A batch body that is empty or holds no addresses. Send a JSON array, an object with an `ips` array, or addresses as text.

```sh
curl -H "Authorization: Bearer YOUR_TOKEN" -d '[]' https://api.host.sh/v2/lookup/batch/
```

```json
{"errors":[{"code":400,"message":"Bad request","reason":"NO_IPS","detail":"Send a JSON array of addresses, e.g. [\"8.8.8.8\", \"1.1.1.1\"]"}]}
```

### TOO_MANY_IPS

A batch takes at most 100 addresses. Split longer lists into several requests.

```json
{"errors":[{"code":400,"message":"Bad request","reason":"TOO_MANY_IPS","detail":"Send at most 100 addresses per request"}]}
```

## 401 Unauthorized

### TOKEN_MISSING

The batch endpoint needs a token, as a Bearer header or `?token=`. A token in the body is not read there, because the body is the list of addresses.

```sh
curl -d '["8.8.8.8"]' https://api.host.sh/v2/lookup/batch/
```

```json
{"errors":[{"code":401,"message":"Unauthorized","reason":"TOKEN_MISSING","detail":"Send a token as ?token=, a JSON body field or an Authorization: Bearer header"}]}
```

## 403 Forbidden

### TOKEN_INVALID

The token is unknown, has been disabled, or its plan doesn't include this endpoint. Check that it is copied whole, with no spaces or quotes around it. When a request has a token in more than one place, the one the API reads first counts; see [Authentication](https://www.host.sh/docs/index.md#authentication).

```sh
curl -H "Authorization: Bearer not-a-token" https://api.host.sh/v2/lookup/
```

```json
{"errors":[{"code":403,"message":"Forbidden","reason":"TOKEN_INVALID","detail":"The token is unknown, disabled or has no permission for this endpoint"}]}
```

### TOKEN_REQUIRED

Without a token the API only looks up your own IP. Asking for another address with `ip=` or `/v2/lookup/{ip}`, or sending `ua=` or `al=`, needs a token.

```sh
curl "https://api.host.sh/v2/lookup/?ip=8.8.8.8"
```

```json
{"errors":[{"code":403,"message":"Forbidden","reason":"TOKEN_REQUIRED","detail":"Looking up anything but your own IP needs a token"}]}
```

## 404, 405 and 413

### 404 Not found

There is no endpoint at this path. The answer has no `reason`. Check the version prefix and the trailing slash: `/v2/lookup/` and `/v2/lookup/batch/` end with one.

```sh
curl https://api.host.sh/v2/nothing/
```

```json
{"errors":[{"code":404,"message":"Not found"}]}
```

### METHOD_NOT_ALLOWED

Lookups take `GET`, `HEAD` and `POST`, the batch endpoint only `POST`, and `/v2/ip/` only `GET` and `HEAD`. `OPTIONS`, a browser's CORS preflight, gets `204` everywhere.

```sh
curl -X DELETE https://api.host.sh/v2/lookup/
```

```json
{"errors":[{"code":405,"message":"Not allowed","reason":"METHOD_NOT_ALLOWED","detail":"This endpoint does not support the request method"}]}
```

### 413 Request Entity Too Large

The request body is over 64 KB. The web server answers before the API sees the request, so the body is a short HTML page, not JSON. A batch of 100 addresses is far below the limit.

## 429 Too many requests

### TOO_MANY_REQUESTS

More than 10 tokenless requests a minute from one IP address. Wait the number of seconds in the `Retry-After` header, or send a token. See [Rate limits](https://www.host.sh/docs/rate-limits/index.md).

```text
{"errors":[{"code":429,"message":"Too many requests","reason":"TOO_MANY_REQUESTS","detail":"Too many requests without a token, retry after a few seconds or use a token"}]}
```

## 5xx Server errors

A `500` (`{"errors":[{"code":500,"message":"Internal server error"}]}`) is a fault on the host.sh side, and `502` to `504` can come from the network in between. Retry with a growing delay.
