エラー

エラーも他のすべてと同じく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このアドレスは、キーなしでの1日2,500件の無料リクエストを使い切りました。リセットを待つか(時刻はRetry-AfterX-Quota-Resetでわかります)、クレジットまたはUnlimitedパッケージのあるキーを送信してください。
401invalid_keyキーが存在しません。入力ミスや、コピーが途中で切れていないかを確認してください。
401key_revokedキーがダッシュボードで無効化されました。有効なキーを使用してください。
402no_creditsキーの1日の無料枠を使い切り、アカウントのクレジット残高もありません。ダッシュボードでカードまたは暗号資産でクレジットをチャージするか、リセットを待ってください。
403key_ip_limitこのキーのIPスロットがすべて、当面の間ほかのアドレスに占有されています。スロットを占有しているマシンのいずれかを使うか、このマシン専用のキーを用意するか、パッケージを追加してください。IPスロットをご覧ください。
403account_suspendedアカウントが停止されています。当社からのメールを確認するか、チケットを作成してください。
404not_foundパスが存在しません。一致する結果がないクエリは、404ではなく空の結果とともに200になります。パスとバージョンの接頭辞を確認してください。
500server_error当社側で問題が発生しました。1秒後に1回だけ再試行してください。記録され、当社に通知されます。数えられません。
503unavailableバックエンドが一時的に利用できません。バックオフを入れて再試行してください。数えられません。

エラーではないもの

互換ホストでのエラー

互換ホストは元のプロバイダーと同じ形式でエラーを返すため、既存のエラー処理がそのまま動作します。たとえば、クレジット残高がない場合、gapi.mygeocode.comではHTTP 200とともに"status": "OVER_QUERY_LIMIT"bing.mygeocode.comではBingのエンベロープ内の"statusCode": 429ipapi.mygeocode.comでは{"status":"fail","message":"quota"}になります。無効なキーの場合は、それぞれREQUEST_DENIED401invalid 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)