How to Install and Use the Official OpenAI CLI (v1.39): Setup, Responses, Files and Batches
OpenAI now ships an official command-line client for its REST API, published as openai/openai-cli under the Apache-2.0 license. One binary called openai covers Responses, Chat Completions, images, audio, files, vector stores, embeddings, batches, fine-tuning and the admin API, and it adds local helpers such as an offline tokenizer. If you have been hand-writing curl calls or throwaway Python scripts to upload a file, check a batch or list your models, this tool replaces them with one command each. This guide covers install, safe key setup, the commands builders use most, how to get machine-readable output for scripts, batch jobs end to end, and the errors you are likely to hit.
Everything below comes from the project’s README, the docs folder in the repo (batches, readable output, error recovery, shell completion), and the v1.39.0 release published October 19, 2026. We installed the v1.39.0 Linux amd64 binary on October 21, 2026 (Beijing time) and ran the help pages, the offline tokenizer and the error paths shown here ourselves. We did not make paid API calls, so for request commands we show the project’s own documented examples.
Key facts (official sources, October 2026):
- What it is: the official CLI for the OpenAI REST API, written in Go. Repo:
openai/openai-cli. License: Apache-2.0.- Latest release: v1.39.0 (October 19, 2026), with builds for macOS, Linux and Windows on amd64 and arm64, plus
.deb,.rpm,.apkand Arch packages.- Install:
brew install openai/tools/openai, orgo install 'github.com/openai/openai-cli/cmd/openai@latest'(Go 1.26 or later).- Command shape:
openai <resource> [subresource...] <command> [flags...].- Auth:
OPENAI_API_KEYfor normal endpoints,OPENAI_ADMIN_KEYfor admin endpoints.- Output: readable text by default, even in pipes. Scripts must pass
--format jsonor--format jsonl.
New to the 7minai site? The homepage lists our latest builder guides. If you are picking a model to call from the CLI, our GPT-6.1 Sol switching guide covers the model side, and if you want an agent in your terminal rather than a raw API client, see the Codex Security guide.
What the OpenAI CLI is, and what it is not
The openai binary is a thin, resource-shaped client over the REST API. Running openai --help on v1.39.0 groups commands by task:
- Generate content:
responses,chat,completions,images,audio,videos - Live and realtime:
live,realtime - Data and retrieval:
files,uploads,vector-stores,embeddings,conversations - Tools and integrations:
containers,skills,webhooks - Models and jobs:
models,fine-tuning,batches - Safety and provenance:
moderations,content-provenance-checks,safety - Administration:
admin - Shortcuts:
transcribe,translate,speak(shortcuts foraudio ...) andprojects(shortcut foradmin projects) - Local tools:
tokenizerandcodex
It is not a coding agent. Running openai codex just prints instructions for the separate Codex CLI. On our install it said: “Codex CLI is a separate command named codex,” and pointed to npm install -g @openai/codex or brew install --cask codex. Think of openai as curl that already knows every endpoint, flag and file format, and codex as the agent.
Step 1: Install the CLI
Pick one of three routes.
Homebrew (macOS and Linux)
brew install openai/tools/openai
Go
You need Go 1.26 or later.
go install 'github.com/openai/openai-cli/cmd/openai@latest'
The binary lands in $HOME/go/bin (or $GOPATH/bin). If your shell can’t find openai, the README says to add that directory to your PATH:
# Add to your shell profile (.zshrc, .bashrc, etc.)
export PATH="$PATH:$(go env GOPATH)/bin"
Release binary (no Homebrew, no Go)
Each GitHub release has archives and packages for every platform, plus a checksums file. This is how we installed v1.39.0 on a Linux x86-64 box:
VER=1.39.0
curl -LO https://github.com/openai/openai-cli/releases/download/v$VER/openai_${VER}_linux_amd64.tar.gz
curl -LO https://github.com/openai/openai-cli/releases/download/v$VER/openai_${VER}_checksums.txt
grep linux_amd64.tar.gz openai_${VER}_checksums.txt | sha256sum -c
tar xzf openai_${VER}_linux_amd64.tar.gz
./openai --version
The checksum check printed OK, and the archive held the openai binary plus completions and man folders. --version printed openai version 1.39.0. Move the binary somewhere on your PATH (for example /usr/local/bin). On macOS, use the macos_arm64.zip or macos_amd64.zip asset. On Windows, use the matching windows_*.zip. On Debian/Ubuntu or Fedora you can install the .deb or .rpm asset instead.
One thing to know: per the README, the first eligible interactive run “quietly saves image picker Tab shortcuts for future Bash, zsh, and fish sessions.” The repo’s image picker shell setup doc explains how to opt out or remove them.
Step 2: Set your API key without leaking it
Create a key at platform.openai.com/settings/organization/api-keys. The CLI has a built-in guide that works offline and checks nothing:
openai help setup
For Bash or zsh, it recommends reading the key with hidden input so it never shows up in your shell history:
read -rs OPENAI_API_KEY
export OPENAI_API_KEY
printf '\n'
For PowerShell it gives:
$openaiKey = Read-Host "API key" -AsSecureString
$env:OPENAI_API_KEY = [System.Net.NetworkCredential]::new("", $openaiKey).Password
Remove-Variable openaiKey
The guide points out that these steps only set the key for the current shell session, so a new window needs it again. Fish users are told to run bash first. For a permanent setup, have a secret manager or your shell profile export the variable instead of typing the key into a command line.
Then check that it works:
openai models list
Environment variables and global flags
Every global flag has an environment variable. These are the ones you are most likely to need:
| Environment variable | Flag | Use |
|---|---|---|
OPENAI_API_KEY | --api-key | Standard endpoints |
OPENAI_ADMIN_KEY | --admin-api-key | Admin endpoints (openai admin ...) |
OPENAI_ORG_ID | --organization | Pick an organization |
OPENAI_PROJECT_ID | --project | Pick a project |
OPENAI_BASE_URL | --base-url | Custom backend. HTTPS is required except for localhost and loopback IPs |
OPENAI_CUSTOM_HEADERS | --header / -H | Extra headers, one Name: Value per line |
OPENAI_UNTRUSTED_STDIN | (none) | Treat piped JSON/YAML as literal data |
The --base-url rule matters if you point the CLI at a local OpenAI-compatible server: http://localhost:8080/v1 or http://127.0.0.1:... is allowed, but a remote plain-HTTP backend is rejected.
Step 3: Learn the command shape and the help system
Every command follows the same pattern:
openai <resource> [subresource...] <command> [flags...]
Help works without a key or network, which makes it the fastest way to discover flags:
openai # command groups by task
openai --help # groups plus global options
openai responses create --help # every flag for one command
openai help responses create # the same page
The README notes that older colon-style commands such as openai admin:organization:usage completions still work, so existing scripts won’t break.
Turn on shell completion
Completion covers commands, help topics, flags and file paths, also offline. From the shell completion doc:
# Bash
eval "$(openai @completion bash)"
# zsh, after compinit
source <(openai @completion zsh)
# fish
openai @completion fish | source
Add the line for your shell to your startup file to keep it. After upgrading the CLI, the docs say to reload completion, because already-loaded scripts keep their old behavior.
Step 4: Call a model with Responses
The README’s first example:
openai responses create \
--input "Say this is a test" \
--model gpt-5.5
Use any model ID your account can access. openai models list shows them. On v1.39.0, openai responses create --help lists the flags that map to the Responses API body, including:
--instructionsfor a system or developer message--max-output-tokensand--max-tool-calls--previous-response-idto continue a conversation--reasoning TEXT=VALUEfor reasoning-model options (repeatable)--text TEXT=VALUEfor text or Structured Outputs configuration--tooland--tool-choicefor built-in tools and function calling--streamfor server-sent events--backgroundto run the response in the background--store,--metadata,--service-tier,--safety-identifier
Prompt caching flags
The help text changed for caching. --prompt-cache-retention is now marked deprecated with “Use prompt_cache_options.ttl instead.” The new --prompt-cache-options TEXT=VALUE help says it is supported for gpt-5.6 and later models, that a request can write up to four explicit breakpoints, that mode set to explicit disables the implicit breakpoint, and that “the ttl defaults to 30m, which is currently the only supported value.” Check the prompt caching guide before you depend on any retention value, since the help text says it links there “for current details.”
Count tokens before you send
There are two token counters, and they answer different questions:
# Local, offline, no key: plain-text tokens only
openai tokenizer count --text "Hello, world!"
# Server-side: counts a full Responses request
openai responses input-tokens count --help
We ran the local one. openai tokenizer count --text "Hello, world!" --format json returned {"encoding":"o200k_base","input_bytes":13,"token_count":4}. openai tokenizer encodings lists o200k_base (default), cl100k_base, r50k_base (legacy) and p50k_base (legacy). openai tokenizer inspect --text "Hello" shows each token ID with its byte range and hex bytes. The tool’s own description warns that plain-text counts “are not complete request counts, billed usage, prices, or context-limit guarantees.” Use responses input-tokens count when you need the real number for a request.
Step 5: Work with files
Upload, inspect and download, straight from the README:
openai files upload "upload space.txt" --purpose user_data
openai files get file-example
openai files download file-example --output "downloaded copy.txt"
openai files download file-example > copy.txt
Replace file-example with the ID the upload returns. Three details trip people up:
- Uploads take a plain path, no
@.files upload --file "@notes.txt"means a file literally named@notes.txt. --purposeis required and the API validates file types against it.- Upload success does not mean processing is complete, as the command’s help says.
For JSON body fields, on the other hand, @myfile.ext does read a local file, and you can force encoding with @file:// (as a string) or @data:// (as base64). If you need a literal value starting with @, escape it as '\@abe'.
Piping input from somewhere you don’t trust
If a script pipes JSON or YAML from another program into the CLI, a crafted @/path value could make it read a local file. Turn that off:
untrusted-producer | OPENAI_UNTRUSTED_STDIN=1 openai <command> --file ./upload.txt
In this mode, @, @file:// and @data:// references in piped bodies, headers and query parameters are treated as literal text, and file uploads must come from explicit flags.
Step 6: Run a Batch job end to end
The CLI’s batch workflow is one of its most useful additions, because v1.39.0 added “waiting, result collection, and safe downloads” (#388 in the release notes). This walkthrough follows the batches doc.
1. Write requests.jsonl. One request per line, each with a unique custom_id, and one model per file:
{"custom_id":"example-1","method":"POST","url":"/v1/responses","body":{"model":"gpt-4.1-mini","input":"Return the word blue."}}
{"custom_id":"example-2","method":"POST","url":"/v1/responses","body":{"model":"gpt-4.1-mini","input":"Return the word green."}}
2. Upload it with purpose batch:
openai files upload --file requests.jsonl --purpose batch
3. Create the batch with the returned file ID:
openai batches create \
--input-file-id file_example \
--endpoint /v1/responses \
--completion-window 24h \
--metadata '{"job":"example"}' \
--output-expires-after.anchor created_at \
--output-expires-after.seconds 86400
The expiration flags keep the output and error files for one day. Omit them to use the service default.
4. Wait and save the results:
openai batches wait batch_example --output-dir ./results --poll-interval 5s --wait-timeout 30m
This saves output.jsonl and error.jsonl (whichever exist) into ./results, creating the folder if needed, and prints the final Batch object. Per the doc, it saves available files for completed, failed, expired and cancelled batches, and still exits nonzero for failed requests or unsuccessful end states. It never overwrites existing files and never creates or cancels a batch.
5. If one download failed, fetch just that file to a new path. Don’t resubmit the batch:
openai batches download batch_example --file error --output ./results/errors-retry.jsonl
Limits from the doc, which cites OpenAI’s batch create reference: up to 50,000 requests and 200 MB per input file, a 24h completion window, and output expiration between 3,600 and 2,592,000 seconds. Batch input files uploaded with purpose batch expire after 30 days by default.
Other batch commands: openai batches list --limit 20 --max-items 20, openai batches retrieve batch_example, and openai batches cancel.
Step 7: Script it with JSON, JSONL and transforms
This is the change most likely to break an old script. Successful responses print readable, labeled text by default, including when stdout is piped or redirected. The readable output doc says: “Scripts that parse responses must select their format explicitly.”
openai --format json files retrieve --file-id file-example
openai --format jsonl models list | jq '.id'
openai files list --format json > files.json
Available formats: auto, text, explore (interactive viewer, JSON when piped), json, jsonl, pretty, raw and yaml. For lists, json now produces one array, while jsonl produces one item per line. Use jsonl for record-by-record consumers. A .json filename does not select JSON; you still need --format json.
To pull out one field without jq, use GJSON paths:
openai --transform id --raw-output batches retrieve batch_example
openai files retrieve --file-id file-example --transform filename --raw-output
Always check the exit status. The doc warns that if a list fails partway, the bytes already written stay in the file and may be incomplete JSON. In Bash or zsh scripts, it recommends set -o pipefail.
Step 8: Generate an image (optional)
openai images generate --prompt "A tiny orange robot"
Per the README, this saves images to ~/Downloads/gpt-images/ and prints their paths. Without an explicit model it uses gpt-image-2.5-sunburst. Use --model for another ID, --name for a filename, or --output-dir for an existing folder. Existing files are kept. --format json returns the API data without saving. openai images preview "path/to/image.png" redisplays a saved image without an API call. images create-variation is retired; the README says to use images edit with a GPT Image model and a prompt instead.
Troubleshooting
We triggered these on v1.39.0 without an API key.
HTTP 401: Unauthorized. Running openai models list with no key printed:
HTTP 401: Unauthorized.
Request ID: ...
Check API key, organization and project.
Key setup: openai help setup
API error details: --format-error json.
It exited with status 1. Export OPENAI_API_KEY in the same shell, and check that OPENAI_ORG_ID / OPENAI_PROJECT_ID aren’t pointing at a project the key can’t use. Add --format-error json for the raw API error body.
Unknown command. Did you mean: openai help models list? That’s a typo like openai modles list (exit status 3). The CLI suggests the right help page but never runs the corrected command for you. Fix the spelling and keep your other arguments.
Invalid value for --limit. Expected an integer, such as 1. That’s a wrong type, for example --limit banana. Each type error points to the command’s --help.
Could not open the file for --file. The path is wrong or unreadable. Remember that uploads take a plain path with no @.
command not found: openai. After go install, add $(go env GOPATH)/bin to PATH. For typos in the executable name itself (such as opneai), the error recovery doc offers an optional hook: source <(openai @completion zsh --suggest-executable) (or the bash / fish equivalents).
Scripts suddenly get text instead of JSON. Add --format json or --format jsonl explicitly. The default for pipes is readable text.
--base-url rejected. Remote backends must use HTTPS. Plain HTTP is only allowed for localhost and loopback addresses.
Debugging requests. --debug prints HTTP request and response details, including bodies, to stderr. The README warns not to share those logs if they may contain sensitive payloads.
FAQ
I already have an openai command. Is it this one? Not necessarily. This CLI is a standalone Go binary from the openai/openai-cli repo, and other tools may install a command with the same name. Check which one you’re running with which openai and openai --version. v1.39.0 prints openai version 1.39.0.
Do I need a key to explore it? No. --help, openai help setup, shell completion and openai tokenizer all work offline without credentials.
Can it manage my organization? Yes, with an admin key in OPENAI_ADMIN_KEY. For example, openai admin organization usage completions --start-time 1735689600 --end-time 1735776000 --bucket-width 1d comes from the README. The v1.39.0 release notes also add guided admin-key setup and project API key scope and expiry updates.
Can I point it at a local or proxy endpoint? Yes, with --base-url or OPENAI_BASE_URL, as long as the endpoint is HTTPS or on localhost. Whether a given compatible server supports a given command depends on that server.
Does it replace Codex CLI? No. openai codex only prints install instructions for the separate codex command. If you want a terminal agent, compare it with our Grok Build CLI guide.
How do I keep it updated? Use brew upgrade if you installed with Homebrew, rerun go install ...@latest for Go, or download the newest release asset. Then reload shell completion.
Sources
- openai/openai-cli on GitHub and its README
- v1.39.0 release notes (October 19, 2026)
- Repo docs: batches, readable output, error recovery, shell completion
- OpenAI Batch guide and prompt caching guide
- Help output and tokenizer results from our own install of v1.39.0 on Linux amd64, October 21, 2026