Interact with the OpenAI API directly from your terminal with the openai command-line tool.
Installation
Install the CLI with Homebrew:
brew install openai/tools/openaiOr install it with Go 1.25 or later:
go install 'github.com/openai/openai-cli/cmd/openai@latest'Older versions of the Python SDK also installed a legacy openai command. If you already had that package installed and the command you see does not match this guide, your shell may still be resolving the older binary. Fresh CLI installs are not affected.
Authentication
The CLI reads your API key from OPENAI_API_KEY:
Command:
export OPENAI_API_KEY="sk-..."If you don’t have an API key yet, create one in the dashboard.
For Admin API endpoints, set OPENAI_ADMIN_KEY instead. The SDK layer selects the admin key or default API key based on the endpoint being called.
To point at a different API host, set OPENAI_BASE_URL.
Use cases
Use the CLI when the work belongs naturally in the terminal:
- Generate local artifacts such as images or speech.
- Extract structured data into JSONL for later shell steps.
- Use Responses with files, computer use, and current web context in the cloud.
- Create projects and API keys with Admin APIs.
Use it directly for one-off terminal requests, or from scripts when agents need repeatable batch work over files and generated artifacts.
CLI vs subagents for Codex
Use the CLI for repeatable API work you want to inspect and rerun, such as batch extraction, file transforms, artifact generation, or deliberate model selection. Use subagents when the work still needs judgment, such as exploring code, comparing hypotheses, debugging, or reviewing changes.
Global flags
These options work across commands:
| Flag | Use |
|---|---|
--format | Print responses as auto, json, jsonl, pretty, raw, yaml, or explore. |
--transform | Extract or reshape response data with a GJSON path before printing. |
--debug | Print request and response details to stderr. Authorization is redacted; review headers before sharing logs. |
This guide focuses on CLI patterns. For the latest arguments and response shapes for any API family, use the live API reference.
You can also change the base URL when you need to point the CLI at another compatible endpoint, such as a deployment that supports a different model set or only a subset of the API surface.
Responses
Use Responses for text generation, structured extraction, web search, file understanding, and repeatable Codex-authored batch scripts.
Send your first request
Command:
1
2
3
openai responses create \
--model gpt-5.5 \
--input "Say hello in one sentence."Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"id": "resp_...",
"object": "response",
"status": "completed",
"model": "gpt-5.5-...",
"output": [
{
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Hello!"
}
]
}
],
"usage": {
"input_tokens": 12,
"output_tokens": 6,
"total_tokens": 18
},
"...": "additional response fields omitted"
}The CLI prints the full API response object by default. Examples on this page keep representative fields such as id, status, model, output, and usage, and omit the rest.
Responses output can include non-message items, such as reasoning items, before the assistant message. When you need assistant text, select the message item by type instead of assuming it is always output[0]:
--transform 'output.#(type=="message").content.0.text'Add a local file to the prompt
For a simple local file, build the prompt inline with command substitution:
1
2
3
4
5
6
7
8
9
openai responses create \
--model gpt-5.5 \
--input "Summarize this note in one sentence.
<note>
$(cat ./note.md)
</note>" \
--format yaml \
--transform 'output.#(type=="message").content.0.text'Output:
The note says the launch checklist is ready except for final support ownership.Passing request bodies
Use flags for short scalar inputs. Use a YAML heredoc for multiline prompts, tools, files, or nested request bodies. The heredoc can contain the same request fields you would otherwise pass as flags.
Be careful with string values that look like YAML, especially prompts that contain : or {}. On flags, the generated parser may interpret those values as structured YAML instead of plain text. If a prompt starts looking like configuration, put it under input: | in a YAML body instead:
Command:
1
2
3
4
5
6
7
8
9
10
11
12
13
openai responses create \
--format yaml \
--transform 'output.#(type=="message").content.0.text' <<'YAML'
model: gpt-5.5
instructions: Return exactly one sentence.
max_output_tokens: 120
input: |
Summarize this release note in one sentence.
<release_note>
Fixed the image generation example and added CLI installation guidance.
</release_note>
YAMLOutput:
The release note updates the CLI docs with corrected image generation and installation guidance.When the prompt itself needs shell assembly, build a YAML body and pipe it into the command:
1
2
3
4
5
6
7
8
9
10
{
printf 'input: |\n'
printf ' Summarize this note in one sentence.\n\n'
printf ' <note>\n'
sed 's/^/ /' ./note.md
printf ' </note>\n'
} | openai responses create \
--model gpt-5.5 \
--format yaml \
--transform 'output.#(type=="message").content.0.text'Write structured data to JSON
Use structured outputs when downstream scripts need stable JSON. Save reusable schemas to disk:
Save as schema.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"type": "json_schema",
"name": "fact",
"strict": true,
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"person": { "type": "string" },
"topic": { "type": "string" }
},
"required": ["person", "topic"]
}
}Command:
1
2
3
4
5
6
7
openai responses create \
--model gpt-5.5 \
--instructions "Extract the person and topic from the input." \
--input "Ada Lovelace wrote notes about the Analytical Engine." \
--text.format "$(cat ./schema.json)" \
--format yaml \
--transform 'output.#(type=="message").content.0.text'Output:
{ "person": "Ada Lovelace", "topic": "notes about the Analytical Engine" }Write structured records to JSONL
When one input may produce many records, ask the model for an array and flatten it into JSONL so later shell steps can process one record per line:
Save as records-schema.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"type": "json_schema",
"name": "items",
"strict": true,
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"title": { "type": "string" },
"summary": { "type": "string" },
"evidence": { "type": "string" }
},
"required": ["title", "summary", "evidence"]
}
}
},
"required": ["items"]
}
}Command:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
: > records.jsonl
for file in notes/*.md; do
extracted="$(
openai responses create \
--model gpt-5.5 \
--text.format "$(cat ./records-schema.json)" \
--raw-output \
--transform 'output.#(type=="message").content.0.text' <<YAML
input: |
<note path="$file">
$(sed 's/^/ /' "$file")
</note>
YAML
)"
jq -r --arg source "$file" \
'.items[]? + {source: $source} | @json' \
<<<"$extracted" >> records.jsonl
doneThis keeps the model response structured while producing one JSON object per line for later shell steps.
Web search
Responses can call hosted tools from the same YAML request body:
Command:
1
2
3
4
5
6
7
8
9
10
openai responses create \
--model gpt-5.5 \
--format yaml \
--transform 'output.#(type=="message").content.0.text' <<'YAML'
tools:
- type: web_search
input: |
Research the latest material news for AAPL.
Return three concise bullets and cite sources in the text.
YAMLOutput:
- Apple announced ...
- Analysts highlighted ...
- The company said ...File inputs
For uploaded files such as PDFs, create the file first, capture its ID, and pass it as input_file.file_id:
Command:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FILE_ID=$(
openai files create \
--file ./brief.pdf \
--purpose user_data \
--format yaml \
--transform id
)
openai responses create \
--model gpt-5.5 \
--format yaml \
--transform 'output.#(type=="message").content.0.text' <<YAML
input:
- role: user
content:
- type: input_text
text: Summarize this brief and list three risks.
- type: input_file
file_id: ${FILE_ID}
YAMLOutput:
- The brief proposes ...
- Risks: migration timing, unclear rollback criteria, and unresolved support ownership.Recent generated builds send local file flags as multipart file parts with filename and content type metadata. If a local upload command fails with an UploadFile type error, update the CLI and retry.
Images
Generate an image
Generate an image, extract the base64 payload, and decode it into a normal asset file:
Command:
1
2
3
4
5
6
openai images generate \
--model gpt-image-2 \
--prompt "A simple product-style render of a translucent green cube on a neutral background." \
--format yaml \
--transform 'data.0.b64_json' | base64 --decode > hero.png
printf 'wrote hero.png\n'Output:
wrote hero.pngCurrent limitation: image commands do not yet have native --output support, so image generation still requires extracting b64_json and decoding it yourself.
For gpt-image-2, omit --input-fidelity; image inputs are always processed at high fidelity. Do not use --background transparent with gpt-image-2. The model also supports broader --size values than earlier GPT Image models, as long as the requested resolution satisfies the Image API size constraints.
Edit an image
Image editing uses the same base64 extraction pattern after the edit request succeeds:
Command:
1
2
3
4
5
6
7
openai images edit \
--model gpt-image-2 \
--image ./hero.png \
--prompt "Turn the cube bright green." \
--format yaml \
--transform 'data.0.b64_json' | base64 --decode > hero-edited.png
printf 'wrote hero-edited.png\n'Output:
wrote hero-edited.pngIf a local image edit upload fails with an UploadFile type error, update the CLI and retry.
Speech
Create an MP3 locally with the speech API:
Command:
1
2
3
4
5
openai audio:speech create \
--model gpt-4o-mini-tts \
--voice marin \
--input "The OpenAI CLI can call the API from ordinary shell scripts." \
--output speech.mp3Output:
Wrote output to: speech.mp3Play it with whatever local audio tool is available on your machine. On macOS:
afplay speech.mp3Use --instructions to shape delivery and --input for the words that should be spoken. Instructions work well for cues such as pace, energy, warmth, formality, emphasis, or audience:
1
2
3
4
5
6
openai audio:speech create \
--model gpt-4o-mini-tts \
--voice marin \
--instructions "Whisper very quickly, like a hurried stage cue, while staying clear and intelligible." \
--input "The launch checklist is ready. Please send final feedback by Friday at noon." \
--output reminder.mp3Transcription
Print plain transcript text for shell pipelines:
Command:
1
2
3
4
5
openai audio:transcriptions create \
--model gpt-4o-transcribe \
--file ./speech.mp3 \
--transform text \
--raw-outputOutput:
The OpenAI CLI can call the API from ordinary shell scripts.Use the response format that matches the artifact you need:
| Need | Command shape |
|---|---|
| Plain transcript text | --model gpt-4o-transcribe --transform text --raw-output |
| Subtitle files | --model whisper-1 --response-format srt or --response-format vtt |
| Segment or word timestamps | --model whisper-1 --response-format verbose_json |
| Speaker-labeled diarization | --model gpt-4o-transcribe-diarize --response-format diarized_json |
For word-level timing, request the verbose transcription shape:
Command:
1
2
3
4
5
6
openai audio:transcriptions create \
--model whisper-1 \
--file ./speech.mp3 \
--response-format verbose_json \
--timestamp-granularity word \
--format jsonOutput:
1
2
3
4
5
6
7
8
9
10
11
{
"task": "transcribe",
"language": "english",
"duration": 6,
"text": "The OpenAI CLI can call the API from ordinary shell scripts.",
"words": [
{ "word": "The", "start": 0, "end": 0.42 },
{ "word": "OpenAI", "start": 0.42, "end": 1.22 }
],
"...": "additional response fields omitted"
}For speaker-labeled output, use the diarization model and request diarized_json:
Command:
1
2
3
4
5
openai audio:transcriptions create \
--model gpt-4o-transcribe-diarize \
--file ./speech.mp3 \
--response-format diarized_json \
--format jsonOutput:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"text": "The OpenAI CLI can call the API from ordinary shell scripts.",
"segments": [
{
"type": "transcript.text.segment",
"id": "seg_0",
"start": 0.05,
"end": 5.25,
"text": " The OpenAI CLI can call the API from ordinary shell scripts.",
"speaker": "A"
}
],
"...": "additional response fields omitted"
}whisper-1 supports json, text, srt, verbose_json, and vtt. diarized_json is the format that carries segments[].speaker; with the same diarization model and plain json, the response contains transcript text but not speaker labels.
Admin APIs
Use Admin APIs for organization management, credential provisioning, compliance, and usage-monitoring workflows. Set OPENAI_ADMIN_KEY, then call the generated admin:organization:* commands.
To provision a new machine credential, create a project, create a service account inside that project, and use the returned API key.
Create a project, service account, and API key
Creating a service account in that project returns an unredacted API key for the service account.
Command:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Create the project that will own this app or agent and save the response.
openai admin:organization:projects create \
--name "automation project" \
--format json > project.json
PROJECT_ID="$(jq -r '.id' project.json)"
# Create a service account inside the project and save the full response.
openai admin:organization:projects:service-accounts create \
--project-id "$PROJECT_ID" \
--name "automation bot" \
--format json > service-account.json
# Extract the returned API key into an env file for the workload to use.
jq -r '.api_key.value | "OPENAI_API_KEY=\(.)"' \
service-account.json > .envOutput:
1
2
3
4
5
6
7
8
9
10
{
"object": "organization.project.service_account",
"id": "svc_acct_...",
"name": "automation bot",
"role": "member",
"api_key": {
"id": "key_...",
"value": "sk-..."
}
}This writes the project response to project.json, parses its ID into the next command, writes the service-account response to service-account.json, and writes the returned credential to .env as OPENAI_API_KEY=.... Treat both JSON files as secrets, and add project.json, service-account.json, and .env to .gitignore before using this pattern in a repository.
For the rest of the surface, see the Admin APIs guide and the current Administration API reference. Be careful about giving unvetted actors access to admin keys.