OpenAI の base_url 変更:優先順位とパスの書き方
送信先は引数、OPENAI_BASE_URL、既定値の順で決まります。base は /v1 や /openai/v1 までで、/chat/completions は書きません。
目次

OpenAI の公式 SDK で送信先を変えるには、クライアントを作るときの base_url(Node では baseURL)か、環境変数 OPENAI_BASE_URL を使います。両方あれば引数が勝ち、どちらもなければ https://api.openai.com/v1 に送られます。SDK はこの base の後ろに /chat/completions や /responses をつなげるだけなので、base には接続先が求めるパスの前半(/v1、/openai/v1、/v1beta/openai など)まで書き、エンドポイント名そのものは書きません。
以下の SDK の挙動は、2026年9月26日時点の最新版である openai-python 3.19.2 と openai(npm)7.23.0 のソースコード、およびローカルのエコーサーバーで実際のリクエストパスを記録した結果に基づきます。base_url が正しくても、接続先が呼び出し中の API を実装していなければ 404 になるため、Chat Completions と Responses のどちらを呼んでいるかも合わせて確認します。
優先順位:引数、環境変数、data residency
Python と Node で共通の基本は「引数 → OPENAI_BASE_URL → 既定値」です。ただし空文字の扱いなど、細部で両者は違います。
| 設定の状態 | Python(openai 3.19.2) | Node(openai 7.23.0) |
|---|---|---|
| 引数だけ指定 | 引数の URL | 引数の URL |
OPENAI_BASE_URL だけ指定 | 環境変数の URL | 環境変数の URL |
| 引数と環境変数の両方 | 引数が優先 | 引数が優先 |
| どちらもなし | https://api.openai.com/v1 | https://api.openai.com/v1 |
OPENAI_BASE_URL=""(空文字) | base が空のまま残り、送信時に APIConnectionError | 未設定と同じ扱いで https://api.openai.com/v1 に送る |
旧名の OPENAI_API_BASE だけ指定 | 無視され、https://api.openai.com/v1 | 無視され、https://api.openai.com/v1 |
data_residency="eu" と環境変数 | https://eu.api.openai.com/v1(環境変数より優先) | dataResidency: 'eu' で同じ結果 |
baseURL: null と環境変数 | 該当なし | 環境変数を読まず既定値 |
いちばん気づきにくいのは、Node で OPENAI_BASE_URL が空文字になっているケースです。エラーにならずに api.openai.com へ送られるため、ゲートウェイや他社のキーがそのまま OpenAI に届きます。OpenAI はそのキーを受け付けないので、キーの不備に見える 401 になりがちです。401 を見てキーを疑う前に、送信先を確かめる価値があります。
OPENAI_API_BASE と openai.api_base は v1 より前の openai-python で使われていた名前で、現行の SDK は読みません。古いサンプルからコピーした設定で送信先が変わらないときは、ここを最初に疑います。フレームワークやツールが自前で環境変数を読んでいる場合は、SDK と同じ名前・同じ優先順位とは限らないので、そのツールのドキュメントを確認してください。Codex CLI は独自の設定(openai_base_url と model_providers)を使うので、Codex カスタムプロバイダー設定:APIキーとBase URLの選び方を参照してください。
OpenAI のリージョン別エンドポイントは、URL を直接書くより data_residency(Node は dataResidency)で指定します。SDK に登録されている値は us、eu、ae で、それぞれ https://us.api.openai.com/v1 のような URL に展開されます。base_url と同時に渡すとクライアントの生成時点でエラーになります。自分のアカウントでそのリージョンを使えるかどうかは、SDK の一覧からは分かりません。
呼び出しごとに送信先を変えたい場合は、client.with_options(base_url=...)(Node は client.withOptions({ baseURL }))が使えます。変わるのはその呼び出しだけで、クライアント本体の base はそのまま残ります。
引数で指定する最小の例として、Gemini API の OpenAI 互換エンドポイントに向けるコードを示します。
import os
from openai import OpenAI
client = OpenAI(
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
api_key=os.environ["GEMINI_API_KEY"],
)
print(client.base_url) # 実際に使われる base を表示import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
apiKey: process.env.GEMINI_API_KEY,
});
console.log(client.baseURL);base_url にはどこまで書くか
SDK は base の後ろに chat/completions や responses といった相対パスをつなげて、1本の URL にします(Python は base の末尾にスラッシュを補います)。したがって、接続先のドキュメントに載っている完全な URL から、末尾のエンドポイント名(/chat/completions、/responses、/embeddings など)を取り除いた残りが、そのまま base_url になります。
ローカルのエコーサーバーに chat.completions.create() を送ったとき、サーバーが受け取ったパスは Python と Node で同じでした。
| base_url に書いた値 | サーバーが受け取ったパス | 判定 |
|---|---|---|
http://[::1]:8765 | /chat/completions | /v1 配下で待ち受けるサーバーでは 404 |
http://[::1]:8765/v1 | /v1/chat/completions | 正しい |
http://[::1]:8765/v1/ | /v1/chat/completions | 正しい。スラッシュは二重にならない |
http://[::1]:8765/v1/chat/completions | /v1/chat/completions/chat/completions | エンドポイント名が二重になり 404 |
末尾のスラッシュの有無は結果に影響しません。影響するのは、途中のパスを書き漏らすか、エンドポイント名まで書いてしまうかの2点です。

