Published on

How to Add Custom Model Provider to Claude Code: 2026 Guide

Authors

Claude Code uses one wire protocol: the Anthropic Messages API. Any endpoint that speaks it can back Claude Code, and you switch endpoints with three environment variables. The same mechanism powers OpenRouter, DeepSeek, Kimi, a corporate LiteLLM gateway, and a local Ollama model.

Each section below is a complete recipe. For the full variable list, read the Claude Code environment variables reference.

Table of Contents

Table of Contents

How the pieces fit

Every custom provider setup has four parts:

  1. A base URL that speaks the Anthropic Messages API.
  2. A credential (a bearer token or an API key).
  3. Model names the provider understands, mapped onto Claude Code's alias tiers.
  4. Optionally, extra request headers or a picker entry.

A request flow looks like this:

Claude Code
   |
   |  POST https://provider.example/anthropic/v1/messages
   |  Authorization: Bearer <credential>
   v
Provider endpoint (Anthropic Messages format)
   |
   v
Target model

Claude Code does not translate protocols. If a provider only speaks OpenAI's chat format, you need a translation layer such as a LiteLLM gateway or a router. Pointing ANTHROPIC_BASE_URL at a raw OpenAI endpoint will not work.

Put the config in a settings file

You can export variables in your shell, but a settings file is better. It applies to every launch, including background agents, and it survives new terminals.

Pick one of:

  • ~/.claude/settings.json for all your projects.
  • .claude/settings.local.json for one project, gitignored.

Do not use .claude/settings.json. That file is committed, and it would leak your credential to everyone who clones the repository.

The shape is always the same:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://provider.example/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "your-key"
  }
}

The env value overrides a shell export of the same variable, so the file is the source of truth once it exists.

Map every alias tier

Claude Code uses several model slots internally:

  • opus and sonnet for main work, with opusplan splitting between plan and execution.
  • haiku for small, fast, background work.
  • fable as a fallback family.
  • Subagents, which follow CLAUDE_CODE_SUBAGENT_MODEL.

If you only set the main model, background tasks and subagents keep requesting claude-... names the provider does not have, and they fail quietly. Map all of them:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://provider.example/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "your-key",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "provider-strong-model",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "provider-balanced-model",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "provider-fast-model",
    "ANTHROPIC_DEFAULT_FABLE_MODEL": "provider-strong-model",
    "CLAUDE_CODE_SUBAGENT_MODEL": "provider-balanced-model"
  }
}

ANTHROPIC_MODEL sets a single main model when you do not want per-tier mapping. /model and --model override it for a session.

OpenRouter

OpenRouter exposes an Anthropic-compatible skin, so no local proxy is needed. One key reaches hundreds of models.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://openrouter.ai/api",
    "ANTHROPIC_AUTH_TOKEN": "sk-or-v1-your-key",
    "ANTHROPIC_API_KEY": "",
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
  }
}

Four details:

  • The base URL is https://openrouter.ai/api, not /api/v1.
  • ANTHROPIC_API_KEY must be explicitly empty. An unset variable is not enough, because a stale key can override the auth and silently fall back to Anthropic.
  • OpenRouter guarantees Anthropic-format behavior best with its first-party Anthropic provider. Set Anthropic first in your OpenRouter provider routing if tool calls misbehave on other models.
  • If a cached Claude login is active, run /logout and relaunch, or the login can override the environment.

Model slugs come from the OpenRouter model list. Map tiers like this:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://openrouter.ai/api",
    "ANTHROPIC_AUTH_TOKEN": "sk-or-v1-your-key",
    "ANTHROPIC_API_KEY": "",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "anthropic/claude-opus-4.6",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "anthropic/claude-sonnet-4.6",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "google/gemini-2.5-flash"
  }
}

GitHub Actions: the Claude Code action reads ANTHROPIC_BASE_URL and ANTHROPIC_CUSTOM_HEADERS from the workflow env block, and it sets the supplied key as ANTHROPIC_API_KEY, so it arrives in the x-api-key header. Pass the OpenRouter key through the action's anthropic_api_key input and set the base URL in env.

DeepSeek

DeepSeek serves an Anthropic-compatible endpoint directly, so no router is required.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "sk-your-deepseek-key",
    "ANTHROPIC_MODEL": "deepseek-chat",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-chat",
    "CLAUDE_CODE_SUBAGENT_MODEL": "deepseek-chat"
  }
}

DeepSeek auto-maps Claude model names for common cases, but setting the tier variables explicitly avoids surprises. Check the DeepSeek API docs for the current model IDs, because the lineup changes faster than most blogs update.

Kimi and Moonshot

Moonshot AI, the maker of Kimi, publishes an Anthropic-compatible base URL.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.moonshot.ai/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "sk-your-moonshot-key",
    "ANTHROPIC_MODEL": "kimi-k2-0905-preview",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "kimi-k2-0905-preview",
    "CLAUDE_CODE_SUBAGENT_MODEL": "kimi-k2-0905-preview"
  }
}

For a mainland China account, use the .cn host instead of .ai. Verify the current model name in the Moonshot platform console before you commit it to a settings file.

Z.ai GLM

Z.ai exposes an Anthropic-compatible endpoint for its GLM coding models.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "your-zai-key"
  }
}

Check the Z.ai console for the exact model IDs.

Ollama (local, no API cost)

Ollama added native Anthropic Messages compatibility in early 2026, so Claude Code can talk to a local model without a proxy.

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://localhost:11434",
    "ANTHROPIC_AUTH_TOKEN": "ollama",
    "ANTHROPIC_MODEL": "qwen3-coder"
  }
}

