# OpenAI
Access GPT models including standard chat models and reasoning models (o1, o3, GPT-5).
ReqLLM also exposes a separate `openai_codex` provider for the ChatGPT Codex backend used by OAuth Codex tokens.
## Configuration
```bash
OPENAI_API_KEY=sk-...
```
## Model Specs
For the full model-spec workflow, see [Model Specs](model-specs.md).
Use exact OpenAI IDs from [LLMDB.xyz](https://llmdb.xyz) when possible. For brand-new model IDs, local OpenAI-compatible servers, or proxies, use `ReqLLM.model!/1` with `provider: :openai`, an explicit `id`, and `base_url` when needed.
### OAuth Access Token (optional)
If you use OAuth instead of API keys, pass an access token and set auth mode:
```elixir
ReqLLM.generate_text(
"openai:gpt-5-codex",
"Write a test",
auth_mode: :oauth,
access_token: System.fetch_env!("OPENAI_ACCESS_TOKEN")
)
```
You can also pass these under `provider_options`.
### ChatGPT Codex Backend (`openai_codex`)
Use `openai_codex:*` when your token comes from the ChatGPT/Codex OAuth flow and you want requests routed to `https://chatgpt.com/backend-api/codex/responses` instead of platform OpenAI `/v1/responses`.
This provider is OAuth-only and resolves `chatgpt_account_id` in this order:
- explicit `provider_options: [chatgpt_account_id: "..."]`
- `accountId` / `account_id` in the oauth/auth JSON file
- JWT claim extraction from the access token
Example:
```elixir
ReqLLM.generate_text(
"openai_codex:gpt-5.3-codex-spark",
"Write a test for this function",
provider_options: [
auth_mode: :oauth,
oauth_file: "/path/to/auth.json"
]
)
```
### OAuth Files (`oauth.json` / `auth.json`)
ReqLLM can also read provider credentials from a JSON file using the same shape used by `pi-ai`:
```json
{
"openai-codex": {
"type": "oauth",
"access": "eyJ...",
"refresh": "oai_rt_...",
"expires": 1762857415123,
"accountId": "user_123"
}
}
```
When `auth_mode: :oauth` is enabled and no explicit `access_token` is passed, ReqLLM will:
- load credentials from `provider_options: [oauth_file: "..."]`
- accept `auth_file` as an alias
- fall back to `oauth.json` or `auth.json` in the current working directory
- refresh expired `openai-codex` credentials automatically and persist the updated file
- reuse `accountId` from the file or derive it from the refreshed access token for Codex requests
Example:
```elixir
ReqLLM.generate_text(
"openai:gpt-5-codex",
"Write a test",
provider_options: [
auth_mode: :oauth,
oauth_file: "/path/to/oauth.json"
]
)
```
If you need to customize the refresh HTTP client, pass `oauth_http_options` under `provider_options`.
For `openai_codex`, you can also override the backend headers with:
- `provider_options: [chatgpt_account_id: "..."]`
- `provider_options: [codex_originator: "pi"]`
## Attachments
OpenAI Chat Completions API only supports image attachments (JPEG, PNG, GIF, WebP).
For document support (PDFs, etc.), use Anthropic or Google providers.
## Dual API Architecture
OpenAI provider automatically routes between two APIs based on model metadata:
- **Chat Completions API**: Standard GPT models (gpt-4o, gpt-4-turbo, gpt-3.5-turbo)
- **Responses API**: Reasoning models (o1, o3, o4-mini, gpt-5) with extended thinking
## Provider Options
Passed via `:provider_options` keyword:
### `max_completion_tokens`
- **Type**: Integer
- **Purpose**: Required for reasoning models (o1, o3, gpt-5)
- **Note**: ReqLLM auto-translates `max_tokens` to `max_completion_tokens` for reasoning models
- **Example**: `provider_options: [max_completion_tokens: 4000]`
### `openai_structured_output_mode`
- **Type**: `:auto` | `:json_schema` | `:tool_strict`
- **Default**: `:auto`
- **Purpose**: Control structured output strategy
- **`:auto`**: Use json_schema when supported, else strict tools
- **`:json_schema`**: Force response_format with json_schema
- **`:tool_strict`**: Force strict: true on function tools
- **Example**: `provider_options: [openai_structured_output_mode: :json_schema]`
### `response_format`
- **Type**: Map
- **Purpose**: Custom response format configuration
- **Example**:
```elixir
provider_options: [
response_format: %{
type: "json_schema",
json_schema: %{
name: "person",
schema: %{type: "object", properties: %{name: %{type: "string"}}}
}
}
]
```
### `openai_parallel_tool_calls`
- **Type**: Boolean | nil
- **Default**: `nil`
- **Purpose**: Override parallel tool call behavior
- **Example**: `provider_options: [openai_parallel_tool_calls: false]`
### `reasoning_effort`
- **Type**: `:low` | `:medium` | `:high`
- **Purpose**: Control reasoning effort (Responses API only)
- **Example**: `reasoning_effort: :high`
### `service_tier`
- **Type**: `:auto` | `:default` | `:flex` | `:priority` | String
- **Purpose**: Service tier for request prioritization
- **Example**: `service_tier: :auto`
### `seed`
- **Type**: Integer
- **Purpose**: Set seed for reproducible outputs
- **Example**: `provider_options: [seed: 42]`
### `logprobs`
- **Type**: Boolean
- **Purpose**: Request log probabilities
- **Example**: `provider_options: [logprobs: true, top_logprobs: 3]`
### `top_logprobs`
- **Type**: Integer (1-20)
- **Purpose**: Number of log probabilities to return
- **Requires**: `logprobs: true`
- **Example**: `provider_options: [logprobs: true, top_logprobs: 5]`
### `user`
- **Type**: String
- **Purpose**: Track usage by user identifier
- **Example**: `provider_options: [user: "user_123"]`
### `verbosity`
- **Type**: `"low"` | `"medium"` | `"high"`
- **Default**: `"medium"`
- **Purpose**: Control output detail level
- **Example**: `provider_options: [verbosity: "high"]`
### `openai_stream_transport`
- **Type**: `:sse` | `:websocket`
- **Default**: `:sse`
- **Purpose**: Select the streaming transport for Responses models
- **Note**: `:websocket` currently applies to OpenAI Responses models only
- **Example**: `provider_options: [openai_stream_transport: :websocket]`
### Embedding Options
#### `dimensions`
- **Type**: Positive integer
- **Purpose**: Control embedding dimensions (model-specific ranges)
- **Example**: `provider_options: [dimensions: 512]`
#### `encoding_format`
- **Type**: `"float"` | `"base64"`
- **Purpose**: Format for embedding output
- **Example**: `provider_options: [encoding_format: "base64"]`
### Responses API Resume Flow
#### `previous_response_id`
- **Type**: String
- **Purpose**: Resume tool calling flow from previous response
- **Example**: `provider_options: [previous_response_id: "resp_abc123"]`
#### `tool_outputs`
- **Type**: List of `%{call_id, output}` maps
- **Purpose**: Provide tool execution results for resume flow
- **Example**: `provider_options: [tool_outputs: [%{call_id: "call_1", output: "result"}]]`
## WebSocket Mode
ReqLLM keeps SSE as the default transport for OpenAI streaming, but Responses models can opt into OpenAI WebSocket mode per request:
```elixir
{:ok, stream_response} =
ReqLLM.stream_text(
"openai:gpt-5",
"Write a short summary",
provider_options: [openai_stream_transport: :websocket]
)
text = ReqLLM.StreamResponse.text(stream_response)
usage = ReqLLM.StreamResponse.usage(stream_response)
```
Use this when you want a call-scoped WebSocket transport while keeping the existing `StreamResponse` API. SSE remains the safer default for broad provider parity and existing fixture coverage.
## Realtime API
ReqLLM also exposes an experimental low-level Realtime WebSocket client for session-oriented workflows that do not fit `stream_text/3`:
```elixir
{:ok, session} = ReqLLM.OpenAI.Realtime.connect("gpt-realtime")
:ok =
ReqLLM.OpenAI.Realtime.session_update(session, %{
"type" => "realtime",
"instructions" => "Be concise and friendly."
})
{:ok, event} = ReqLLM.OpenAI.Realtime.next_event(session)
:ok = ReqLLM.OpenAI.Realtime.close(session)
```
This API is intentionally low-level. You send JSON events, receive JSON events, and manage the session lifecycle explicitly.
## Usage Metrics
OpenAI provides comprehensive usage data including:
- `reasoning_tokens` - For reasoning models (o1, o3, gpt-5)
- `cached_tokens` - Cached input tokens
- Standard input/output/total tokens and costs
### Web Search (Responses API)
Models using the Responses API (o1, o3, gpt-5) support web search tools:
```elixir
{:ok, response} = ReqLLM.generate_text(
"openai:gpt-5-mini",
"What are the latest AI announcements?",
tools: [%{"type" => "web_search"}]
)
# Access web search usage
response.usage.tool_usage.web_search
#=> %{count: 2, unit: "call"}
# Access cost breakdown
response.usage.cost
#=> %{tokens: 0.002, tools: 0.02, images: 0.0, total: 0.022}
```
### Image Generation
Image generation costs are tracked separately:
```elixir
{:ok, response} = ReqLLM.generate_image("openai:gpt-image-1", prompt)
response.usage.image_usage
#=> %{generated: %{count: 1, size_class: "1024x1024"}}
response.usage.cost
#=> %{tokens: 0.0, tools: 0.0, images: 0.04, total: 0.04}
```
See the [Image Generation Guide](image-generation.md) for more details.
## Resources
- [OpenAI API Documentation](https://platform.openai.com/docs/api-reference)
- [Model Overview](https://platform.openai.com/docs/models)