Ошибки

Ошибки возвращаются в JSON, как и всё остальное, с соответствующим HTTP-статусом и кодом, по которому можно ветвить логику. Сообщение представляет собой предложение для ваших логов, а не для пользователей; его формулировка может меняться, код не изменится никогда.

Обёртка ошибки

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

param присутствует, если проблему вызвал конкретный параметр. X-Request-Id задаётся и в ответах с ошибкой; укажите его, если обращаетесь в поддержку.

Коды

HTTPcodeКогдаЧто делать
400invalid_requestОтсутствует обязательный параметр, значение имеет неверный формат или вне допустимого диапазона, в пакете больше 100 точек или адресов, запрошено больше 10 результатов.Исправьте запрос. Не повторяйте его без изменений.
401missing_keyКлюч не передан, а доступ без ключа на этом сервере отключён. По умолчанию он включён, и запрос без ключа просто обрабатывается.Передайте ключ в X-API-Key, в виде bearer-токена или в key=.
429quota_exceededАдрес исчерпал свои 2 500 бесплатных запросов на сегодня без ключа.Дождитесь сброса (когда он произойдёт, показывают Retry-After и X-Quota-Reset) или передайте ключ с балансом или пакетом Unlimited.
401invalid_keyКлюч не существует.Проверьте, нет ли опечаток или обрезанной при копировании части.
401key_revokedКлюч был отозван в личном кабинете.Используйте действующий ключ.
402no_creditsБесплатная дневная квота ключа исчерпана, а баланс аккаунта пуст.Пополните баланс в личном кабинете картой или криптовалютой либо дождитесь сброса.
403key_ip_limitВсе IP-слоты этого ключа на ближайшее время заняты другими адресами.Используйте одну из машин, занимающих слоты, выдайте этой машине собственный ключ или добавьте пакет. См. IP-слоты.
403account_suspendedАккаунт заблокирован.Проверьте письма от нас или создайте обращение в поддержку.
404not_foundПуть не существует. Запрос без совпадений возвращает 200 с пустыми результатами, а не 404.Проверьте путь и префикс версии.
500server_errorЧто-то сломалось на нашей стороне.Повторите один раз через секунду. Ошибка записывается в лог и оповещает нас. Не засчитывается.
503unavailableБэкенд временно недоступен.Повторяйте с увеличивающейся задержкой. Не засчитывается.

Что не является ошибкой

Ошибки на совместимых хостах

Совместимые хосты возвращают ошибки в том формате, который использует исходный провайдер, поэтому существующая обработка работает. Например, пустой баланс это "status": "OVER_QUERY_LIMIT" с HTTP 200 на gapi.mygeocode.com, "statusCode": 429 в обёртке Bing на bing.mygeocode.com и {"status":"fail","message":"quota"} на ipapi.mygeocode.com; неверный ключ соответственно REQUEST_DENIED, 401 и invalid key. Заголовки X-Quota-* отправляются на каждом хосте в любом случае, поэтому при желании реальное состояние можно узнать из заголовков.

Разумная стратегия обработки

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 r.status_code in (500, 503):
            time.sleep(0.2 * (2 ** attempt))
            continue
        # no_credits, key_ip_limit, invalid_request, invalid_key: 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)