Ollama ignores the token value, but Claude Code expects a credential, so set a placeholder. Pull the model first with ollama pull qwen3-coder. A local model is the cleanest option for private or offline work, at the cost of speed and quality on hard tasks.

LiteLLM gateway (many providers, one endpoint)

LiteLLM is an Anthropic-compatible gateway that fronts many providers and adds budgets, cost tracking, and fallbacks. Anthropic names LiteLLM as an example gateway.

Install and configure:

pip install 'litellm[proxy]'

config.yaml:

model_list:
  - model_name: anthropic-claude
    litellm_params:
      model: anthropic/claude-sonnet-4-6
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: openai-route
    litellm_params:
      model: openai/gpt-5
      api_key: os.environ/OPENAI_API_KEY
  - model_name: gemini-route
    litellm_params:
      model: gemini/gemini-2.5-pro
      api_key: os.environ/GEMINI_API_KEY
litellm_settings:
  master_key: os.environ/LITELLM_MASTER_KEY

Start it on port 4000:

litellm --config config.yaml

Point Claude Code at it:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://localhost:4000",
    "ANTHROPIC_AUTH_TOKEN": "sk-your-litellm-master-key",
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
  }
}

The gateway must expose /v1/messages and /v1/messages/count_tokens, and forward anthropic-beta and anthropic-version. Discovery populates the /model picker from the gateway's model list.

Security note: LiteLLM versions 1.82.7 and 1.82.8 on PyPI shipped credential-stealing malware. Pin a clean version and rotate any credentials that ran through those releases.

Add extra headers

Gateways often tag requests with a tenant or routing header. Add them with ANTHROPIC_CUSTOM_HEADERS, using \n between pairs in JSON.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",
    "ANTHROPIC_AUTH_TOKEN": "sk-gateway-key",
    "ANTHROPIC_CUSTOM_HEADERS": "X-Org-Route: prod\nX-Tenant: example"
  }
}

Make the provider's models selectable

By default the /model picker lists built-in Claude models. To fix that:

  • Turn on discovery: CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1. The picker fills from the gateway's model list.
  • Add one entry manually with ANTHROPIC_CUSTOM_MODEL_OPTION, plus optional name and description.
{
  "env": {
    "ANTHROPIC_CUSTOM_MODEL_OPTION": "provider.example/strong-model",
    "ANTHROPIC_CUSTOM_MODEL_OPTION_NAME": "Provider Strong",
    "ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION": "Routed through the provider gateway"
  }
}

Verify the setup

After any change, restart Claude Code and run /status. It should show the gateway base URL and the credential source, for example Auth token: ANTHROPIC_AUTH_TOKEN. If it shows a saved login, the variable is not being read.

Then:

  1. Run /model and confirm the provider models appear.
  2. Send a trivial prompt and confirm you get a reply, not a 401 or a model-not-found error.
  3. Watch a background task complete, which proves the haiku tier is mapped.

If the picker is empty, run claude --debug and look for gateway discovery lines.

Common errors

ErrorCauseFix
401Credential in the wrong headerTry ANTHROPIC_AUTH_TOKEN and ensure ANTHROPIC_API_KEY is empty
Model not foundUnmapped alias tierSet all ANTHROPIC_DEFAULT_*_MODEL variables
Requests still hit AnthropicSaved login activeRun /logout or unset the login, then relaunch
Tool calls fail on a modelEndpoint lacks full tool-use supportPrefer the provider's Anthropic-compatible or first-party route
Empty pickerDiscovery offSet CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1
Remote Control or voice missingGateway credential activeExpected; disable the gateway to restore them

Which approach should you use

SituationUse
One non-Anthropic model, provider has an Anthropic endpointNative variables plus settings file
Many models, one key, no installOpenRouter
Private or offline workOllama
Many providers, budgets, and cost trackingLiteLLM gateway
Per-task routing and failover across agentsA router such as Claude Code Router
Enterprise governanceManaged settings plus an approved gateway

Frequently asked questions

Can Claude Code use OpenRouter, DeepSeek, Kimi, and Ollama?

Yes, if the endpoint speaks the Anthropic Messages API. Set ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN, then map ANTHROPIC_DEFAULT_OPUS_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL, and CLAUDE_CODE_SUBAGENT_MODEL. For OpenRouter use https://openrouter.ai/api and set ANTHROPIC_API_KEY="".

Do I need a proxy for custom providers?

Only if the provider lacks an Anthropic endpoint. DeepSeek, Moonshot/Kimi, Z.ai, and Ollama speak it natively. OpenAI-only providers need a translator such as LiteLLM (https://openrouter.ai/api) or Claude Code Router.

Why does my custom model return model not found?

Unmapped alias tiers. Claude Code still requests claude-... names for background tasks and subagents. Map all tiers explicitly. See the model configuration docs.

How do I switch providers in Claude Code?

Change the environment variables and restart. /model changes the model, not the endpoint. A local router is the only way to change providers without a restart.

Is LiteLLM safe to use with Claude Code?

LiteLLM is an Anthropic-documented gateway example, but versions 1.82.7 and 1.82.8 shipped malware. Pin a clean version and rotate credentials.

What Anthropic supports

Anthropic documents the gateway mechanism and does not endorse, maintain, or audit third-party gateways. It also does not support routing Claude Code to non-Claude models through a gateway. The base URL and credential variables are stable interfaces; the behavior of any specific model behind them is the provider's responsibility.

Sources: Claude Code gateway docs · OpenRouter Claude Code guide · DeepSeek Claude Code · Kimi Claude Code

Closing

Adding a provider is four lines of configuration: base URL, credential, model mapping, and an optional header or picker entry. Put it in a settings file, map every alias tier, and confirm with /status.

Start with one provider and one model. Add tiers and routing only after the first request succeeds.

Related Posts