PythonからChatGPTを使うには、OpenAIの公式ライブラリopenaiでAPIを呼び出します。2026年10月時点の最新は3.28.0(10月9日公開)で、2026年8月の3.0.0から通信部分が入れ替わりました。さらに、古いサンプルに多いgpt-3.5-turboとgpt-4は2026年10月23日にAPIから外れます。この記事では、openai 3.28.0とローカルのモックAPIで実行を確認したコードを使って、最初の1回から会話の継続、JSONでの受け取り、タイムアウトとリトライ、業務システムへの組み込みまでを順に説明します。
まとめ:PythonでChatGPT APIを使う要点
- ChatGPTの画面でPythonを動かすデータ分析機能と、PythonからAPIを呼ぶ連携は別物。外部システムとつなぐなら後者を使う。
- 導入は
pip install openai。3系はPython 3.10以上が必要で、APIキーは環境変数OPENAI_API_KEYに置く。 - 新規の実装は
client.responses.create()で書く。既存のchat.completionsもサポートは続いている。 gpt-3.5-turbo・gpt-4は2026年10月23日に停止する。モデルIDは環境変数で差し替えられる形にしておく。- 既定の待ち時間は接続5秒・読み取り600秒、自動リトライは2回。画面から呼ぶ処理では
timeoutと処理全体の期限を別に決める。 - 大量件数は
AsyncOpenAIと同時実行数の上限で流す。429が続く場合はエラー内容を確認し、流量制限なら送信量を下げ、課金・利用上限なら該当する設定を見直す。
ChatGPTとPythonの組み合わせ方:API連携と画面上のPython実行の違い
「ChatGPT Python」で調べると、2種類の使い方が混ざって出てきます。1つはChatGPTの画面にファイルを渡し、ChatGPT自身にPythonを書かせて実行させる使い方です。OpenAIのヘルプによると、ChatGPTのデータ分析はJupyterノートブック環境でPythonを実行しますが、この環境から外部のWebやAPIへは要求を送れません。もう1つが、自分のPythonプログラムからOpenAI APIを呼び出す使い方で、本記事が扱うのはこちらです。
| 比較軸 | ChatGPT上のPython実行 | PythonからAPIを呼ぶ |
|---|---|---|
| コードを書く人 | ChatGPT | 自分 |
| 動く場所 | ChatGPTの隔離環境 | 自社のPCやサーバー |
| 外部システムとの接続 | 不可 | 可能(DB・社内API・チャット) |
| 課金 | ChatGPTのプラン | APIの従量課金(別建て) |
| 向く用途 | 手元のExcelやCSVの単発分析 | 定期処理・業務システムへの組み込み |
月に1回、手元のCSVを集計してグラフにする程度なら、APIを使うより画面上のデータ分析の方が早く終わります。問い合わせの自動分類や、毎晩のバッチで議事録を要約するといった、人が画面に張り付かない処理はAPIの出番です。
openai 3系のインストールとAPIキーの準備
準備はライブラリの導入、APIキーの発行、課金の設定の3つです。APIキーを設定したうえで、まず最小リクエスト例を実行します。以降の同期コード例は、そこで定義するclientとMODELを引き継いで実行してください。非同期の例は別のスクリプトとして実行します。
pip install openai とPython 3.10以上の確認
パッケージ名はopenaiです。PyPIの登録情報では、3系はPython 3.10以上を要求します。3.9以前の環境では最新版が入らないので、先にPythonを上げます。
python3 --version
python3 -m pip install --upgrade openai
python3 -c "import openai; print(openai.__version__)"
本記事のコードは3.28.0で確認しています。本番環境ではrequirements.txtにopenai==3.28.0のように版を固定し、上げるときは変更履歴で使っている機能に影響がないかを確かめます。
APIキーの発行とクレジットの購入
APIキーはOpenAIのAPIプラットフォームのダッシュボードで発行します。APIの利用料はChatGPTの有料プランとは別の請求で、ChatGPT Plusの料金にAPI利用料は含まれません。前払いの設定では最低5ドルからクレジットを買え、購入分は1年で失効します。購入画面では自動チャージ(auto-reload)が初期状態でオンになっているため、動作確認だけで追加購入を避けたいならオフにしてから確定します。
環境変数 OPENAI_API_KEY へのキー設定
SDKは環境変数OPENAI_API_KEYを自動で読み込みます。キーをソースコードに書くと、Gitへ誤って上げたときにそのまま不正利用されるため、環境変数に置きます。
# macOS・Linux
export OPENAI_API_KEY="sk-..."
# Windows PowerShell(そのウィンドウだけ有効)
$env:OPENAI_API_KEY = "sk-..."
Responses APIの最小リクエストと応答取得
OpenAIは移行ガイドで、新しい開発にはResponses APIを勧め、Chat Completionsもサポートを続けると明記しています。これから書くならResponses API、既存のコードはChat Completionsのままで構いません。
client.responses.create による応答取得
指示はinstructions、質問はinputに渡し、生成された文章はoutput_textで取り出します。
import os
from openai import OpenAI
client = OpenAI() # 環境変数 OPENAI_API_KEY を読み込む
MODEL = os.environ.get("OPENAI_MODEL", "gpt-6-luna")
response = client.responses.create(
model=MODEL,
instructions="あなたは社内ヘルプデスクの担当者です。簡潔に答えてください。",
input="経費精算の締め日を確認する方法を3行で教えてください。",
)
print(response.output_text)
Chat Completionsのmessages指定と応答取得
既存システムがChat Completionsで書かれているなら、役割ごとの発話をmessagesに並べ、本文をchoices[0].message.contentから取ります。
completion = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": "あなたは社内ヘルプデスクの担当者です。"},
{"role": "user", "content": "経費精算の締め日を確認する方法は?"},
],
)
print(completion.choices[0].message.content)
Responses APIとの細かな違いと移行の判断はOpenAI Responses APIとは|Chat Completions APIとの違いと移行判断で扱っています。
モデルIDの選び方と料金
OpenAIの最新モデル案内では、2026年10月時点の現行はGPT-6系の3種類です。単価は100万トークンあたりのStandard・短文脈の料金で、入力はキャッシュ未使用時の値です。長文脈やキャッシュの利用、Batchなどの処理モードによって適用料金が変わります。
| モデルID | 入力 | 出力 | 向く処理 |
|---|---|---|---|
| gpt-6-luna | $0.10 | $0.50 | 分類・要約などの大量処理 |
| gpt-6.1-sol | $2 | $10 | コード生成・複雑な業務文書 |
| gpt-6-astra | $10 | $50 | 多段の判断を伴うエージェント |
最初はgpt-6-lunaで組み、品質が足りない処理だけ上位へ回すと、費用が数十倍に跳ねるのを防げます。3モデルの使い分けはGPT-6 Solとは?6.1 Solの変更点とLuna・Astraの使い分け【2026年10月】で詳しく比べています。上のコードでモデルIDを環境変数OPENAI_MODELから読むのは、次の章の停止に備えるためです。
古いサンプルが動かない3つの原因と直し方
検索で見つかる「ChatGPT API Python」のサンプルには、今は動かないものが残っています。原因はほぼ次の3つに分かれます。
| 症状 | 原因 | 直し方 |
|---|---|---|
| ChatCompletionでAPIRemovedInV1 | v1.0より前の書き方 | client = OpenAI() 方式に書き換える |
| モデルが見つからないエラー | 旧モデルのAPI停止 | モデルIDを現行へ差し替える |
| 証明書の検証エラー・httpxが無い | 3.0.0の通信部分の変更 | OSに社内CAを入れる・httpxを直接入れる |
openai.ChatCompletion.create のAPIRemovedInV1と移行先
openai.ChatCompletion.create()は、2023年11月6日公開の1.0.0より前の書き方です。1.0.0以降はクライアントを作って呼ぶ記法に移り、旧メソッドを呼ぶとAPIRemovedInV1という例外が出ます。前の章のコードに置き換えれば動きます。旧メソッドと新メソッドの対応表はOpenAI Python SDKの使い方|インストールから最新クライアント・Responses APIまでにまとめています。
gpt-3.5-turbo・gpt-4の停止日と移行先
OpenAIの廃止予定一覧では、2026年4月22日の告知で次のモデルが10月23日に停止します。公式の代替先も載っています。
| 停止するモデルID | 公式の代替先 |
|---|---|
| gpt-3.5-turbo(0125含む) | gpt-5.6-terra |
| gpt-4(0613含む) | gpt-5.6-sol |
| gpt-4-turbo(2024-04-09含む) | gpt-5.6-sol |
| gpt-4o-2024-05-13 | gpt-5.6-sol |
代替先のgpt-5.6系についても、廃止予定一覧で告知の有無を定期的に確認します。コードにモデルIDを直書きしている箇所は、停止日を待たずに環境変数か設定ファイルへ移しておくと、コードを編集せずにモデルIDを変更できます。ただし、設定の反映には再起動や再デプロイが必要な構成もあり、移行先での動作確認も必要です。
3.0.0で起きる証明書エラーとhttpxの欠落
2026年8月12日の3.0.0では、通信ライブラリがHTTPX2に切り替わりました。影響は2つあります。1つは、openaiを入れてもhttpxが一緒に入らなくなったことです。自分のコードでimport httpxしているなら、httpxを依存に明記します。もう1つは、証明書の検証にcertifiの同梱データでなくOSの証明書ストアを使うようになったことです。社内プロキシがTLSを中継する環境や、CA証明書を入れていない最小構成のコンテナでは、2系で通っていた通信が検証エラーになります。社内CAをOSに登録するか、次の環境変数で証明書の場所を指定します。
export SSL_CERT_FILE=/path/to/corp-ca-bundle.pem
会話を続ける:履歴の持たせ方3通り
APIは1回ごとに独立しているため、何もしなければ前の質問を覚えていません。チャットボットのように文脈を続けるには、次の3通りのどれかで履歴を渡します。
inputによる会話履歴の自前管理
やり取りを自分のリストに保存し、毎回まとめて送る方法です。どの発言を残すかを自分で決められるので、古い発言を削って入力を短く保つ制御もできます。
history = [] # 単一利用者向けの例。Webアプリでは利用者・会話ごとに分離する
def ask(text: str) -> str:
history.append({"role": "user", "content": text})
res = client.responses.create(model=MODEL, input=history)
history.append({"role": "assistant", "content": res.output_text})
return res.output_text
print(ask("Pythonの辞書を値で並べ替える方法は?"))
print(ask("降順にするには?")) # 前の質問を踏まえて答える
previous_response_id による前回応答の引き継ぎ
前回の応答IDを渡すと、OpenAI側に保存された会話の文脈が引き継がれます。ただし、前回のトップレベルのinstructionsは引き継がれないため、継続する指示は毎回渡します。会話状態のガイドによると、応答は既定で30日間保存され、つないだ過去分の入力もすべて入力トークンとして課金されます。履歴を自前で持たなくてよい代わりに、長い会話ほど1回あたりの費用が増えます。
first = client.responses.create(model=MODEL, input="Pythonの辞書を値で並べ替える方法は?")
second = client.responses.create(
model=MODEL,
previous_response_id=first.id, # 前回の応答IDを渡すだけで文脈が続く
input="降順にするには?",
)
print(second.output_text)
Conversations APIによる会話単位の保存
ユーザーごとの会話を長く保ちたい場合は、会話オブジェクトを作り、そのIDを毎回渡します。30日の保存期限を気にせず、会話単位で履歴を管理できます。
conv = client.conversations.create()
reply = client.responses.create(model=MODEL, conversation=conv.id, input="こんにちは")
print(reply.output_text)
社内の問い合わせ対応のように、会話をDBに残して監査したいなら1つ目の自前管理が向きます。履歴の引き継ぎをOpenAI側に任せるなら、2つ目か3つ目で実装が短くなります。ただし、1つ目の自前管理でもResponsesの応答は既定で保存されます。応答オブジェクトの保存を無効にする場合はstore=Falseを指定し、別途データ保持ポリシーも確認してください。
結果をJSONで受け取る:responses.parseとPydantic
業務システムにつなぐと、応答を文章でなく決まった形のデータで受け取りたくなります。responses.parseにPydanticのクラスを渡すと、正常に完了した構造化出力を型付きで取得できます。拒否や未完了の応答では、結果を利用する前に状態の確認が必要です。
from typing import Literal
from pydantic import BaseModel
class Inquiry(BaseModel):
category: str # 請求・障害・契約 など
urgency: Literal[1, 2, 3, 4, 5] # 緊急度。範囲外の値はスキーマで弾く
summary: str
res = client.responses.parse(
model=MODEL,
instructions="問い合わせ文を分類し、緊急度を1〜5で付けてください。",
input="先月分の請求書が届いていません。至急再発行をお願いします。",
text_format=Inquiry,
)
item = res.output_parsed
if res.status != "completed" or item is None:
raise RuntimeError("構造化出力を取得できませんでした。応答のstatus・refusal・incomplete_detailsを確認してください。")
print(item.category, item.urgency, item.summary)
「JSONで答えて」とプロンプトに書くだけの方法は、項目の抜けや余計な前置きが混ざり、取り込み側で例外になります。データベースや別システムへ流す出力は、最初からスキーマ指定で受け取ってください。JSONモードとの違いや、スキーマ指定を見送る条件はLLM構造化出力とは?JSON Schemaで出力形式を保証する仕組みと実装・見送り条件を解説【2026年版】で解説しています。
エラー処理・タイムアウト・リトライの設定
API呼び出しは、流量の上限や通信断によって失敗することがあります。業務で動かすコードには、失敗の種類ごとの分岐と時間の上限を入れます。
SDK例外の種類と捕捉順序
APIとの接続失敗やHTTPエラーに対応する例外はopenai.APIErrorを継承しています。APITimeoutErrorはAPIConnectionErrorの子クラスなので、先に書かないと接続エラー側に吸い込まれます。
import openai
from openai import OpenAI
client = OpenAI(timeout=30.0, max_retries=3) # 既定は読み取り600秒・再送2回
try:
res = client.responses.create(model=MODEL, input="次の議事録を要約してください:本日の会議では、請求書の再発行を田中が担当し、金曜日までに送付することを決めました。")
print(res.output_text)
except openai.APITimeoutError:
print("時間切れ:timeoutを見直すか、処理を非同期に回す")
except openai.APIConnectionError as e:
print("接続できない:プロキシ・証明書・DNSを確認", e.__cause__)
except openai.RateLimitError as e:
print("429:流量か残高の上限", e.request_id)
except openai.AuthenticationError:
print("401:APIキーが無効")
except openai.BadRequestError as e:
print("400:モデル名やパラメータの誤り", e.message)
except openai.APIStatusError as e:
print("その他のHTTPエラー", e.status_code, e.request_id)
401と400はコードや設定の誤りで、何度送り直しても直りません。流量制限による429や一時的な接続エラー・500番台は、時間を置くと通る可能性があります。429が支出・利用上限による場合は、例外のcodeとmessageを確認し、該当する上限に対処します。問い合わせ窓口に調査を頼むときはrequest_idを添えると話が早く進みます。
タイムアウトと自動リトライの既定値・変更設定
公式READMEのリトライとタイムアウトの節によると、既定のタイムアウトは10分で、接続エラー・408・409・429・500以上は2回まで自動で再送されます。手元で3.28.0を確かめると、接続の確立だけは5秒、読み取りは600秒が既定でした。画面のボタンから呼ぶ処理で10分待たせると、利用者には「止まった」ようにしか見えません。画面から呼ぶ処理はtimeout=30.0などで通信段階ごとの待機時間を設定します。ただし、再送と待機を含む処理全体が30秒以内に終わる保証ではありません。全体の期限とリトライ回数も別に設計し、夜間バッチはmax_retriesを増やす、というように処理の性格で分けます。なおmax_retries=3にすると、429が返り続けた場合に最初の1回と再送3回の計4回送信してから例外になります。
ストリーミング受信中の失敗と手動再試行
同じREADMEは、ストリームの受信途中の失敗を自動リトライしないと明記しています。途中まで表示した文章が二重に出るのを避けるためです。業務実装では受信ループをtryで囲み、途中まで受け取った文字列を残して、再試行するか打ち切るかを決めます。次章のコードは正常時の逐次表示だけを示す最小例で、この例外処理は省略しています。
ストリーミングによる応答の逐次表示
stream=Trueを付けると、生成途中の文章がイベントとして順に届きます。本文の差分はresponse.output_text.deltaという種類のイベントに入っているので、それだけを拾って表示します。
stream = client.responses.create(
model=MODEL,
input="PythonでCSVを読み込む方法を説明してください。",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
print()
回答の完成を待たずに、受信した本文の差分から表示できます。ただし、最初の本文が届くまでの時間は、モデルの推論や混雑状況などによって変わります。逆に、結果をまとめてDBへ書くだけのバッチ処理では、ストリーミングにする利点はありません。
業務システムにChatGPTを組み込むときの判断
スクリプトが1回動くことと、業務で毎日動き続けることの間には、流量・鍵・費用の3つの壁があります。
AsyncOpenAIによる大量処理と同時実行数の制御
数千件を一気に並列で投げると、429が連発してかえって遅くなります。非同期版のAsyncOpenAIとasyncio.Semaphoreで同時に投げる件数に上限を付けます。
import asyncio
import os
from openai import AsyncOpenAI
client = AsyncOpenAI(max_retries=5)
MODEL = os.environ.get("OPENAI_MODEL", "gpt-6-luna")
limit = asyncio.Semaphore(5) # 同時に投げるのは5件まで
async def summarize(text: str) -> str:
async with limit:
res = await client.responses.create(model=MODEL, input=f"50字で要約してください:{text}")
return res.output_text
async def main(texts: list[str]) -> list[str]:
return await asyncio.gather(*(summarize(t) for t in texts))
results = asyncio.run(main(["議事録A", "議事録B", "議事録C"]))
print(results)
同時実行数はダッシュボードの利用上限と応答時間を見て調整します。Semaphoreが制限するのは同時実行数だけなので、リクエスト数やトークン数の時間当たり上限には、送信間隔の制御も必要です。完了まで最大24時間の処理枠を許容でき、期限内に終わらないリクエストへの対応も用意できるなら、gpt-6-lunaのモデルページにあるとおりBatchの料金は標準の半額なので、同期で回すより安く済みます。
APIキーのサーバー側管理と用途別の分離
ブラウザで動くJavaScriptにAPIキーを埋め込む構成は採用しないでください。キーが利用者に丸見えになり、第三者に使われた分まで請求されます。APIの呼び出しは必ずPythonのサーバー側に置き、画面は自社のサーバーを経由させます。キーは本番・検証・バッチで分けておくと、漏えいや暴走が起きたときに1本だけ止められます。
APIを使わない方がよい場面
APIで組むべきでないのは、担当者が月に数回だけ手元のデータを分析する用途です。この場合はChatGPTの画面上のデータ分析で十分で、APIキーの管理や例外処理を書く手間の方が大きくなります。反対に、社内の基幹システムやチャットツールと結果をやり取りする、毎日決まった件数を処理する、といった要件があるならAPIで組みます。既存システムとの接続設計まで含めて任せたい場合は、イッソウのAPI開発・システム連携でも対応しています。料金の試算や内製と外注の線引きはChatGPT APIとは?できること・料金の仕組み・使い方と企業導入の判断基準を解説を参照してください。
よくある質問
openai.ChatCompletion.create() が動かないのはなぜですか?
1.0.0(2023年11月)より前の書き方だからです。client = OpenAI()でクライアントを作り、client.responses.create()かclient.chat.completions.create()を呼ぶ形に書き換えてください。あわせて、モデルIDがgpt-3.5-turboやgpt-4なら2026年10月23日以降は停止するため差し替えが必要です。
ChatGPT APIは無料で使えますか?
無料では使えないと考えてください。APIはChatGPTの有料プランとは別の請求で、前払いのクレジットは最低5ドルから購入でき、購入分は1年で失効します。gpt-6-lunaの短文脈・Standard料金は、100万トークンあたり入力0.10ドル、出力0.50ドルです。確認回数と入出力量を抑え、Usage画面で実際の費用を確認してください。
PythonからのAPI呼び出しがタイムアウトになるのはなぜですか?
接続・読み取り・書き込み・接続プールのいずれかで、待ち時間の上限を超えるとAPITimeoutErrorになります。既定は接続の確立が5秒、読み取りなどが600秒なので、10分より早く切れるなら、自分で指定したtimeoutか、社内プロキシなど途中の機器の制限を疑います。長い文章を生成させる処理はstream=Trueで逐次受信すると、1回の読み取りで待つ時間が短くなります。それでも通信は切れることがあるため、再試行の処理は残します。
API呼び出しで409エラーが返るのはなぜですか?
409(Conflict)は、同じ対象への処理が競合したときに返るステータスで、SDKはConflictErrorとして扱い、既定で2回まで自動で再送します。再送後も続くなら、例外のmessageとrequest_idを記録し、どのリクエストが競合したかを確かめます。なお、ChatGPTの画面に出る「リクエストに問題があります」はAPIとは別のサービス側の表示です。
requestsで直接APIを叩くのとopenaiライブラリのどちらがよいですか?
特別な理由がなければopenaiライブラリを使います。認証ヘッダーの組み立て、リトライ、ストリーミングの解析、Pydanticでの型付けまで済ませてくれるためです。requestsで自作する選択肢もあります。たとえば、requestsが既存の依存に含まれ、追加のSDKを導入できない環境や、社内共通のHTTP通信基盤に統一する場合、SDK未対応の機能を試す場合です。