接続先ごとの書き方は次のとおりです。キーと model に渡す値も接続先に合わせて変わるので、base_url だけを差し替えて終わりにはなりません。
| 接続先 | base_url | API キー | model に渡す値 | 注意点 |
|---|---|---|---|---|
| OpenAI(既定) | https://api.openai.com/v1 | OpenAI の API キー | OpenAI のモデル ID | 何も指定しなければこれ |
| OpenAI のリージョン別 | data_residency で us・eu・ae を指定 | OpenAI の API キー | OpenAI のモデル ID | base_url と併用不可 |
| Azure OpenAI(v1 API) | https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1/ | Azure OpenAI の API キー、または Entra ID のトークンプロバイダー | デプロイ名 | OpenAI() を使い、api-version は不要 |
| Gemini API の OpenAI 互換 | https://generativelanguage.googleapis.com/v1beta/openai/ | Gemini API キー | Gemini のモデル ID | ライブラリ対応はベータ版。Responses API の記載なし |
| OpenAI 互換ゲートウェイ(例:LaoZhang API) | https://api.laozhang.ai/v1 | ゲートウェイの API キー | ゲートウェイのモデル一覧にある ID | Responses は GPT-6 系のみ明記 |
| ローカル・自前のサーバー | 互換エンドポイントの URL から末尾のエンドポイント名を除いたもの | サーバーの設定による | サーバーが読み込んでいるモデル名 | Cursor からは直接届かない(後述) |
Azure OpenAI は、Microsoft Learn の v1 API の説明(2026年5月13日更新)で、エンドポイントに /openai/v1 を付けて base_url に渡すよう案内されています。*.services.ai.azure.com/openai/v1/ の形も使え、OPENAI_BASE_URL と OPENAI_API_KEY を設定すれば引数なしの OpenAI() でも動きます。SDK には従来の AzureOpenAI(azure_endpoint=..., api_version=...) も残っていますが、こちらは別の方式なので、v1 の base_url と混ぜないようにします。v1 の GA 版が対応するのは機能の一部だと同じページに書かれています。Azure で 429 が出る場合は Azure OpenAI の TPM 制限を切り分ける:使用量が少なくても出る 429 の対処法が参考になります。
Gemini API の互換ドキュメント(2026年9月2日更新)は、コードを3行更新すれば OpenAI のライブラリから使えると説明しています。その3行は API キー、base_url、モデル名のことで、動く範囲はドキュメントに載っている機能(Chat Completions、ストリーミング、関数呼び出し、構造化出力、埋め込み、画像、バッチなど)に限られます。
LaoZhang API のドキュメントでは、OpenAI SDK 用の base_url は https://api.laozhang.ai/v1 で、Google Gen AI SDK と Anthropic SDK はホスト直下の https://api.laozhang.ai を使うとされています。同じゲートウェイでも SDK によって base の書き方が変わる、という点でも分かりやすい例です。2026年9月のお知らせで Chat Completions と Responses の両方から呼べると明記されているのは GPT-6 Astra、Sol、Luna の3モデルで、それ以外のモデルで Responses を使えるかは各モデルの説明を確認してください。
Chat Completions と Responses:互換の範囲を確かめる
client.responses.create() は POST {base}/responses に送られます。base_url が正しくても、接続先がこのパスを実装していなければ 404 です。「OpenAI 互換」と書かれていても、どの API までが互換なのかはサービスごとに違います。
2025年11月には、OpenAI のコミュニティフォーラムで、Gemini の互換 base_url に対して responses.create() を呼んだら 404 になったという報告がありました。そこに付いた利用者の回答は「他社は一般に Chat Completions の互換だけを提供している」というものです。Gemini のドキュメントにも Responses API の記載はありません。記載がないことは「絶対に動かない」という意味ではありませんが、前提にはできません。
一方、Azure OpenAI の v1 API のサンプルは responses.create() を使っています。判断の手順は単純で、接続先のドキュメントに /responses が載っていれば Responses を、載っていなければ chat.completions.create() を使います。Responses を前提にしたエージェントのコードを互換サービスに移すなら、先にその対応を確かめてください。Assistants API のコードを移している途中なら、Assistants APIからResponses APIへ移行する実装手順【停止日・状態・ロールバック】も合わせて確認できます。
プロキシと base_url は別の設定
base_url は「どのサービスの API を呼ぶか」という API の起点です。HTTP プロキシは「そのリクエストがどの経路を通るか」という通信の経路です。日本語ではどちらも「プロキシ」と呼ばれがちなので、手元の設定がどちらなのかを先に決めます。
- 相手から専用の URL と API キーを渡され、その URL に API として送る場合は、ゲートウェイです。base_url に書きます。
http://proxy.example.com:8080のようなアドレスを経由して api.openai.com などに届く場合は、HTTP プロキシです。HTTP クライアントの設定に書きます。
HTTP プロキシのアドレスを base_url に入れると、SDK はそこへ /chat/completions を API リクエストとして直接送るため、意図した経路にはなりません。

