Python library for the OpenAI APIを使って、Azure API Managementの背後にあるAzure OpenAI ServiceのGPT-5モデルにアクセスしたい

このエントリは2025/10/29現在の情報に基づいています。将来の機能追加や変更に伴い、記載事項からの乖離が発生する可能性があります。

問い合わせ

Azure OpenAI Service (AOAI) のGPT-5モデルを使おうとしているいつもの主から、以下のような問い合わせが届いた。

AOAIのGPT-5モデルはこれまでのモデルとは異なり、V1 API (Responses APIs) でアクセスしなければいけないようなので、ドキュメントに従って構成しているが、API Keyを渡すところで詰まっている。ふつうにSDKからAOAIに直接アクセスする場合、特に問題なく動作するのだが、Azure API Management (APIM) を挟んだ場合、APIMでSubscription Keyを渡そうとするとうまくいかない。APIMを挟んでも動作するようにするにはどうすればよいか?

GPT-5ではV1を使う必要がある、という記述は以下にある。

API と機能のサポート / API & feature support
https://learn.microsoft.com/azure/ai-foundry/openai/how-to/reasoning?tabs=gpt-5#api–feature-support

この主によると、OpenAIのGitHubリポジトリにはAzureOpenAIパッケージを使うようなサンプルが出ているが、

The official Python library for the OpenAI API
https://github.com/openai/openai-python?tab=readme-ov-file#microsoft-azure-openai

Azure OpenAIのドキュメントを見たところOpenAIパッケージを使うように、というガイドが出ていたので、Python library for the OpenAI API (以下、OpenAI API) を利用することにした、とのこと。

v1 API
https://learn.microsoft.com/en-us/azure/ai-foundry/openai/api-version-lifecycle?tabs=python#v1-api

ちょっと動作確認

確かに、簡単なアプリケーションを作ってみたところ、直接アクセスする分には動作した。

from openai import OpenAI

endpoint = "https://...."
model_name = "gpt-5-chat"
deployment_name = "gpt-5-chat"
api_key = "...."

def main():
    print("Hello from gpt5-chat!")
    client = OpenAI(
        base_url=f"{endpoint}",
        api_key=api_key,
    )

    completion = client.chat.completions.create(
        model=deployment_name,
        messages=[
            {"role": "user", "content": "Hello! Can you tell me a fun fact about space?"}
        ],
    )

    print("Response from gpt5-chat:")
    print(completion.choices[0].message.content)  # type: ignore

if __name__ == "__main__":
    main()

で、APIMを挟むと、以下のような例外が出て動作しない。

openai.AuthenticationError: Error code: 401 - 
{
  'statusCode': 401, 
  'message': 'Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API.'
}

これは有名な話なので、すぐにピンと来た方もいらっしゃるかもしれない。

OpenAI APIでのAPI Keyの取り扱い

ドキュメントにも記載があるが、OpenAI APIでは、

authorization: Bearer {API Key}

の形でAPI Keyを渡す。実際のコードでは以下に記載されている。

https://github.com/openai/openai-python/blob/main/src/openai/_client.py#L328
https://github.com/openai/openai-python/blob/main/src/openai/_client.py#L679

ドキュメントでは以下のあたり。

Authentication
https://platform.openai.com/docs/api-reference/authentication

API keys should be provided via HTTP Bearer authentication.

authorization: Bearer OPENAI_API_KEY

ただ、APIMを挟む場合は状況が異なる。クライアントからはAOAIのAPI Keyを渡すことはほぼなく、通常はAPIMのSubscription Key、もしくはJWTと組み合わせてAPIMに渡す。JWTだけの場合は、JWTを検証するポリシーに委ねるため、特に問題はない。APIクライアントからSubscription Keyを送信してくる場合、OpenAI APIはお構いなしで

authorization: Bearer APIM_SUBSCRIPTION_KEY

で渡してくる。もし、旧APIと同じく、api-keyでSubscription Keyを渡してくると期待している場合、つまり

api-key: APIM_SUBSCRIPTION_KEY

でAPIMを構成してしまうと、当然ながら401が返ってしまう。

現時点での回避策

現時点では以下の回避策が考えられる。

方針設定備考
①旧APIの方針に合わせる
(クライアントアプリでカスタムヘッダーを追加)
クライアントアプリは、Subscription Keyをapi-keyにも指定するよう、カスタムヘッダーを追加する
api-key: APIM_SUBSCRIPTION_KEY
– APIMでは、authorizationの内容を無視
– クライアントアプリが多い場合、修正が大変
②Responses APIに合わせる
(APIMでSubscription Key Headerを変更)
Subscription Key Headerを authorizationに変更
https://learn.microsoft.com/azure/api-management/api-management-subscriptions#use-a-subscription-key
作成したSubscription Keyの前にBearer を付ける
– クライアントアプリが本来の認証認可目的でトークンを渡してくる場合、クライアントは別のHTTP Headerを使う必要がある(authorizationは使えない)
③ホストしている旧APIの前にProxyを置く様々な方法があるが、例えばAPIMを2段構成にする。
1段目で
authorization: Bearer APIM_SUBSCRIPTION_KEY

api-key: APIM_SUBSCRIPTION_KEY
に書き換え、バックエンドサービス(旧API)にルーティング
– V1完全移行後も残存
– CORS関連で例外が出る可能性がある

①はクライアント側に一手間必要であるが、APIMとしては特に何もする必要がない。

②は、Subscription Key作成時に以下のあたりを考慮する必要がある。

  • クライアントに渡すKeyにはBearer 含めない
  • APIM側ではBearer を含めて保持しておく

これは以下の理由による。

  • APIMではBearer を含めた値でSubscription Keyをチェックするため、Bearer が抜け落ちていると401を返す

これは、クライアントアプリはSubscription Keyを

authorization: Bearer APIM_SUBSCRIPTION_KEY

で送信してくるにもかかわらず、APIM内でBearer を付けずにSubscription Keyを保持していると、APIMは

authorization: APIM_SUBSCRIPTION_KEY

を期待してしまうため、結果整合せずに401を返してしまう。そしてもう一つ、

  • APIMのInboundセクションでは、Subscription Keyの判断はできない(ポリシースコープ外)。そのため、Bearer を除去する仕組みをInboundセクション内に設定しても意味がない

は文字通りで、Subscription KeyのチェックはInboundセクションでは取り扱いできないので、Bearer の有無はポリシーで判断できない。

なので、Subscription Keyのセルフサービス発行をさせたい場合にはハードルが上がってしまう。なおカスタムのSubscription Keyを作成する方法は、以下のエントリを参照。

③は過去に以下のエントリで記載している構成と同じ。

コストも掛かるし、手間も掛かり、かつエレガントではないので、3方法の中では一番おすすめしないやり方。

どれを選択すべきか

現時点では、V1 APIへの移行過渡期のため、

  • 旧APIの利用が考えられる、かつクライアントアプリの個数が少ないのであれば、①
  • V1にすべて移行するのであれば、②
  • どうしても、というなら、③

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください