Pi coding agent model configuration has three layers: use /login to log in to built-in providers (with a subscription or API key), or set environment variables; add endpoints compatible with the OpenAI, Anthropic, or Google APIs but not built into Pi to ~/.pi/agent/models.json; only services requiring special authentication or protocols need an extension. After choosing one, switch with /model.This article explains how to configure each layer based on Pi's documentation after the September 22, 2026 revision and the v0.99.2 source released on September 30, 2026, including the post-revision key lookup order, three defaults that silently cause problems for custom models, and a complete example for connecting an OpenAI-compatible endpoint.
First, make sure which Pi this is. This page covers the terminal coding agent published by Earendil at pi.dev, whose repository is earendil-works/pi (formerly badlogic/pi-mono), under the MIT license. It is not Inflection's Pi chatbot (pi.ai), Pi Network's cryptocurrency, Raspberry Pi, or another author's Oh My Pi.
Choose a connection method first
Pi's model documentation begins with this comparison:
| What you have | Recommended approach |
|---|---|
| A supported subscription plan | Log in with /login |
| An API key for a provider | Save it with /login, or set that provider's environment variable |
| A local GGUF model | Connect an llama.cpp router (managed with /llama) |
| An OpenAI-, Anthropic-, or Google-compatible endpoint | Add it to models.json |
| A provider with a custom protocol or authentication flow | Write or install a provider extension |
Pi includes a built-in model catalog and can overlay newer catalog data from pi.dev; it uses the cache when offline, and you can run pi update --models to force an update. You need custom model configuration only when Pi does not include the provider or endpoint you need.
Select a model in Pi
/model: Search for and select a model. Only models for which the provider has usable credentials are listed.- Press Ctrl+S on a model: save it as the default model for new sessions.
/thinking: Choose the thinking level for the current model; Pi lists only the levels that model supports. Press Ctrl+S to save it as the startup default.- Ctrl+P: Cycle through available models;
/scoped-modelscontrols and saves the cycle list.
Sessions record changes to the model and thinking level and restore them when resumed, but they do not change defaults for new sessions.
Key lookup order (changed after the revision)
When multiple key sources are configured, Pi's documentation gives this order: runtime --api-key → credentials stored in auth.json → models.json's apiKey → the provider's environment variable (or a cloud platform's environment credentials). Therefore, an old key saved with /login overrides the one you just added to the file. This is the most common reason that “I changed the configuration but it still uses the old account”; /logout removes stored credentials. Note that before the September 22 revision, the documentation placed environment variables before models.json, so online tutorials may still show the old order.
Another common misunderstanding: when a model does not appear in /model, the problem is usually credentials rather than invalid JSON. The documentation says custom models can be loaded from models.json, but they remain “unavailable” until Pi resolves usable credentials.
models.json: complete example for an OpenAI-compatible endpoint
Pi's own example uses local Ollama—the dummy key merely makes the model available; Ollama itself does not inspect it:
{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"models": [{ "id": "qwen2.5-coder:7b" }]
}
}
}For an authenticated endpoint such as Kunavo, use this:
{
"providers": {
"kunavo": {
"baseUrl": "https://api.kunavo.com/v1",
"api": "openai-completions",
"apiKey": "$KUNAVO_API_KEY",
"models": [
{
"id": "claude-sonnet-5",
"name": "Claude Sonnet 5",
"reasoning": true,
"input": ["text", "image"],
"contextWindow": 1000000,
"maxTokens": 128000
},
{
"id": "claude-haiku-4-5",
"name": "Claude Haiku 4.5",
"input": ["text", "image"],
"contextWindow": 200000,
"maxTokens": 64000
}
]
}
}
}baseUrlandapiare required.They can be specified at the provider or model level; according to the v0.99.2 source, if either is missing, Pi does not load the model.apiis not a choice among four fixed values.Before the September 22 revision, Pi's documentation listed four values for custom providers:openai-completions,openai-responses,anthropic-messages, andgoogle-generative-ai. The revised documentation no longer lists them; it only says “OpenAI-, Anthropic-, or Google-compatible endpoints” in the comparison table above, and its examples use onlyopenai-completions. The v0.99.2 source definesapias an arbitrary string and dispatches it to the corresponding one of ten built-in implementations—those four plusopenai-codex-responses,azure-openai-responses,google-vertex,mistral-conversations,bedrock-converse-stream, andpi-messages. Only the first four have ever been documented as custom-provider usage; the other six have not been tested here, and this page does not claim they can connect to third-party endpoints.- The
baseUrlofopenai-completionsmust include/v1.The documentation does not state this as a single rule, but all compatible-endpoint examples include a version path. Omitting/v1produces a 404, not an authentication error. - Do not hard-code keys.
apiKeyand header values can reference environment variables with$NAMEor${NAME}, contain literal values, or use!指令to obtain them; the documentation says commands inmodels.jsonrun on every request and are not cached. Keepauth.jsonand any key-retrieval command secret. - No restart is needed after editing.Pi rereads the file when
/modelis opened. Entries with the same ID inmodelsadd or replace that provider's model; to modify the metadata of a built-in model without replacing the entire catalog, usemodelOverrides.
Three defaults that silently cause problems
The September 22 documentation revision removed the field table, but the defaults remain in the source code (v0.99.2's provider-composer.ts). If a custom model omits a field, these defaults apply:
| Field | Default when omitted | What it causes |
|---|---|---|
cost | input, output, cacheRead, and cacheWrite all 0 | Costs at the bottom and in /session always show $0; this does not mean free, only that no price source is configured |
contextWindow | 128000 | Models with larger contexts are compressed too early |
maxTokens | 16384 | Long replies are truncated |
Additionally, reasoning defaults to false, and input defaults to text only. The Kunavo example above fills in context and output limits according to the price list and omits cost because prices hard-coded in a file become outdated quickly—if you want the bottom bar to show costs, fill in per-million-token prices yourself from the price list. Pi also supports promptCache (declaring the provider cache lifetime in seconds, for cache warming); the documentation recommends choosing a conservative value within the publicly stated range.
anthropic-messages: supported, but baseUrl is unsettled
anthropic-messages is one of the four values that the pre-revision documentation listed for custom providers, and Kunavo also provides an Anthropic Messages interface, so api: "anthropic-messages" is a viable path. However, Pi's documentation has never clearly stated whether this type of baseUrl should include /v1: before the revision, one example used https://proxy.example.com/v1 and another used https://proxy.example.com without a path; after the revision, both examples removed it, and the issue remains unsettled. If you take this route, try one first; if the first request returns a 404 (rather than a 401), change this line. compat also contains several switches designed specifically for non-native endpoints (such as supportsEagerToolInputStreaming and supportsStrictTools), but the documentation warns that compatibility settings should describe “verified behavioral differences” and should not be enabled merely because an endpoint claims OpenAI or Anthropic compatibility. The openai-completions example above avoids these issues, which is the real reason it is recommended as a starting point—not because it is faster.
An honest note about testing and payment
The configuration reference above was compiled from Pi's documentation and source code. Kunavo has not actually run Pi against its own endpoint—we have not run sessions, streaming, or tool round trips, nor confirmed which model ultimately receives the request. Keep the route you currently know works and give Pi a task that reads and writes real files; Pi relies on tool calls for nearly every step, so this first run is the best way to expose streaming or tool-format incompatibilities. The complete configuration page in English is Pi integration guide; pricing comparisons for the various paid routes, including Earendil's own Radius gateway, are in Pi coding agent pricing.
Kunavo uses prepaid, token-based billing with no monthly fee. The minimum top-up is $10; checkout is handled by Stripe, with Taiwan-supported credit cards (Visa, Mastercard, American Express, JCB, UnionPay), Apple Pay, Google Pay, and Link. JKO Pay and LINE Pay are not available. See the billing documentation; when ready, create an account and generate a key.
Frequently asked questions
How do I change models in the Pi coding agent?
In Pi, enter /model to search available models and select one; press Ctrl+S on a model to save it as the default for new sessions, and use /thinking to choose the thinking level (also press Ctrl+S to save it as the startup default). Ctrl+P cycles through available models, and /scoped-models controls the scope of the cycle. The menu lists only models from providers with usable credentials; sessions record model changes and restore them when resumed, but they do not change defaults for new sessions.
How do I connect Pi to a custom API endpoint?
For built-in providers, use /login or environment variables. For an endpoint that Pi does not include natively but that uses an API it supports (OpenAI-, Anthropic-, or Google-compatible), add a provider block to ~/.pi/agent/models.json with baseUrl, api, apiKey, and a models list. If either baseUrl or api is missing, Pi's source code will not load the model. api is not a fixed pick-list: before the September 22, 2026 documentation revision, Pi documented four values for custom providers (openai-completions, openai-responses, anthropic-messages, google-generative-ai); the revised documentation no longer lists them. The v0.99.2 source defines api as an arbitrary string and dispatches it to the corresponding one of ten built-in implementations; the other six have never been documented as custom-provider usage and have not been tested here. For an OpenAI-compatible endpoint, use openai-completions, which the revised documentation examples still use. Services requiring custom streaming, model discovery, or special authentication flows need a provider extension.
Where does Pi read API keys from, and in what order?
Pi's model documentation (October 1, 2026) gives this order: the runtime --api-key has highest priority, followed by credentials stored in auth.json (/login stores them there), then apiKey in models.json, and finally the provider's environment variable. Therefore, an old key previously saved with /login overrides the one you just added to models.json. The apiKey field can reference environment variables with $NAME or ${NAME}, contain a literal value, or execute a command beginning with ! to obtain the key. Note: before the September 22, 2026 documentation revision, this order was different (environment variables came before models.json); older tutorials may still show the old order.
Why does my custom model show $0 at the bottom of Pi?
Because all cost values for custom models default to 0 (according to the v0.99.2 source code). The bottom bar and /session show the prices in the configuration file, not what the endpoint actually charges. It is not free; there is simply no price source. Fill in input, output, cacheRead, and cacheWrite per million tokens according to the provider's price list. Also fill in contextWindow and maxTokens: when omitted, they default to 128000 and 16384 respectively, so large-context models will be compressed too early and replies will be truncated.
Should Pi's baseUrl include /v1?
It should for openai-completions. Pi's documentation does not state the rule in one sentence, but its compatible-endpoint example uses Ollama at http://localhost:11434/v1, and the pre-revision OpenRouter, Vercel AI Gateway, and llama.cpp examples also included the version path. Therefore, use a /v1 root for OpenAI-compatible endpoints, such as https://api.kunavo.com/v1. For anthropic-messages, there is no definitive answer: before the revision, one documentation example included /v1 and another did not; after the revision, both examples removed it, and the documentation still does not say which is correct.
Verified October 1, 2026: pi.dev/docs/latest/models (Choose a Model), the providers page, src/core/model-config.ts and provider-composer.ts at the earendil-works/pi v0.99.2 tag, and version information from the GitHub API. On the same day, the api field was separately verified against the values currently listed on the models page (only openai-completions in the Ollama example), the api type in v0.99.2 model-config.ts (arbitrary string, lines 191 and 233), custom-model dispatch in provider-composer.ts (line 579), and BUILTIN_APIS in packages/ai/src/compat.ts (line 180, ten types total). Kunavo has not actually run Pi against its own endpoint.