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, .apk and Arch packages.
  • Install: brew install openai/tools/openai, or go install 'github.com/openai/openai-cli/cmd/openai@latest' (Go 1.26 or later).
  • Command shape: openai <resource> [subresource...] <command> [flags...].
  • Auth: OPENAI_API_KEY for normal endpoints, OPENAI_ADMIN_KEY for admin endpoints.
  • Output: readable text by default, even in pipes. Scripts must pass --format json or --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 for audio ...) and projects (shortcut for admin projects)
  • Local tools: tokenizer and codex

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 variableFlagUse
OPENAI_API_KEY--api-keyStandard endpoints
OPENAI_ADMIN_KEY--admin-api-keyAdmin endpoints (openai admin ...)
OPENAI_ORG_ID--organizationPick an organization
OPENAI_PROJECT_ID--projectPick a project
OPENAI_BASE_URL--base-urlCustom backend. HTTPS is required except for localhost and loopback IPs
OPENAI_CUSTOM_HEADERS--header / -HExtra 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:

  • --instructions for a system or developer message
  • --max-output-tokens and --max-tool-calls
  • --previous-response-id to continue a conversation
  • --reasoning TEXT=VALUE for reasoning-model options (repeatable)
  • --text TEXT=VALUE for text or Structured Outputs configuration
  • --tool and --tool-choice for built-in tools and function calling
  • --stream for server-sent events
  • --background to 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:

  1. Uploads take a plain path, no @. files upload --file "@notes.txt" means a file literally named @notes.txt.
  2. --purpose is required and the API validates file types against it.
  3. 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