Run Goose on a custom OpenAI-compatible endpoint.

Updated 2026-07-16

Goose's openai provider takes a host override. Set GOOSE_PROVIDER=openai, point OPENAI_HOST at https://api.apisrouter.com, export one key, and the whole agent loop, tool calls included, routes through a single endpoint with every catalog model addressable by id.

Quick answer: keep the openai provider, override the host.

Goose ships a documented custom-endpoint path: keep GOOSE_PROVIDER set to openai and override where that provider points. OPENAI_HOST replaces the default api.openai.com host, OPENAI_API_KEY authenticates, and GOOSE_MODEL picks the model by exact id. The request path is separate: OPENAI_BASE_PATH defaults to v1/chat/completions and normally needs no change. Note the shape carefully, because it is the reverse of most tools in this class: OPENAI_HOST takes the bare host, https://api.apisrouter.com, with no /v1 suffix. The /v1/chat/completions part lives in OPENAI_BASE_PATH. Appending /v1 to the host doubles the path and produces 404s that look like a broken gateway.

export GOOSE_PROVIDER=openai
export OPENAI_HOST=https://api.apisrouter.com   # bare host, no /v1
export OPENAI_API_KEY=sk-APIsRouter-...
export GOOSE_MODEL=claude-sonnet-4-6

goose session

How Goose talks to its provider.

Goose (block on GitHub, roughly 51K stars) is an autonomous engineering agent from Block that plans tasks, edits files, runs shell commands, and drives MCP-based extensions. All of that sits on one model conversation: every step of the loop is a /v1/chat/completions request with tool definitions attached, so the provider configuration decides where the entire agent runs. Configuration is layered. The interactive path is goose configure, which for the openai provider prompts for the API key and an optional custom host, then writes non-secret settings like GOOSE_PROVIDER and GOOSE_MODEL to ~/.config/goose/config.yaml; the desktop app exposes the same provider settings through its UI. Secrets are handled separately: keys go to the system keychain or come from environment variables, and a key pasted directly into config.yaml is ignored rather than read. Environment variables override the file, which is what makes the env path above work everywhere from a laptop shell to a CI runner. Because Goose passes GOOSE_MODEL through as a plain string, the id can be anything the endpoint behind OPENAI_HOST serves: a Claude id today, a Kimi or Qwen id tomorrow, one variable apart.

The declarative path: a custom provider file.

Beyond the env override, current Goose documentation also describes declarative custom providers: a JSON file dropped into ~/.config/goose/custom_providers/ (per-platform config directory on Windows) that registers a named provider alongside the built-ins. The file declares the engine (openai for chat-completions endpoints), which environment variable holds the key, the endpoint URL, and the models the provider offers. Mind the URL convention here, because it flips again: unlike OPENAI_HOST, the custom provider's base_url is the full request URL including the path, https://api.apisrouter.com/v1/chat/completions. Each models entry carries a context_limit so Goose knows the window it can pack. The declarative file is the better fit when you want the gateway to appear as its own named provider in Goose's provider list, with its own key variable, rather than occupying the openai slot. The env override is the better fit for CI and quick switching. Both end at the same endpoint; pick one and avoid stacking them.

{
  "name": "apisrouter",
  "display_name": "APIsRouter",
  "engine": "openai",
  "api_key_env": "APISROUTER_API_KEY",
  "base_url": "https://api.apisrouter.com/v1/chat/completions",
  "models": [
    { "name": "claude-sonnet-4-6", "context_limit": 200000 },
    { "name": "claude-opus-4-7",   "context_limit": 200000 },
    { "name": "kimi-k2.7-code",    "context_limit": 200000 }
  ],
  "supports_streaming": true,
  "requires_auth": true
}

Choosing a model for an autonomous agent.

The practical workflow is to hold your task set fixed and rotate GOOSE_MODEL across two or three candidates for a few sessions each. Because every candidate routes through the same key, the per-key usage view prices each experiment without any bookkeeping on your side.

  • Goose runs unattended stretches: plan, edit, run, read output, repeat. Tool-call reliability matters more than raw eloquence, which is why claude-sonnet-4-6 and claude-opus-4-7 are the defaults people converge on for the main loop.
  • Coding-tuned ids like kimi-k2.7-code are worth testing for refactor-heavy sessions; through a gateway that test is one GOOSE_MODEL change, not a provider migration.
  • Long sessions compound context. A model with a genuine 200k window, declared honestly via context_limit in the declarative path, lets Goose carry more session history before summarizing.
  • For scripted or CI usage, a mid-tier id (gpt-5.4, qwen3.7-max) often clears the bar for well-scoped tasks at a fraction of frontier spend; measure on your own tasks before defaulting up.

Pay-as-you-go · transparent per-model pricing

Selected models are priced below official list prices. Exact input, output, cache, and per-request prices are shown for each model.

