openai
openai is the official Python SDK for the OpenAI REST API, generated from the OpenAPI specification with Stainless. It gives you synchronous and asynchronous clients built on httpx, typed request params (TypedDicts) and typed responses (Pydantic models), and covers the Responses API, Chat Completions, streaming, the Realtime API over WebSockets, file uploads, fine-tuning, and webhook verification. Retries, timeouts, and pagination are handled by the client, and it requires Python 3.10 or newer.
If you call OpenAI from Python, use the official SDK; the typed clients, retries, and streaming support are worth it. Just do not expect snippets you found last year to run unchanged, and pin your version.
Use it if
- You call OpenAI models from Python and want typed requests, automatic retries, and pagination instead of hand-rolled HTTP
- You need streaming responses or the Realtime API, which the SDK wires up over SSE and WebSockets for you
- You receive OpenAI webhooks and want signature verification via client.webhooks.unwrap() rather than doing HMAC checks yourself
- You want provider portability: this SDK is OpenAI-shaped, and moving to Anthropic or local models means rewriting call sites or adding a layer like litellm
- You are stuck on Python 3.9 or older: the library requires Python 3.10+
- Your whole use is one endpoint in a constrained environment: the SDK brings httpx, pydantic, and friends where a single POST with any HTTP client would do
- You expect old tutorials to work: the 2023 v1 rewrite broke every openai.ChatCompletion-style snippet, and the platform's shift from Chat Completions to Responses means online examples mix two APIs
Setup reality
pip install openai, set OPENAI_API_KEY, and the basic call works. The rough edges are legacy and defaults: an enormous amount of pre-2023 sample code (openai.ChatCompletion.create) no longer runs, so most debugging sessions for newcomers are really migration sessions. The default timeout is a surprising 10 minutes and failed requests are retried twice automatically, which can hide flakiness or double your latency if you do not tune max_retries and timeout. Async with aiohttp and the httpx2 mode are separate optional extras.
Patterns
Generate text with the Responses APIbasic-response
from openai import OpenAI
client = OpenAI() # reads OPENAI_API_KEY
response = client.responses.create(
model="gpt-5.5",
instructions="You are a terse coding assistant.",
input="How do I reverse a list in Python?",
)
print(response.output_text)Responses is the primary API now; output_text is the convenience accessor for the final text.
Use the Chat Completions APIchat-completions
from openai import OpenAI
client = OpenAI()
completion = client.chat.completions.create(
model="gpt-5.5",
messages=[
{"role": "developer", "content": "Talk like a pirate."},
{"role": "user", "content": "Explain generators."},
],
)
print(completion.choices[0].message.content)Chat Completions remains supported indefinitely, but new platform features tend to land on Responses first.
Stream a response as it generatesstream-response
from openai import OpenAI
client = OpenAI()
stream = client.responses.create(
model="gpt-5.5",
input="Write a one-sentence bedtime story about a unicorn.",
stream=True,
)
for event in stream:
print(event)Streaming yields typed SSE events, not raw text deltas; filter on event type before printing to users.
Call the API asynchronouslyasync-client
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def main() -> None:
response = await client.responses.create(
model="gpt-5.5", input="Explain asyncio in one paragraph."
)
print(response.output_text)
asyncio.run(main())The async client mirrors the sync API exactly; install the aiohttp extra and pass DefaultAioHttpClient for higher concurrency.
Send an image to the modelvision-image-input
import base64
from openai import OpenAI
client = OpenAI()
with open("photo.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode("utf-8")
response = client.responses.create(
model="gpt-5.5",
input=[{
"role": "user",
"content": [
{"type": "input_text", "text": "What is in this image?"},
{"type": "input_image", "image_url": f"data:image/png;base64,{b64}"},
],
}],
)image_url takes either an https URL or a data URL; base64 payloads count against request size limits.
Handle API errors by typeerror-handling
import openai
from openai import OpenAI
client = OpenAI()
try:
client.responses.create(model="gpt-5.5", input="hi")
except openai.APIConnectionError as e:
print("network problem:", e.__cause__)
except openai.RateLimitError:
print("429: back off")
except openai.APIStatusError as e:
print(e.status_code, e.response)All errors inherit from openai.APIError; catch APIStatusError to read the request_id of a failed call.
Configure or disable automatic retriesconfigure-retries
from openai import OpenAI
client = OpenAI(max_retries=0) # default is 2
# or per request:
client.with_options(max_retries=5).responses.create(
model="gpt-5.5", input="hello"
)Connection errors, 408, 409, 429, and 5xx are retried with exponential backoff by default; timeouts get retried too.
Set request timeoutsconfigure-timeout
import httpx
from openai import OpenAI
client = OpenAI(
timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
)
# or per request:
client.with_options(timeout=5.0).responses.create(
model="gpt-5.5", input="quick answer please"
)The default timeout is 10 minutes, which is almost never what a web request path wants.
Iterate a paginated list endpointauto-pagination
from openai import OpenAI
client = OpenAI()
all_jobs = []
for job in client.fine_tuning.jobs.list(limit=20):
all_jobs.append(job) # pages are fetched as neededIterating the list result fetches successive pages automatically; use .has_next_page() for manual control.
Upload a filefile-upload
from pathlib import Path
from openai import OpenAI
client = OpenAI()
client.files.create(
file=Path("input.jsonl"),
purpose="fine-tune",
)You can pass bytes, a PathLike, or a (filename, contents, media type) tuple; the async client reads files without blocking.
Verify and parse an OpenAI webhookverify-webhook
from openai import OpenAI
from flask import Flask, request
app = Flask(__name__)
client = OpenAI() # uses OPENAI_WEBHOOK_SECRET env var
@app.route("/webhook", methods=["POST"])
def webhook():
body = request.get_data(as_text=True)
try:
event = client.webhooks.unwrap(body, request.headers)
return "ok"
except Exception:
return "Invalid signature", 400Pass the raw body string, not parsed JSON; unwrap() verifies the signature before parsing and raises on mismatch.
Read response headers and request IDsraw-response-headers
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.with_raw_response.create(
messages=[{"role": "user", "content": "Say this is a test"}],
model="gpt-5.5",
)
print(response.headers.get("x-request-id"))
completion = response.parse()Every parsed object also exposes _request_id; log it when reporting problems to OpenAI support.