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:

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

LimitValueOver it
Addresses per batch100400, reason TOO_MANY_IPS
Request body64 KB413 (an HTML page from the web server, not JSON)
callback name64 characters400, 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.
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"])

This page as Markdown: /docs/rate-limits/index.md