openai/openai-python

Public

mirrored from https://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.32.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

README.md

965lines · modeblame

08b8179aDavid Schnurr2 years ago1# OpenAI Python API library
3c6d4cd6Greg Brockman5 years ago2
db5c3504stainless-app[bot]11 months ago3<!-- prettier-ignore -->
4[![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/)
3c6d4cd6Greg Brockman5 years ago5
3f52ac84stainless-app[bot]7 months ago6The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.9+
08b8179aDavid Schnurr2 years ago7application. The library includes type definitions for all request params and response fields,
8and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
9
10It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/).
11
12## Documentation
13
2954945eRobert Craigie1 years ago14The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs/api-reference). The full API of this library can be found in [api.md](api.md).
3c6d4cd6Greg Brockman5 years ago15
16## Installation
17
18```sh
1879c97aStainless Bot2 years ago19# install from PyPI
6d217096Robert Craigie2 years ago20pip install openai
3c6d4cd6Greg Brockman5 years ago21```
22
08b8179aDavid Schnurr2 years ago23## Usage
24
986f3128Stainless Bot2 years ago25The full API of this library can be found in [api.md](api.md).
376dd199Logan Kilpatrick2 years ago26
2954945eRobert Craigie1 years ago27The primary API for interacting with OpenAI models is the [Responses API](https://platform.openai.com/docs/api-reference/responses). You can generate text from the model with the code below.
28
376dd199Logan Kilpatrick2 years ago29```python
fb5ba01cStainless Bot2 years ago30import os
08b8179aDavid Schnurr2 years ago31from openai import OpenAI
32
33client = OpenAI(
2954945eRobert Craigie1 years ago34# This is the default and can be omitted
35api_key=os.environ.get("OPENAI_API_KEY"),
36)
37
38response = client.responses.create(
84e0c1d0Charlie Guo5 months ago39model="gpt-5.2",
2954945eRobert Craigie1 years ago40instructions="You are a coding assistant that talks like a pirate.",
41input="How do I check if a Python object is an instance of a class?",
08b8179aDavid Schnurr2 years ago42)
43
2954945eRobert Craigie1 years ago44print(response.output_text)
45```
46
47The previous standard (supported indefinitely) for generating text is the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). You can use that API to generate text from the model with the code below.
48
49```python
50from openai import OpenAI
51
52client = OpenAI()
53
54completion = client.chat.completions.create(
84e0c1d0Charlie Guo5 months ago55model="gpt-5.2",
08b8179aDavid Schnurr2 years ago56messages=[
2954945eRobert Craigie1 years ago57{"role": "developer", "content": "Talk like a pirate."},
08b8179aDavid Schnurr2 years ago58{
59"role": "user",
2954945eRobert Craigie1 years ago60"content": "How do I check if a Python object is an instance of a class?",
61},
08b8179aDavid Schnurr2 years ago62],
63)
2954945eRobert Craigie1 years ago64
65print(completion.choices[0].message.content)
376dd199Logan Kilpatrick2 years ago66```
67
08b8179aDavid Schnurr2 years ago68While you can provide an `api_key` keyword argument,
69we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
70to add `OPENAI_API_KEY="My API Key"` to your `.env` file
2954945eRobert Craigie1 years ago71so that your API key is not stored in source control.
72[Get an API key here](https://platform.openai.com/settings/organization/api-keys).
3c6d4cd6Greg Brockman5 years ago73
5be95364Kar Petrosyan2 months ago74### Workload Identity Authentication
75
76For secure, automated environments like cloud-managed Kubernetes, Azure, and Google Cloud Platform, you can use workload identity authentication with short-lived tokens from cloud identity providers instead of long-lived API keys.
77
78#### Kubernetes (service account tokens)
79
80```python
81from openai import OpenAI
82from openai.auth import k8s_service_account_token_provider
83
84client = OpenAI(
85workload_identity={
86"client_id": "your-client-id",
87"identity_provider_id": "idp-123",
88"service_account_id": "sa-456",
89"provider": k8s_service_account_token_provider(
90"/var/run/secrets/kubernetes.io/serviceaccount/token"
91),
92},
93organization="org-xyz",
94project="proj-abc",
95)
96
97response = client.chat.completions.create(
98model="gpt-4",
99messages=[{"role": "user", "content": "Hello!"}],
100)
101```
102
103#### Azure (managed identity)
104
105```python
106from openai import OpenAI
107from openai.auth import azure_managed_identity_token_provider
108
109client = OpenAI(
110workload_identity={
111"client_id": "your-client-id",
112"identity_provider_id": "idp-123",
113"service_account_id": "sa-456",
114"provider": azure_managed_identity_token_provider(
115resource="https://management.azure.com/",
116),
117},
118)
119```
120
121#### Google Cloud Platform (compute engine metadata)
122
123```python
124from openai import OpenAI
125from openai.auth import gcp_id_token_provider
126
127client = OpenAI(
128workload_identity={
129"client_id": "your-client-id",
130"identity_provider_id": "idp-123",
131"service_account_id": "sa-456",
132"provider": gcp_id_token_provider(audience="https://api.openai.com/v1"),
133},
134)
135```
136
137#### Custom subject token provider
138
139```python
140from openai import OpenAI
141
142
143def get_custom_token() -> str:
144return "your-jwt-token"
145
146
147client = OpenAI(
148workload_identity={
149"client_id": "your-client-id",
150"identity_provider_id": "idp-123",
151"service_account_id": "sa-456",
152"provider": {
153"token_type": "jwt",
154"get_token": get_custom_token,
155},
156}
157)
158```
159
160You can also customize the token refresh buffer (default is 1200 seconds (20 minutes) before expiration):
161
162```python
163from openai import OpenAI
164from openai.auth import k8s_service_account_token_provider
165
166client = OpenAI(
167workload_identity={
168"client_id": "your-client-id",
169"identity_provider_id": "idp-123",
170"service_account_id": "sa-456",
171"provider": k8s_service_account_token_provider("/var/token"),
172"refresh_buffer_seconds": 120.0,
173}
174)
175```
176
192b8f2bDan Corin1 years ago177### Vision
178
2954945eRobert Craigie1 years ago179With an image URL:
192b8f2bDan Corin1 years ago180
181```python
2954945eRobert Craigie1 years ago182prompt = "What is in this image?"
183img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg"
184
185response = client.responses.create(
84e0c1d0Charlie Guo5 months ago186model="gpt-5.2",
2954945eRobert Craigie1 years ago187input=[
192b8f2bDan Corin1 years ago188{
189"role": "user",
190"content": [
2954945eRobert Craigie1 years ago191{"type": "input_text", "text": prompt},
192{"type": "input_image", "image_url": f"{img_url}"},
192b8f2bDan Corin1 years ago193],
194}
195],
196)
197```
198
199With the image as a base64 encoded string:
200
201```python
2954945eRobert Craigie1 years ago202import base64
203from openai import OpenAI
204
205client = OpenAI()
206
207prompt = "What is in this image?"
208with open("path/to/image.png", "rb") as image_file:
209b64_image = base64.b64encode(image_file.read()).decode("utf-8")
210
211response = client.responses.create(
84e0c1d0Charlie Guo5 months ago212model="gpt-5.2",
2954945eRobert Craigie1 years ago213input=[
192b8f2bDan Corin1 years ago214{
215"role": "user",
216"content": [
2954945eRobert Craigie1 years ago217{"type": "input_text", "text": prompt},
218{"type": "input_image", "image_url": f"data:image/png;base64,{b64_image}"},
192b8f2bDan Corin1 years ago219],
220}
221],
222)
223```
224
08b8179aDavid Schnurr2 years ago225## Async usage
3c6d4cd6Greg Brockman5 years ago226
08b8179aDavid Schnurr2 years ago227Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call:
ede08829Jakub Roztocil3 years ago228
08b8179aDavid Schnurr2 years ago229```python
fb5ba01cStainless Bot2 years ago230import os
08b8179aDavid Schnurr2 years ago231import asyncio
232from openai import AsyncOpenAI
ede08829Jakub Roztocil3 years ago233
08b8179aDavid Schnurr2 years ago234client = AsyncOpenAI(
2954945eRobert Craigie1 years ago235# This is the default and can be omitted
236api_key=os.environ.get("OPENAI_API_KEY"),
08b8179aDavid Schnurr2 years ago237)
ede08829Jakub Roztocil3 years ago238
239
08b8179aDavid Schnurr2 years ago240async def main() -> None:
2954945eRobert Craigie1 years ago241response = await client.responses.create(
84e0c1d0Charlie Guo5 months ago242model="gpt-5.2", input="Explain disestablishmentarianism to a smart five year old."
08b8179aDavid Schnurr2 years ago243)
2954945eRobert Craigie1 years ago244print(response.output_text)
ede08829Jakub Roztocil3 years ago245
246
08b8179aDavid Schnurr2 years ago247asyncio.run(main())
2b21516eAtty Eleti3 years ago248```
ede08829Jakub Roztocil3 years ago249
08b8179aDavid Schnurr2 years ago250Functionality between the synchronous and asynchronous clients is otherwise identical.
251
c62e9907stainless-app[bot]1 years ago252### With aiohttp
253
254By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
255
256You can enable this by installing `aiohttp`:
257
258```sh
259# install from PyPI
260pip install openai[aiohttp]
261```
262
263Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
264
265```python
bd988473stainless-app[bot]6 months ago266import os
c62e9907stainless-app[bot]1 years ago267import asyncio
268from openai import DefaultAioHttpClient
269from openai import AsyncOpenAI
270
271
272async def main() -> None:
273async with AsyncOpenAI(
bd988473stainless-app[bot]6 months ago274api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
c62e9907stainless-app[bot]1 years ago275http_client=DefaultAioHttpClient(),
276) as client:
277chat_completion = await client.chat.completions.create(
278messages=[
279{
280"role": "user",
281"content": "Say this is a test",
282}
283],
84e0c1d0Charlie Guo5 months ago284model="gpt-5.2",
c62e9907stainless-app[bot]1 years ago285)
286
287
288asyncio.run(main())
289```
290
2b23eb53Stainless Bot2 years ago291## Streaming responses
08b8179aDavid Schnurr2 years ago292
293We provide support for streaming responses using Server Side Events (SSE).
d53d9efbRachel Lim5 years ago294
08b8179aDavid Schnurr2 years ago295```python
296from openai import OpenAI
297
298client = OpenAI()
d53d9efbRachel Lim5 years ago299
2954945eRobert Craigie1 years ago300stream = client.responses.create(
84e0c1d0Charlie Guo5 months ago301model="gpt-5.2",
2954945eRobert Craigie1 years ago302input="Write a one-sentence bedtime story about a unicorn.",
08b8179aDavid Schnurr2 years ago303stream=True,
304)
2954945eRobert Craigie1 years ago305
306for event in stream:
307print(event)
d53d9efbRachel Lim5 years ago308```
309
08b8179aDavid Schnurr2 years ago310The async client uses the exact same interface.
d53d9efbRachel Lim5 years ago311
312```python
db0aa22cAdel Basli1 years ago313import asyncio
08b8179aDavid Schnurr2 years ago314from openai import AsyncOpenAI
315
316client = AsyncOpenAI()
317
a3da0196Stainless Bot2 years ago318
dfe1c8daSahand Sojoodi2 years ago319async def main():
f588695fstainless-app[bot]1 years ago320stream = await client.responses.create(
84e0c1d0Charlie Guo5 months ago321model="gpt-5.2",
2954945eRobert Craigie1 years ago322input="Write a one-sentence bedtime story about a unicorn.",
dfe1c8daSahand Sojoodi2 years ago323stream=True,
324)
d53d9efbRachel Lim5 years ago325
f588695fstainless-app[bot]1 years ago326async for event in stream:
2954945eRobert Craigie1 years ago327print(event)
08b8179aDavid Schnurr2 years ago328
329
2954945eRobert Craigie1 years ago330asyncio.run(main())
62b73b9bAtty Eleti3 years ago331```
332
3d3d16abstainless-app[bot]10 months ago333## Realtime API
488ec04bRobert Craigie1 years ago334
335The Realtime API enables you to build low-latency, multi-modal conversational experiences. It currently supports text and audio as both input and output, as well as [function calling](https://platform.openai.com/docs/guides/function-calling) through a WebSocket connection.
336
337Under the hood the SDK uses the [`websockets`](https://websockets.readthedocs.io/en/stable/) library to manage connections.
338
255677d7Robert Craigie1 years ago339The Realtime API works through a combination of client-sent events and server-sent events. Clients can send events to do things like update session configuration or send text and audio inputs. Server events confirm when audio responses have completed, or when a text response from the model has been received. A full event reference can be found [here](https://platform.openai.com/docs/api-reference/realtime-client-events) and a guide can be found [here](https://platform.openai.com/docs/guides/realtime).
488ec04bRobert Craigie1 years ago340
341Basic text based example:
342
343```py
344import asyncio
345from openai import AsyncOpenAI
346
347async def main():
348client = AsyncOpenAI()
349
3d3d16abstainless-app[bot]10 months ago350async with client.realtime.connect(model="gpt-realtime") as connection:
35cb4e9bDan Martins8 months ago351await connection.session.update(
352session={"type": "realtime", "output_modalities": ["text"]}
353)
488ec04bRobert Craigie1 years ago354
355await connection.conversation.item.create(
356item={
357"type": "message",
358"role": "user",
359"content": [{"type": "input_text", "text": "Say hello!"}],
360}
361)
362await connection.response.create()
363
364async for event in connection:
35cb4e9bDan Martins8 months ago365if event.type == "response.output_text.delta":
488ec04bRobert Craigie1 years ago366print(event.delta, flush=True, end="")
367
35cb4e9bDan Martins8 months ago368elif event.type == "response.output_text.done":
488ec04bRobert Craigie1 years ago369print()
370
371elif event.type == "response.done":
372break
373
374asyncio.run(main())
375```
376
6935dfdcRobert Craigie1 years ago377However the real magic of the Realtime API is handling audio inputs / outputs, see this example [TUI script](https://github.com/openai/openai-python/blob/main/examples/realtime/push_to_talk_app.py) for a fully fledged example.
488ec04bRobert Craigie1 years ago378
379### Realtime error handling
380
2954945eRobert Craigie1 years ago381Whenever an error occurs, the Realtime API will send an [`error` event](https://platform.openai.com/docs/guides/realtime-model-capabilities#error-handling) and the connection will stay open and remain usable. This means you need to handle it yourself, as _no errors are raised directly_ by the SDK when an `error` event comes in.
488ec04bRobert Craigie1 years ago382
383```py
384client = AsyncOpenAI()
385
3d3d16abstainless-app[bot]10 months ago386async with client.realtime.connect(model="gpt-realtime") as connection:
488ec04bRobert Craigie1 years ago387...
388async for event in connection:
389if event.type == 'error':
390print(event.error.type)
391print(event.error.code)
392print(event.error.event_id)
393print(event.error.message)
394```
395
08b8179aDavid Schnurr2 years ago396## Using types
397
47656567Stainless Bot2 years ago398Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
f4b9655fStainless Bot2 years ago399
47656567Stainless Bot2 years ago400- Serializing back into JSON, `model.to_json()`
401- Converting to a dictionary, `model.to_dict()`
2b21516eAtty Eleti3 years ago402
08b8179aDavid Schnurr2 years ago403Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
0e21703eTed Sanders4 years ago404
08b8179aDavid Schnurr2 years ago405## Pagination
0e21703eTed Sanders4 years ago406
08b8179aDavid Schnurr2 years ago407List methods in the OpenAI API are paginated.
408
409This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
0e21703eTed Sanders4 years ago410
411```python
ff5add01stainless-app[bot]1 years ago412from openai import OpenAI
0e21703eTed Sanders4 years ago413
08b8179aDavid Schnurr2 years ago414client = OpenAI()
0e21703eTed Sanders4 years ago415
08b8179aDavid Schnurr2 years ago416all_jobs = []
417# Automatically fetches more pages as needed.
418for job in client.fine_tuning.jobs.list(
419limit=20,
420):
421# Do something with job here
422all_jobs.append(job)
423print(all_jobs)
0e21703eTed Sanders4 years ago424```
425
08b8179aDavid Schnurr2 years ago426Or, asynchronously:
0e21703eTed Sanders4 years ago427
08b8179aDavid Schnurr2 years ago428```python
429import asyncio
ff5add01stainless-app[bot]1 years ago430from openai import AsyncOpenAI
0e21703eTed Sanders4 years ago431
08b8179aDavid Schnurr2 years ago432client = AsyncOpenAI()
0e21703eTed Sanders4 years ago433
434
08b8179aDavid Schnurr2 years ago435async def main() -> None:
436all_jobs = []
437# Iterate through items across all pages, issuing requests as needed.
438async for job in client.fine_tuning.jobs.list(
439limit=20,
440):
441all_jobs.append(job)
442print(all_jobs)
0e21703eTed Sanders4 years ago443
62b51ca0Boris Dayma4 years ago444
08b8179aDavid Schnurr2 years ago445asyncio.run(main())
446```
2942bf4bLogan Kilpatrick2 years ago447
08b8179aDavid Schnurr2 years ago448Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
2942bf4bLogan Kilpatrick2 years ago449
08b8179aDavid Schnurr2 years ago450```python
451first_page = await client.fine_tuning.jobs.list(
452limit=20,
453)
454if first_page.has_next_page():
455print(f"will fetch next page using these details: {first_page.next_page_info()}")
456next_page = await first_page.get_next_page()
457print(f"number of items we just fetched: {len(next_page.data)}")
458
459# Remove `await` for non-async usage.
62b51ca0Boris Dayma4 years ago460```
461
08b8179aDavid Schnurr2 years ago462Or just work directly with the returned data:
0e21703eTed Sanders4 years ago463
08b8179aDavid Schnurr2 years ago464```python
465first_page = await client.fine_tuning.jobs.list(
466limit=20,
467)
e389823bMorgan McGuire2 years ago468
08b8179aDavid Schnurr2 years ago469print(f"next page cursor: {first_page.after}") # => "next page cursor: ..."
470for job in first_page.data:
471print(job.id)
e389823bMorgan McGuire2 years ago472
08b8179aDavid Schnurr2 years ago473# Remove `await` for non-async usage.
474```
e389823bMorgan McGuire2 years ago475
08b8179aDavid Schnurr2 years ago476## Nested params
3c00e856hallacy3 years ago477
08b8179aDavid Schnurr2 years ago478Nested parameters are dictionaries, typed using `TypedDict`, for example:
3c00e856hallacy3 years ago479
480```python
08b8179aDavid Schnurr2 years ago481from openai import OpenAI
3c00e856hallacy3 years ago482
08b8179aDavid Schnurr2 years ago483client = OpenAI()
484
2954945eRobert Craigie1 years ago485response = client.chat.responses.create(
486input=[
aa681899Stainless Bot2 years ago487{
488"role": "user",
2954945eRobert Craigie1 years ago489"content": "How much ?",
aa681899Stainless Bot2 years ago490}
491],
84e0c1d0Charlie Guo5 months ago492model="gpt-5.2",
aa681899Stainless Bot2 years ago493response_format={"type": "json_object"},
494)
08b8179aDavid Schnurr2 years ago495```
3c00e856hallacy3 years ago496
2b23eb53Stainless Bot2 years ago497## File uploads
dc33cb9dMichelle Pokrass3 years ago498
acf68ef3stainless-app[bot]1 years ago499Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
dc33cb9dMichelle Pokrass3 years ago500
376dd199Logan Kilpatrick2 years ago501```python
08b8179aDavid Schnurr2 years ago502from pathlib import Path
503from openai import OpenAI
504
505client = OpenAI()
506
507client.files.create(
508file=Path("input.jsonl"),
509purpose="fine-tune",
510)
dc33cb9dMichelle Pokrass3 years ago511```
512
08b8179aDavid Schnurr2 years ago513The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.
376dd199Logan Kilpatrick2 years ago514
18e0b36astainless-app[bot]1 years ago515## Webhook Verification
516
517Verifying webhook signatures is _optional but encouraged_.
518
4f99c4e6David Meadows1 years ago519For more information about webhooks, see [the API docs](https://platform.openai.com/docs/guides/webhooks).
520
18e0b36astainless-app[bot]1 years ago521### Parsing webhook payloads
522
523For most use cases, you will likely want to verify the webhook and parse the payload at the same time. To achieve this, we provide the method `client.webhooks.unwrap()`, which parses a webhook request and verifies that it was sent by OpenAI. This method will raise an error if the signature is invalid.
524
525Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). The `.unwrap()` method will parse this JSON for you into an event object after verifying the webhook was sent from OpenAI.
526
527```python
528from openai import OpenAI
529from flask import Flask, request
530
531app = Flask(__name__)
532client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default
533
534
535@app.route("/webhook", methods=["POST"])
536def webhook():
537request_body = request.get_data(as_text=True)
538
539try:
540event = client.webhooks.unwrap(request_body, request.headers)
541
542if event.type == "response.completed":
543print("Response completed:", event.data)
544elif event.type == "response.failed":
545print("Response failed:", event.data)
546else:
547print("Unhandled event type:", event.type)
548
549return "ok"
550except Exception as e:
551print("Invalid signature:", e)
552return "Invalid signature", 400
553
554
555if __name__ == "__main__":
556app.run(port=8000)
557```
558
559### Verifying webhook payloads directly
560
561In some cases, you may want to verify the webhook separately from parsing the payload. If you prefer to handle these steps separately, we provide the method `client.webhooks.verify_signature()` to _only verify_ the signature of a webhook request. Like `.unwrap()`, this method will raise an error if the signature is invalid.
562
563Note that the `body` parameter must be the raw JSON string sent from the server (do not parse it first). You will then need to parse the body after verifying the signature.
564
565```python
566import json
567from openai import OpenAI
568from flask import Flask, request
569
570app = Flask(__name__)
571client = OpenAI() # OPENAI_WEBHOOK_SECRET environment variable is used by default
572
573
574@app.route("/webhook", methods=["POST"])
575def webhook():
576request_body = request.get_data(as_text=True)
577
578try:
579client.webhooks.verify_signature(request_body, request.headers)
580
581# Parse the body after verification
582event = json.loads(request_body)
583print("Verified event:", event)
584
585return "ok"
586except Exception as e:
587print("Invalid signature:", e)
588return "Invalid signature", 400
589
590
591if __name__ == "__main__":
592app.run(port=8000)
593```
594
08b8179aDavid Schnurr2 years ago595## Handling errors
376dd199Logan Kilpatrick2 years ago596
08b8179aDavid Schnurr2 years ago597When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `openai.APIConnectionError` is raised.
2b21516eAtty Eleti3 years ago598
08b8179aDavid Schnurr2 years ago599When the API returns a non-success status code (that is, 4xx or 5xx
600response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties.
62b73b9bAtty Eleti3 years ago601
08b8179aDavid Schnurr2 years ago602All errors inherit from `openai.APIError`.
603
604```python
605import openai
606from openai import OpenAI
607
608client = OpenAI()
609
610try:
8dab1421Stainless Bot2 years ago611client.fine_tuning.jobs.create(
23444ed9Stainless Bot1 years ago612model="gpt-4o",
8dab1421Stainless Bot2 years ago613training_file="file-abc123",
08b8179aDavid Schnurr2 years ago614)
615except openai.APIConnectionError as e:
616print("The server could not be reached")
617print(e.__cause__) # an underlying Exception, likely raised within httpx.
618except openai.RateLimitError as e:
619print("A 429 status code was received; we should back off a bit.")
620except openai.APIStatusError as e:
621print("Another non-200-range status code was received")
622print(e.status_code)
623print(e.response)
62b73b9bAtty Eleti3 years ago624```
625
fee9c81bstainless-app[bot]1 years ago626Error codes are as follows:
08b8179aDavid Schnurr2 years ago627
628| Status Code | Error Type |
629| ----------- | -------------------------- |
630| 400 | `BadRequestError` |
631| 401 | `AuthenticationError` |
632| 403 | `PermissionDeniedError` |
633| 404 | `NotFoundError` |
634| 422 | `UnprocessableEntityError` |
635| 429 | `RateLimitError` |
636| >=500 | `InternalServerError` |
637| N/A | `APIConnectionError` |
638
4b302346Robert Craigie1 years ago639## Request IDs
640
641> For more information on debugging requests, see [these docs](https://platform.openai.com/docs/api-reference/debugging-requests)
642
643All object responses in the SDK provide a `_request_id` property which is added from the `x-request-id` response header so that you can quickly log failing requests and report them back to OpenAI.
644
645```python
2954945eRobert Craigie1 years ago646response = await client.responses.create(
84e0c1d0Charlie Guo5 months ago647model="gpt-5.2",
2954945eRobert Craigie1 years ago648input="Say 'this is a test'.",
4b302346Robert Craigie1 years ago649)
2954945eRobert Craigie1 years ago650print(response._request_id) # req_123
4b302346Robert Craigie1 years ago651```
652
653Note that unlike other properties that use an `_` prefix, the `_request_id` property
2954945eRobert Craigie1 years ago654_is_ public. Unless documented otherwise, _all_ other `_` prefix properties,
655methods and modules are _private_.
4b302346Robert Craigie1 years ago656
709926ffRobert Craigie1 years ago657> [!IMPORTANT]
658> If you need to access request IDs for failed requests you must catch the `APIStatusError` exception
659
660```python
661import openai
662
663try:
664completion = await client.chat.completions.create(
84e0c1d0Charlie Guo5 months ago665messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-5.2"
709926ffRobert Craigie1 years ago666)
667except openai.APIStatusError as exc:
668print(exc.request_id) # req_123
669raise exc
670```
671
2954945eRobert Craigie1 years ago672## Retries
376dd199Logan Kilpatrick2 years ago673
08b8179aDavid Schnurr2 years ago674Certain errors are automatically retried 2 times by default, with a short exponential backoff.
675Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
676429 Rate Limit, and >=500 Internal errors are all retried by default.
0abf6413Andrew Chen Wang3 years ago677
08b8179aDavid Schnurr2 years ago678You can use the `max_retries` option to configure or disable retry settings:
0abf6413Andrew Chen Wang3 years ago679
680```python
08b8179aDavid Schnurr2 years ago681from openai import OpenAI
682
683# Configure the default for all requests:
684client = OpenAI(
685# default is 2
686max_retries=0,
687)
688
689# Or, configure per-request:
690client.with_options(max_retries=5).chat.completions.create(
691messages=[
692{
693"role": "user",
23444ed9Stainless Bot1 years ago694"content": "How can I get the name of the current day in JavaScript?",
08b8179aDavid Schnurr2 years ago695}
696],
84e0c1d0Charlie Guo5 months ago697model="gpt-5.2",
08b8179aDavid Schnurr2 years ago698)
0abf6413Andrew Chen Wang3 years ago699```
700
2954945eRobert Craigie1 years ago701## Timeouts
0abf6413Andrew Chen Wang3 years ago702
08b8179aDavid Schnurr2 years ago703By default requests time out after 10 minutes. You can configure this with a `timeout` option,
90e3d396Guspan Tanadi1 years ago704which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:
376dd199Logan Kilpatrick2 years ago705
08b8179aDavid Schnurr2 years ago706```python
707from openai import OpenAI
708
709# Configure the default for all requests:
710client = OpenAI(
1381f46eStainless Bot2 years ago711# 20 seconds (default is 10 minutes)
08b8179aDavid Schnurr2 years ago712timeout=20.0,
713)
714
715# More granular control:
716client = OpenAI(
717timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
718)
719
720# Override per-request:
2a678e30Stainless Bot2 years ago721client.with_options(timeout=5.0).chat.completions.create(
08b8179aDavid Schnurr2 years ago722messages=[
723{
724"role": "user",
725"content": "How can I list all files in a directory using Python?",
726}
727],
84e0c1d0Charlie Guo5 months ago728model="gpt-5.2",
08b8179aDavid Schnurr2 years ago729)
0abf6413Andrew Chen Wang3 years ago730```
731
08b8179aDavid Schnurr2 years ago732On timeout, an `APITimeoutError` is thrown.
376dd199Logan Kilpatrick2 years ago733
08b8179aDavid Schnurr2 years ago734Note that requests that time out are [retried twice by default](#retries).
376dd199Logan Kilpatrick2 years ago735
08b8179aDavid Schnurr2 years ago736## Advanced
376dd199Logan Kilpatrick2 years ago737
08b8179aDavid Schnurr2 years ago738### Logging
376dd199Logan Kilpatrick2 years ago739
08b8179aDavid Schnurr2 years ago740We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.
376dd199Logan Kilpatrick2 years ago741
f6199d60stainless-app[bot]1 years ago742You can enable logging by setting the environment variable `OPENAI_LOG` to `info`.
376dd199Logan Kilpatrick2 years ago743
08b8179aDavid Schnurr2 years ago744```shell
f6199d60stainless-app[bot]1 years ago745$ export OPENAI_LOG=info
376dd199Logan Kilpatrick2 years ago746```
747
f6199d60stainless-app[bot]1 years ago748Or to `debug` for more verbose logging.
749
08b8179aDavid Schnurr2 years ago750### How to tell whether `None` means `null` or missing
dc33cb9dMichelle Pokrass3 years ago751
08b8179aDavid Schnurr2 years ago752In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:
3c6d4cd6Greg Brockman5 years ago753
08b8179aDavid Schnurr2 years ago754```py
755if response.my_field is None:
756if 'my_field' not in response.model_fields_set:
757print('Got json like {}, without a "my_field" key present at all.')
758else:
759print('Got json like {"my_field": null}.')
760```
376dd199Logan Kilpatrick2 years ago761
08b8179aDavid Schnurr2 years ago762### Accessing raw response data (e.g. headers)
376dd199Logan Kilpatrick2 years ago763
86379b44Stainless Bot2 years ago764The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,
08b8179aDavid Schnurr2 years ago765
766```py
767from openai import OpenAI
768
769client = OpenAI()
770response = client.chat.completions.with_raw_response.create(
771messages=[{
772"role": "user",
773"content": "Say this is a test",
774}],
84e0c1d0Charlie Guo5 months ago775model="gpt-5.2",
08b8179aDavid Schnurr2 years ago776)
777print(response.headers.get('X-My-Header'))
778
779completion = response.parse() # get the object that `chat.completions.create()` would have returned
780print(completion)
376dd199Logan Kilpatrick2 years ago781```
782
16315f22stainless-app[bot]1 years ago783These methods return a [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version.
86379b44Stainless Bot2 years ago784
785For the sync client this will mostly be the same with the exception
786of `content` & `text` will be methods instead of properties. In the
787async client, all methods will be async.
788
789A migration script will be provided & the migration in general should
790be smooth.
791
792#### `.with_streaming_response`
793
794The above interface eagerly reads the full response body when you make the request, which may not always be what you want.
795
796To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.
797
798As such, `.with_streaming_response` methods return a different [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object, and the async client returns an [`AsyncAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object.
799
800```python
801with client.chat.completions.with_streaming_response.create(
802messages=[
803{
804"role": "user",
805"content": "Say this is a test",
806}
807],
84e0c1d0Charlie Guo5 months ago808model="gpt-5.2",
86379b44Stainless Bot2 years ago809) as response:
810print(response.headers.get("X-My-Header"))
811
812for line in response.iter_lines():
813print(line)
814```
815
816The context manager is required so that the response will reliably be closed.
3c6d4cd6Greg Brockman5 years ago817
73869eeeStainless Bot2 years ago818### Making custom/undocumented requests
819
7931ebaaStainless Bot2 years ago820This library is typed for convenient access to the documented API.
73869eeeStainless Bot2 years ago821
822If you need to access undocumented endpoints, params, or response properties, the library can still be used.
823
824#### Undocumented endpoints
825
826To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
fee9c81bstainless-app[bot]1 years ago827http verbs. Options on the client will be respected (such as retries) when making this request.
73869eeeStainless Bot2 years ago828
829```py
830import httpx
831
832response = client.post(
833"/foo",
834cast_to=httpx.Response,
835body={"my_param": True},
836)
837
838print(response.headers.get("x-foo"))
839```
840
802819c8Stainless Bot2 years ago841#### Undocumented request params
73869eeeStainless Bot2 years ago842
843If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request
844options.
845
802819c8Stainless Bot2 years ago846#### Undocumented response properties
73869eeeStainless Bot2 years ago847
848To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You
849can also get all the extra fields on the Pydantic model as a dict with
850[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).
851
08b8179aDavid Schnurr2 years ago852### Configuring the HTTP client
376dd199Logan Kilpatrick2 years ago853
08b8179aDavid Schnurr2 years ago854You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:
376dd199Logan Kilpatrick2 years ago855
6a1ab551stainless-app[bot]1 years ago856- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)
857- Custom [transports](https://www.python-httpx.org/advanced/transports/)
d3254d12stainless-app[bot]2 years ago858- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
376dd199Logan Kilpatrick2 years ago859
860```python
6a1ab551stainless-app[bot]1 years ago861import httpx
347363edStainless Bot2 years ago862from openai import OpenAI, DefaultHttpxClient
08b8179aDavid Schnurr2 years ago863
864client = OpenAI(
0733934fStainless Bot2 years ago865# Or use the `OPENAI_BASE_URL` env var
38dd5348Adrian Cole1 years ago866base_url="http://my.test.server.example.com:8083/v1",
347363edStainless Bot2 years ago867http_client=DefaultHttpxClient(
6a1ab551stainless-app[bot]1 years ago868proxy="http://my.test.proxy.example.com",
08b8179aDavid Schnurr2 years ago869transport=httpx.HTTPTransport(local_address="0.0.0.0"),
870),
871)
872```
873
f8f01a61stainless-app[bot]1 years ago874You can also customize the client on a per-request basis by using `with_options()`:
875
876```python
877client.with_options(http_client=DefaultHttpxClient(...))
878```
879
08b8179aDavid Schnurr2 years ago880### Managing HTTP resources
881
882By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.
883
588935e2stainless-app[bot]1 years ago884```py
885from openai import OpenAI
886
887with OpenAI() as client:
888# make requests here
889...
890
891# HTTP client is now closed
892```
893
08b8179aDavid Schnurr2 years ago894## Microsoft Azure OpenAI
895
6e7d8548Scott Addie2 years ago896To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the `AzureOpenAI`
08b8179aDavid Schnurr2 years ago897class instead of the `OpenAI` class.
898
899> [!IMPORTANT]
900> The Azure API shape differs from the core API shape which means that the static types for responses / params
901> won't always be correct.
376dd199Logan Kilpatrick2 years ago902
08b8179aDavid Schnurr2 years ago903```py
904from openai import AzureOpenAI
376dd199Logan Kilpatrick2 years ago905
08b8179aDavid Schnurr2 years ago906# gets the API Key from environment variable AZURE_OPENAI_API_KEY
907client = AzureOpenAI(
6e7d8548Scott Addie2 years ago908# https://learn.microsoft.com/azure/ai-services/openai/reference#rest-api-versioning
819ae68dJackYu2 years ago909api_version="2023-07-01-preview",
6e7d8548Scott Addie2 years ago910# https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
08b8179aDavid Schnurr2 years ago911azure_endpoint="https://example-endpoint.openai.azure.com",
912)
913
914completion = client.chat.completions.create(
915model="deployment-name", # e.g. gpt-35-instant
916messages=[
917{
918"role": "user",
919"content": "How do I output all files in a directory using Python?",
920},
921],
922)
47656567Stainless Bot2 years ago923print(completion.to_json())
376dd199Logan Kilpatrick2 years ago924```
3c6d4cd6Greg Brockman5 years ago925
08b8179aDavid Schnurr2 years ago926In addition to the options provided in the base `OpenAI` client, the following options are provided:
927
7758c54bStainless Bot2 years ago928- `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable)
08b8179aDavid Schnurr2 years ago929- `azure_deployment`
7758c54bStainless Bot2 years ago930- `api_version` (or the `OPENAI_API_VERSION` environment variable)
931- `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable)
08b8179aDavid Schnurr2 years ago932- `azure_ad_token_provider`
933
6e7d8548Scott Addie2 years ago934An example of using the client with Microsoft Entra ID (formerly known as Azure Active Directory) can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py).
08b8179aDavid Schnurr2 years ago935
936## Versioning
937
938This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
939
9401. Changes that only affect static types, without breaking runtime behavior.
e502d301Josiah Altschuler1 years ago9412. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
08b8179aDavid Schnurr2 years ago9423. Changes that we do not expect to impact the vast majority of users in practice.
943
944We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
945
946We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions.
947
fee10404stainless-app[bot]1 years ago948### Determining the installed version
949
950If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version.
951
952You can determine the version that is being used at runtime with:
953
954```py
955import openai
956print(openai.__version__)
957```
958
08b8179aDavid Schnurr2 years ago959## Requirements
3c6d4cd6Greg Brockman5 years ago960
3f52ac84stainless-app[bot]7 months ago961Python 3.9 or higher.
a3001d8dStainless Bot1 years ago962
963## Contributing
964
965See [the contributing documentation](./CONTRIBUTING.md).