# host.sh docs: API rate limits, 429 answers and Retry-After

> The host.sh API rate limits: 10 tokenless lookups a minute per IP, no per-minute limit with a token, 100 addresses per batch, and how to retry a 429.

## Without a token

Tokenless lookups of your own IP (`/v2/lookup/` with no token) are limited to 10 requests a minute per client IP address, with short bursts of up to 10. A request over the limit gets `429` and a `Retry-After` header with the number of seconds to wait:

```text
HTTP/2 429
retry-after: 6
content-type: application/json

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

The headers are readable from a browser too (the API exposes `Retry-After` to cross-origin scripts). Wait at least that long before the next request, or send a token.

Not counted against this limit:

- `GET /v2/ip/`, which only echoes your address and does no lookup
- requests with a token
- `curl host.sh` in a terminal, which is answered by host.sh itself and keeps the answer for your IP for an hour

## With a token

The API has no per-minute limit for requests with a token. Each lookup is counted for your token, and a batch counts one lookup per address. Which sections of the answer you get depends on your token's plan.

## Size limits

| Limit | Value | Over it |
|---|---|---|
| Addresses per batch | 100 | `400`, reason `TOO_MANY_IPS` |
| Request body | 64 KB | `413` (an HTML page from the web server, not JSON) |
| `callback` name | 64 characters | `400`, reason `INVALID_CALLBACK` |

## Retrying

- On `429`, wait `Retry-After` seconds, then retry.
- On a network error or a `5xx` answer, retry with a growing delay (1, 2, 4 seconds and so on).
- Don't retry `400`, `401`, `403` or `404`: the same request fails the same way. See [Errors](https://www.host.sh/docs/errors/index.md).

```python
import time
import requests

def lookup(url, attempts=5):
    for attempt in range(attempts):
        try:
            r = requests.get(url, timeout=10)
        except (requests.ConnectionError, requests.Timeout):
            time.sleep(2 ** attempt)
            continue
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "6")))
            continue
        if r.status_code >= 500:
            time.sleep(2 ** attempt)
            continue
        r.raise_for_status()  # 400, 401, 403, 404, 413: retrying won't help
        return r.json()
    raise RuntimeError("host.sh API: too many retries")

print(lookup("https://api.host.sh/v2/lookup/?fields=connection.ip")["connection"]["ip"])
```
