Errors

Errors are JSON, like everything else, with an HTTP status that matches and a code you can switch on. The message is a sentence meant for your logs, not for your users; it may change wording, the code never will.

The error envelope

{
  "status": "error",
  "error": {
    "code": "invalid_request",
    "message": "Parameter 'lat' must be a number between -90 and 90.",
    "param": "lat"
  }
}

param is present when a specific parameter caused the problem. X-Request-Id is set on error responses too; include it if you contact support.

Codes

HTTPcodeWhenWhat to do
400invalid_requestA required parameter is missing, a value is malformed or out of range, more than 100 elevation points, more than 10 results requested, a plain HTTP request.Fix the request. Do not retry unchanged.
401invalid_keyThe key does not exist or has been revoked.Check the key. A request with a bad key does not fall back to the free tier.
402no_creditsThe account's free allowance for the day is used and its credit balance is zero.Add credits in the dashboard, by card or crypto, or wait for the reset.
403key_ip_limitThe key has already been used from two other IP addresses in the last 24 hours.Use the key from a recorded address, whitelist this server, or create a key for it. See the two IP rule.
403account_ip_limitUnlimited plan: the account has already been used from two other IP addresses in the last 24 hours.Use one of the recorded addresses, or switch to credits, which have no account-wide address limit.
403forbiddenThe account is suspended for a terms breach.Check email from us.
404not_foundThe path does not exist. Note that a query with no matches is 200 with empty results, not 404.Check the path and version prefix.
405method_not_allowedAnything other than GET (or POST where an endpoint documents it).Use GET.
429quota_exceededThe anonymous daily allowance for this IP or /40 is used up.Wait for X-Quota-Reset or attach an account. Do not retry in a loop.
429rate_limitedMore than the burst limit in one second.Back off and retry. Retry-After: 1.
500server_errorSomething broke on our side.Retry once after a second. It is logged and alerts us. Not counted.
503unavailableThe endpoint or its data is temporarily unavailable, typically during a data release.Retry with backoff. Retry-After is set. Not counted.

Things that are not errors

Errors on the drop-in hosts

The compatibility hosts return errors in the shape the original provider uses, so existing handling works. For example, a used-up quota is "status": "OVER_QUERY_LIMIT" with HTTP 200 on gapi.mygeocode.com, "statusCode": 429 in the Bing envelope on bing.mygeocode.com, and {"status":"fail","message":"quota"} on ipapi.mygeocode.com. The X-Quota-* headers are sent on every host regardless, so you can read the real state from the headers if you want to.

A reasonable handling strategy

def call(url, params, tries=3):
    for attempt in range(tries):
        r = session.get(url, params=params, timeout=10)
        if r.status_code == 200:
            return r.json()
        body = r.json()
        code = body["error"]["code"]
        if code == "rate_limited" or r.status_code in (500, 503):
            time.sleep(0.2 * (2 ** attempt))
            continue
        # quota_exceeded, invalid_request, invalid_key, key_ip_limit: retrying will not help
        raise ApiError(code, body["error"]["message"], r.headers.get("X-Request-Id"))
    raise ApiError("retries_exhausted", "Gave up after %d attempts" % tries, None)