Python(3.19.2)では、README にあるとおり http_client にプロキシを渡します。古いバージョンではクラス名が DefaultHttpxClient なので、インストール済みのバージョンの README を確認してください。
from openai import OpenAI, DefaultHttpx2Client
client = OpenAI(
base_url="https://api.openai.com/v1", # API の起点
http_client=DefaultHttpx2Client(proxy="http://proxy.example.com:8080"), # 経路
)Node では undici の fetch と ProxyAgent を fetchOptions の dispatcher に渡します。
import OpenAI from 'openai';
import { fetch, ProxyAgent } from 'undici';
const client = new OpenAI({
fetch,
fetchOptions: { dispatcher: new ProxyAgent('http://proxy.example.com:8080') },
});応答がまったく返らずに APIConnectionError になる場合は、リクエストが接続先に届いていないので、OpenAI API 接続エラー: APIConnectionError はまず経路から切り分けるの手順が使えます。
実際の送信先を確かめる
設定を変えたら、リクエストが本当にどこへ向かっているかを確認します。軽い順に次の4つです。
- クライアントを作った直後に
print(client.base_url)(Node はconsole.log(client.baseURL))で、最終的な base を表示する。 - 環境変数
OPENAI_LOG=debugで SDK のログを出す。Node ではクライアントのlogLevel: 'debug'でも指定でき、環境変数より優先されます。ログの形式はバージョンによって変わります。 - ローカルのエコーサーバーに向けて、SDK が組み立てたパスをそのまま見る。
- 接続先のダッシュボードやログに、そのリクエストが記録されているかを見る。記録がなければ、リクエストはそこに届いていません。
3 は、本物の接続先を呼ばずにパスの組み立てだけを確かめる方法です。Python の標準ライブラリだけで動きます。
# echo_server.py
import json
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer
class Echo(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
model = json.loads(body or b"{}").get("model")
print(f"{self.command} {self.path} model={model}", flush=True)
reply = {
"id": "chatcmpl-echo", "object": "chat.completion", "created": 0,
"model": model or "echo",
"choices": [{"index": 0, "finish_reason": "stop",
"message": {"role": "assistant", "content": self.path}}],
}
data = json.dumps(reply).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def log_message(self, *args):
pass
class V6Server(HTTPServer):
address_family = socket.AF_INET6
V6Server(("::1", 8765), Echo).serve_forever()# check.py
from openai import OpenAI
client = OpenAI(api_key="sk-dummy", max_retries=0)
print("base_url:", client.base_url)
r = client.chat.completions.create(
model="test-model",
messages=[{"role": "user", "content": "ping"}],
)
print("server saw:", r.choices[0].message.content)別のターミナルで python echo_server.py を起動し(IPv6 のループバックアドレス ::1 で待ち受けます)、OPENAI_BASE_URL=http://[::1]:8765/v1 python check.py を実行すると、server saw: /v1/chat/completions と表示されます。OPENAI_BASE_URL に /chat/completions まで含めると /v1/chat/completions/chat/completions が表示されます。本番のコードと同じ方法で base_url を渡してから試すと、フレームワーク経由の設定が効いているかも分かります。
キーはダミーで構いません。base_url の向け先には、Authorization ヘッダーで API キーがそのまま届きます。見知らぬサーバーやテスト用のサーバーに本物のキーを送らないようにしてください。
症状から直す場所を決める
エラーの種類と、実際に送られたパスを組み合わせると、直すべき層が決まります。
| 症状 | 送信先・パスの状態 | 直す場所 |
|---|---|---|
404、パスが /v1/chat/completions/chat/completions | base にエンドポイント名まで入っている | base_url から /chat/completions を削る |
404、パスが /chat/completions(/v1 などがない) | base に途中のパスが足りない | /v1、/openai/v1、/v1beta/openai などを足す |
/responses だけが 404 | 接続先が Responses API を実装していない | chat.completions.create() に切り替えるか、対応している接続先を使う |
他社のキーなのに OpenAI の Incorrect API key provided | リクエストが api.openai.com に届いている | 旧名の環境変数、Node の空文字、環境変数が実行プロセスに渡っていない、などを確認 |
| model not found、存在しないモデル | 接続先には届いている | model を接続先の ID に直す。Azure はデプロイ名 |
APIConnectionError、応答なし | 接続先に届いていない | Python の空文字の OPENAI_BASE_URL、ホストとポート、プロキシ、ファイアウォール |
| 429 | 接続先には届いている | base_url ではなく、接続先のレート制限とクォータ |
401 が返っていて送信先が正しい場合は、キー自体の問題です。OpenAI に直接つなぐ場合にどのキーや ID が必要なのかは、OpenAI API Key と Organization ID: 2026年に本当に必要なものにまとまっています。
Cursor の "Override OpenAI Base URL"
Cursor では Settings > Models の API キー欄に "Override OpenAI Base URL" という項目があり、OpenAI 互換の別サービスにチャットを向けられます。Cursor の公式ヘルプページは自分の API キーの登録方法を説明していますが、この項目の挙動は載っていません。以下は2026年8〜9月の Cursor スタッフによるフォーラム回答に基づくもので、一部は修正時期が未定の既知の問題です。
設定は次の順で行います(2026年8月25日・9月10日のスタッフ回答)。
- OpenAI API Key の欄に接続先のキーを貼り、"Use OpenAI API key" のトグルをオンにする。キーを貼ることとトグルを入れることは別の操作で、トグルがオフのあいだは base URL も使われません。
- Override OpenAI Base URL をオンにする。オンにした直後は
https://api.openai.com/v1が入っているので、接続先の base(例:Fireworks ならhttps://api.fireworks.ai/inference/v1)に書き換える。 - Enter を押すか欄の外をクリックして保存し、Settings を閉じて開き直し、値が残っていることを確かめる。トグルをオフにしてからオンに戻すと、既定値にリセットされます。
- "+ Add Custom Model" で接続先の実際のモデル ID を追加し、新しいチャットで送る。
うまくいかないときは、症状から次のように切り分けます。
| 症状 | スタッフ回答での原因 | 対処 |
|---|---|---|
他社のキーが OpenAI に Incorrect API key provided で拒否される | base URL が既定値の https://api.openai.com/v1 のまま(2026年9月10日) | 手順2〜3をやり直す |
| Unauthorized User API key が出るが、接続先のログに記録がない | モデル ID が Cursor の管理するモデル名(例:kimi-k3、kimi-k2.6)と重なり、base URL を使わずに Cursor 側で処理された(2026年8月19日・9月10日) | 名前が重ならない、接続先の実際のモデル ID を使う |
| BAD_MODEL_NAME、AI Model Not Found | "Use OpenAI API key" がオフで、キーが付かずに送られた(2026年8月25日) | トグルをオンにして新しいチャットを開く |
| Composer や Grok で "This model does not support custom API keys" | この設定は Claude と Gemini 以外のすべてのモデルに効き、Cursor 上で動くモデルは自分のキーを受け付けない(2026年9月23日) | Cursor のモデルを使うあいだは OpenAI のキーをオフにする |
| 手元の PC のローカルアドレスや社内ネットワークのアドレスに届かない | リクエストは Cursor のサーバーでプロンプトを組み立ててから送られるため、公開された HTTPS が必要(2026年2月22日・4月2日・5月22日) | ngrok や Cloudflare Tunnel で公開 URL を作る |
オンにしているあいだは、内蔵のモデル一覧から選んだ OpenAI 系のモデルも含めて、Claude と Gemini 以外のリクエストがこの URL に向かいます。自前のモデルと Cursor のモデルを同時に使い分ける機能は検討中で、時期は示されていません(2026年8月21日・9月15日)。今のところは、使うモデルに合わせてトグルを切り替えるしかありません。
トンネルでローカルのモデルサーバーを公開すると、その URL を知っていれば誰でも届きます。公開する前に、サーバー側で認証を有効にしておくのが安全です。
公式ヘルプページには、ほかにも判断に関わる条件があります。自分のキーが使われるのはチャットモデルだけで、Tab 補完は Cursor のモデルのままです。自分のキーを使う場合、Cursor のゼロデータ保持ポリシーは適用されません。Team と Enterprise では、自分のキーで送ったリクエストにも Cursor Token 料金(入力・出力・キャッシュを含め100万トークンあたり$0.25)がかかります。また、自分のキーでは接続先のレート制限とコンテキスト上限がそのまま効くため、長い会話では制限を超えやすくなります。
なお、Cursor 3.15.6 では OpenAI API Key と Override OpenAI Base URL の入力欄がクリックで選べない不具合がありました。スタッフはトグルを入れてから Tab キーで欄に移動する回避策を案内し、修正は後続の 3.15 系の更新で配布されると告知しています。