ModelOfficial PriceOur Price
Claude Sonnet 4.6$3.00 / $15.00 per M$2.40 / $12.00 per M
Claude Opus 4.7$5.00 / $25.00 per M$4.00 / $20.00 per M
GPT-5.4$2.50 / $15.00 per M$2.00 / $12.00 per M
Kimi K2.7 Code$0.95 / $4.00 per M$0.85 / $3.60 per M
Qwen 3.7 Max$2.50 / $7.50 per M$2.25 / $6.75 per M

The failure modes specific to Goose.

/v1 appended to OPENAI_HOST. The host variable takes the bare host; the path lives in OPENAI_BASE_PATH, which already defaults to v1/chat/completions. https://api.apisrouter.com/v1 as the host yields /v1/v1/... requests and 404s. This is the single most common mistake, precisely because every other tool wants the /v1 suffix. The full-URL convention in custom provider files. The declarative base_url is the complete request URL including /v1/chat/completions, the opposite convention from OPENAI_HOST. Copying a bare host into a custom provider file breaks it just as surely as copying a full URL into OPENAI_HOST. Keys in config.yaml do not authenticate. Goose reads secrets from the keychain or the environment, and ignores key values placed in config.yaml. If a 401 persists after editing the file, that is why; export the variable or re-run goose configure and enter the key when prompted. Desktop sessions do not see shell exports. The desktop app inherits nothing from your terminal profile. Configure the provider through the desktop settings UI, or launch from a shell that has the variables set. Stacked configuration sources. An old OPENAI_HOST export can override what you just set in config.yaml, because environment beats file. When routing looks wrong, print the relevant variables in the same shell that launches Goose before blaming either layer.

Who routes Goose through a gateway.

  • Engineers running Goose as a daily driver who want Claude, GPT, Kimi, and Qwen reachable behind one key instead of one credential set per vendor.
  • Teams putting Goose into CI or scheduled jobs. The env-only path means the runner needs exactly two routing variables and one secret, easy to inject and easy to rotate.
  • Developers comparing agent models on real tasks. Each candidate is one GOOSE_MODEL value against the same endpoint, priced automatically by per-key usage.
  • Platform teams that want agent spend visible per key and per model on one billing surface, rather than reconciling several vendor dashboards.
  • Developers without access to a given vendor's billing. Top-up based access with no card requirement removes the sign-up dependency per provider.

Verify the endpoint and debug the first session.

Confirm the gateway serves the id in GOOSE_MODEL before starting a session; the /v1/models listing is the authoritative spelling, version suffixes included. First-session failures are consistent. A 404 means the host and path composed wrong, almost always /v1 in OPENAI_HOST. A 401 means the key is not where Goose looks: not exported in the shell that launched it, not in the keychain, or sitting uselessly inside config.yaml. A model-not-found error from the gateway is an id typo in GOOSE_MODEL. If the session starts but tool calls behave oddly, check you are on a model that genuinely supports tool use; the ids in the table above all do. Once the loop runs, the APIsRouter console shows per-request model, token counts, and spend. An autonomous agent is the workload where this matters most: sessions are long, tool-call turns are many, and the usage view is how you see what an afternoon of Goose actually cost.

curl -s https://api.apisrouter.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY" | head -50

FAQ

Can Goose drive Claude or Kimi models through its openai provider?

Yes. The openai provider is a protocol client, not a vendor lock: with OPENAI_HOST pointed at a multi-vendor endpoint, GOOSE_MODEL can be any served id, Claude, Kimi, and Qwen included, and the agent loop with tool calling works unchanged.

Does OPENAI_HOST need the /v1 suffix?

No, and adding it breaks routing. OPENAI_HOST takes the bare host (https://api.apisrouter.com); the request path lives in OPENAI_BASE_PATH, which defaults to v1/chat/completions. This is the reverse of the convention most tools use.

What is the difference between the env override and a custom provider file?

The env override reroutes the built-in openai provider: fastest to set up, ideal for CI. A custom provider JSON in ~/.config/goose/custom_providers/ registers the gateway as its own named provider with its own key variable and model list. Same endpoint either way; pick one.

Why does Goose ignore the API key I put in config.yaml?

By design. Goose reads secrets from the system keychain or environment variables and ignores keys in config.yaml. Export OPENAI_API_KEY (or your api_key_env variable), or enter the key through goose configure or the desktop settings so it lands in the keychain.

Do the CLI and the desktop app share this configuration?

They share config.yaml and the keychain, but not your shell environment: variables exported in a terminal reach CLI sessions launched from that terminal, not the desktop app. Configure the desktop app through its settings UI, or rely on the shared config file plus keychain.

Which model should GOOSE_MODEL name for agent work?

Start with claude-sonnet-4-6 for the main loop; it holds up well on multi-step tool use. Test kimi-k2.7-code on refactor-heavy sessions and a mid-tier id on well-scoped CI tasks. Behind one endpoint each test is a single variable change.