pi coding agent
One provider block in models.json: base URL, key, the model ids you want, every model on one key, pay as you go.
At the end you have pi on a provider named uplinkswitch in ~/.pi/agent/models.json, pointed at https://api.uplinkswitch.com/v1 with one key from the console. Every id you list appears in /model, Claude included, and each request bills per token to the prepaid balance behind the key: 335 chat models on one key, no subscription. The table below is what the 6 ids in the block cost today.
Before you start
You need pi installed (curl -fsSL https://pi.dev/install.sh | sh, or the npm package @earendil-works/pi-coding-agent, Node 22.19 or newer), an account on the console with credit on it, and one key created there. Create the key for pi alone, so its requests sit apart from any other tool on the Usage page. Nothing is installed on the UplinkSwitch side: the base URL and the key are the whole configuration.
Where the settings are
pi keeps user configuration in its agent directory, ~/.pi/agent by default (PI_CODING_AGENT_DIR moves it). Compatible endpoints and custom models go in models.json there; startup defaults go in settings.json. pi takes a compatible endpoint through models.json, so the Anthropic-style variables from the Claude Code guide do not apply.
The field reference is pi's models-configuration.md and the compatible-endpoint walkthrough is in models.md, both read 2026-10-28.
The provider block
Any key under providers that is not a built-in id adds a provider; it needs baseUrl and an api. Use openai-completions, which calls /chat/completions under the base URL. The cost fields are what pi shows in its own session figure; they are filled here from the live catalog. headers are sent on every request, and the gateway files X-Title as the client app on the Usage page.
{
"$schema": "https://pi.dev/schemas/models.schema.json",
"providers": {
"uplinkswitch": {
"name": "UplinkSwitch",
"baseUrl": "https://api.uplinkswitch.com/v1",
"api": "openai-completions",
"apiKey": "$UPLINKSWITCH_API_KEY",
"headers": {
"HTTP-Referer": "https://uplinkswitch.ai",
"X-Title": "pi"
},
"models": [
{
"id": "claude-sonnet-5-5",
"name": "Claude Sonnet 5.5",
"input": [
"text",
"image"
],
"cost": {
"input": 1.2,
"output": 3,
"cacheRead": 0.075,
"cacheWrite": 1.2
},
"contextWindow": 1000000,
"maxTokens": 128000
},
{
"id": "claude-opus-5-5",
"name": "Claude Opus 5.5",
"input": [
"text",
"image"
],
"cost": {
"input": 2.8,
"output": 10,
"cacheRead": 0.15,
"cacheWrite": 2.8
},
"contextWindow": 1000000,
"maxTokens": 128000
},
{
"id": "gpt-6.1-sol",
"name": "GPT-6.1 Sol",
"input": [
"text",
"image"
],
"cost": {
"input": 0.6,
"output": 3,
"cacheRead": 0.06,
"cacheWrite": 0.6
},
"contextWindow": 1050000,
"maxTokens": 128000
},
{
"id": "gemini-3.1-pro-preview",
"name": "Gemini 3.1 Pro Preview",
"input": [
"text",
"image"
],
"cost": {
"input": 1.2,
"output": 7.2,
"cacheRead": 0.12,
"cacheWrite": 1.2
},
"contextWindow": 1048576,
"maxTokens": 65536
},
{
"id": "deepseek-v4-pro-0813",
"name": "DeepSeek V4 Pro 0813",
"cost": {
"input": 0.792,
"output": 1.188,
"cacheRead": 0.0625,
"cacheWrite": 0.792
},
"contextWindow": 1048576,
"maxTokens": 393216
},
{
"id": "kimi-k3",
"name": "Kimi K3",
"input": [
"text",
"image"
],
"cost": {
"input": 0.9,
"output": 4.5,
"cacheRead": 0.12,
"cacheWrite": 0.9
},
"contextWindow": 1048576,
"maxTokens": 1048576
}
]
}
}
}A models entry needs id and the four cost fields; name is what /model shows and what --model matches by fuzzy search. Omitted contextWindow and maxTokens fall back to pi's defaults of 128,000 and 16,384 tokens, so set them where the model page lists larger figures. Add any other id from the Models page the same way.
The key
"$UPLINKSWITCH_API_KEY" reads the variable from the environment, so export it in the shell that starts pi. A literal key in apiKey works too, and a value starting with ! runs a command and uses its output, for a secret manager.
export UPLINKSWITCH_API_KEY="sk-uplink-live-..."
pi --model uplinkswitch/claude-sonnet-5-5Pick the model
--model takes provider/id, an exact id, or a fuzzy match on id or name; --provider uplinkswitch narrows the lookup to this provider. Inside pi, /model searches every model with usable authentication, Ctrl+S on a model saves it as the default for new sessions, and Ctrl+P cycles. The same default as a file:
{
"defaultProvider": "uplinkswitch",
"defaultModel": "claude-sonnet-5-5"
}enabledModels in the same file, or --models on the command line, limits the startup pick and the cycle to a list of exact ids, fuzzy matches or globs.
Which model id to list
Any chat id on the Models page. Tool calling works on every chat model here, and pi's agent needs it for every file read, edit and shell command, so no id on the list is a dead end. The ids in the block are the usual coding set: a Claude flagship for planning and hard edits, GPT-6.1 Sol and Gemini 3.1 Pro as second opinions, DeepSeek V4 Pro and Kimi K3 where the input price decides.
| Model | Input / 1M | Output / 1M | Cached input / 1M | List input | Under list |
|---|---|---|---|---|---|
| Claude Sonnet 5.5 | $1.20 | $3.00 | $0.075 | $2.00 | 40% on input |
| Claude Opus 5.5 | $2.80 | $10.00 | $0.15 | $4.00 | 30% on input |
| GPT-6.1 Sol | $0.60 | $3.00 | $0.06 | $2.00 | 70% on input |
| Gemini 3.1 Pro Preview | $1.20 | $7.20 | $0.12 | $2.00 | 40% on input |
| DeepSeek V4 Pro 0813 | $0.792 | $1.188 | $0.0625 | $1.32 | 40% on input |
| Kimi K3 | $0.90 | $4.50 | $0.12 | $3.00 | 70% on input |
USD per million tokens, live from GET /v1/catalog. List: the developer’s own published price. The same figures fill the cost fields in the block above.
Input decides the cost of an agent session, because the conversation and every tool result travel up again on each step. Cached input bills at the cache-read price by itself; there is no switch to set. The Claude Code pricing post works an hour, a day and a month of agent steps at these rates.
Check it
Confirm the key from a shell before you open pi:
curl https://api.uplinkswitch.com/v1/models -H "Authorization: Bearer $UPLINKSWITCH_API_KEY"Then run pi and /model: UplinkSwitch and the ids you listed appear in the picker. Send one prompt. The request shows on the console Usage page within seconds, with its tokens, its charge and pi as the client app.
Errors you will see
401 invalid_api_key: the key is wrong, revoked, or the variable was not set in the shell that started pi. Copy it again from the console and export it beforepi. If a literal key still returns 401, add"authHeader": trueto the provider so pi sends it asAuthorization: Bearer.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.404 model_not_found: theidin the models array is not on the Models page at this base URL. Copy the id from the page rather than typing it: a dot, a dash or a date suffix out of place is enough.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 carriesretry-afterin seconds. Wait that long and resend.- The model is missing from
/model: pi hides a custom model until it can resolve the provider's credentials. Check the variable, then open/modelagain to reload the file.
Good to know
- Opening
/modelreloadsmodels.json, so a new id needs no restart. - A
modelsentry whose id matches an existing model on the provider replaces it;modelOverrideschanges metadata on a built-in model without replacing the list. input: ["text", "image"]lets pi attach screenshots to a model that takes them; the block sets it where the model page lists image input.- A key per tool keeps pi's spend apart from Claude Code or OpenCode on the Usage page; all keys draw on the same balance. A monthly spend limit on the pi key caps what an unattended run can cost.
The same key in other agents
OpenCode and OpenClaw read a provider block much like pi's; Claude Code takes two environment variables; Cline and Kilo Code take the base URL and key in a settings panel. One balance serves all of them.
Questions
Get a key in the console: prepaid credit, no subscription, 30-day price lock.