Codex CLI

One provider block in ~/.codex/config.toml, one key, every model on the list, pay as you go with no subscription.

Checked 2026-10-28

·All guides·Models

At the end you have Codex CLI on a provider block that points at https://api.uplinkswitch.com/v1, with one API key from the console and any model id on the list as model, Claude included. No subscription and no OpenAI key: each request bills per token to the prepaid balance behind that key, and the live table below is the price on each model today.

Before you start

You need Codex CLI installed, an account on the console with credit on it, and one key created there. Create the key for Codex alone, so its requests sit apart from any other tool on the Usage page. Nothing is installed on the UplinkSwitch side: the base URL https://api.uplinkswitch.com/v1 and the key are the whole configuration.

Where the settings are

~/.codex/config.toml, the user config. A project can carry its own .codex/config.toml, but that file ignores model_provider and model_providers, so the provider block goes in the user file (Codex docs, read 2026-10-28).

Codex only speaks the Responses API to a custom provider: wire_api = "responses" is the one value and the default. The gateway answers it at /v1/responses for every chat model, so the Anthropic-style variables from the Claude Code guide do not apply here.

The provider block

Add this to ~/.codex/config.toml. The table id, uplinkswitch, is yours to choose as long as it is not openai, ollama or lmstudio, which Codex reserves; model_provider must match it.

~/.codex/config.toml
model = "claude-sonnet-5-5"
model_provider = "uplinkswitch"

[model_providers.uplinkswitch]
name = "UplinkSwitch"
base_url = "https://api.uplinkswitch.com/v1"
env_key = "UPLINKSWITCH_API_KEY"
wire_api = "responses"
http_headers = { "HTTP-Referer" = "https://uplinkswitch.ai", "X-Title" = "Codex CLI" }

base_url is the /v1 root, as in the provider examples in the Codex docs. http_headers adds two static headers to every request so the console can attribute the traffic to Codex on the Usage page; env_http_headers does the same from environment variables if you prefer. The retry knobs request_max_retries (4), stream_max_retries (5) and stream_idle_timeout_ms (300000) keep their defaults unless you set them.

The key

env_key names the environment variable Codex reads the key from. Export it before you start Codex:

shell
export UPLINKSWITCH_API_KEY="sk-uplink-live-..."
codex

The variable is the provider's credential (Codex docs, read 2026-10-28); nothing else is set up on the Codex side.

Which model id to use

Any chat id on the Models page goes in model, Claude included: tool calling works on every chat model here, and Codex needs it for each file read, edit and command. The ids in the table below are the coding flagships; the uncensored coding builds take the same slot.

To switch for one run pass -m, or keep a profile file next to the config with its own model and start with --profile. Profile files use top-level keys, not a [profiles.name] table (Codex docs, read 2026-10-28).

profile
# ~/.codex/opus.config.toml
model = "claude-opus-5-5"
model_provider = "uplinkswitch"
shell
codex -m gpt-6.1-sol
codex --profile opus

Price per token

ModelInput / 1MCached input / 1MOutput / 1MList inputList outputUnder list
Claude Sonnet 5.5$1.20$0.075$3.00$2.00$10.0040% on input
Claude Opus 5.5$2.80$0.15$10.00$4.00$20.0030% on input
GPT-6.1 Sol$0.60$0.06$3.00$2.00$10.0070% on input
Gemini 3.1 Pro Preview$1.20$0.12$7.20$2.00$12.0040% on input
DeepSeek V4 Pro 0813$0.792$0.0625$1.188$1.32$3.9640% on input
Kimi K3$0.90$0.12$4.50$3.00$15.0070% on input

USD per million tokens, live from GET /v1/catalog. List: the developer’s own published price. Cached input bills at the cache-read rate where the model has one, with no switch to set.

Codex sends the conversation and every tool result back on each step, so the input column decides what a session costs, and cached input brings it down further on the models that have a cache-read rate. The Claude Code pricing post works an hour of agent steps into days and months at these rates, and the 30-day price lock on each top-up holds them.

Check it

From a shell, the same key should list the models:

shell
curl https://api.uplinkswitch.com/v1/models -H "Authorization: Bearer $UPLINKSWITCH_API_KEY"

Then send one request the way Codex does, to /v1/responses. -i prints the headers: x-uplink-request-id names the request, and on a non-streamed answer x-uplink-charge is what it cost in USD.

shell
curl -i https://api.uplinkswitch.com/v1/responses \
  -H "Authorization: Bearer $UPLINKSWITCH_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model": "claude-sonnet-5-5", "input": "Say ok."}'

Then run codex and ask it something. The request shows on the console Usage page within seconds, with its tokens and the same charge, filterable by key and model.

Errors you will see

  • 401 invalid_api_key: the key is wrong, revoked, or the variable named in env_key is not exported in the shell that runs Codex. Export it again, or copy the key from the console.
  • 404 model_not_found: the id in model is not on the Models page at this base URL. Copy it from the page rather than typing it.
  • 402 insufficient_balance: the balance does not cover the request. Top up in the console.
  • 402 monthly_limit_reached: the key hit the monthly spend limit you set on it. Raise it in the console, or wait for the first of the month.
  • 403 scope_denied: the key carries a model allowlist and this id is not on it. Pick an allowed id or use a key without the limit.
  • 429 rate_limited: the key hit its requests-per-minute or concurrency limit. The response carries retry-after in seconds; Codex retries on its own up to request_max_retries.
  • The block has no effect: model_provider does not match the table id, or the block sits in a project .codex/config.toml, which ignores it. Move it to ~/.codex/config.toml.

Good to know

  • -c key=value overrides any config key for one run; values parse as TOML where they can.
  • model_reasoning_effort in the same file sets the reasoning level for models that take one.
  • Cached input bills at the cache-read price on the model page by itself; there is no switch to set.
  • A monthly spend limit and a model allowlist on the Codex key cap what it can spend and which ids it can call.

The same key in other agents

Claude Code takes two environment variables; OpenCode and OpenClaw read a provider block much like this one; Cline, Kilo Code and Cursor take the base URL and key in a settings panel. One balance serves all of them, and a key per tool keeps the spend apart in the console.

Questions

Yes. Codex takes a custom provider as a model_providers table in ~/.codex/config.toml with a base_url and an env_key, and model_provider points at it (Codex docs, read 2026-10-28). The block on this page is that table filled in for https://api.uplinkswitch.com/v1.

Yes. Set model to claude-sonnet-5-5 or claude-opus-5-5 under the provider block above. Codex speaks the Responses API, and every model on the list answers it at https://api.uplinkswitch.com/v1/responses, so the Claude ids need no second provider. Claude Sonnet 5.5 is $1.20 per million input tokens and $3.00 per million output tokens here today.

Not with a custom provider. Codex reads the key named in env_key and sends every request to base_url, so no ChatGPT plan and no OpenAI key are involved. The bill is per token to the prepaid balance behind the key.

~/.codex/config.toml, the user config. A project can add .codex/config.toml, but that file ignores model_provider and model_providers, so the provider block has to live in the user file (Codex docs, read 2026-10-28).

Change model in config.toml, pass -m with an id for one run, or keep a profile file such as ~/.codex/opus.config.toml and start with --profile opus (Codex docs, read 2026-10-28). Any id on the Models page works under the same provider.

The id in model is not on the Models page at this base URL, so the gateway returned 404 model_not_found. Copy the id from the Models page rather than typing it; a dot, a dash or a date suffix out of place is enough. Also check base_url ends in /v1 and model_provider matches the table name.

Get a key in the console: prepaid credit, no subscription, 30-day price lock.