Skip to content

Models

Claude Code

Claude Code treats any host that speaks the Anthropic Messages format as a gateway. Grafilab serves that format at https://llm.grafilab.ai/v1/messages, so pointing the CLI at Grafilab takes two environment variables plus a model id.

  • Claude Code installed and working. Run claude --version to confirm.
  • A Grafilab API key. In the console open API (https://app.grafilab.ai/api) and click Generate New Key — see API Keys.

Name the key claude-code so its spend is separated from your other tools.

The model catalog is live, so list it rather than copying ids from a page. Every entry’s id is the exact string to use in any tool or SDK.

Terminal window
curl https://llm.grafilab.ai/v1/models \
-H "Authorization: Bearer $GRAFILAB_API_KEY" | jq -r '.data[].id'

The same ids appear as cards in the Playground, with context size and prices.

The Anthropic surface has no model listing of its own, so list the catalog on the OpenAI surface as above and paste the id into ANTHROPIC_MODEL.

Set the base URL to the bare origin. Claude Code appends /v1/messages itself, so a base URL ending in /v1 produces requests to /v1/v1/messages and a 404.

Terminal window
export ANTHROPIC_BASE_URL="https://llm.grafilab.ai"
export ANTHROPIC_AUTH_TOKEN="sk-grafilab-..."
export ANTHROPIC_MODEL="grafilab/qwen3.6-flash"
claude

ANTHROPIC_AUTH_TOKEN sends the key as Authorization: Bearer, which Grafilab accepts. Do not also set ANTHROPIC_API_KEY: it sends the key as x-api-key, which Grafilab also accepts, but having both set means Claude Code prompts you to approve the API key before it takes over, and a startup warning about two credential sources. Pick one, and prefer ANTHROPIC_AUTH_TOKEN.

ANTHROPIC_MODEL only covers the session’s main model. Claude Code also calls a small model for background work and spawns subagents, and both default to Anthropic model names that Grafilab does not serve. Set them to a Grafilab id as well, or those calls fail with 404:

Terminal window
export ANTHROPIC_DEFAULT_HAIKU_MODEL="grafilab/qwen3.6-flash"
export CLAUDE_CODE_SUBAGENT_MODEL="grafilab/qwen3.6-flash"

ANTHROPIC_DEFAULT_HAIKU_MODEL is what the haiku alias resolves to and what Claude Code uses for background work; ANTHROPIC_DEFAULT_SONNET_MODEL and ANTHROPIC_DEFAULT_OPUS_MODEL do the same for sonnet and opus, so set those too if you switch models with /model. ANTHROPIC_SMALL_FAST_MODEL is the deprecated predecessor of the haiku variable — use the new name.

Exports last one terminal session. Two ways to keep the configuration, depending on whether you want every Claude Code session on Grafilab or only some.

Put the variables in the env block of a Claude Code settings file. ~/.claude/settings.json applies to all your projects; .claude/settings.local.json applies to one project and is gitignored for you. A settings file also reaches background agents, which shell exports do not reliably do.

~/.claude/settings.json
{
"env": {
"ANTHROPIC_BASE_URL": "https://llm.grafilab.ai",
"ANTHROPIC_AUTH_TOKEN": "sk-grafilab-...",
"ANTHROPIC_MODEL": "grafilab/qwen3.6-flash",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "grafilab/qwen3.6-flash",
"CLAUDE_CODE_SUBAGENT_MODEL": "grafilab/qwen3.6-flash"
}
}

Settings files are strict JSON: a trailing comma or a // comment makes Claude Code report a settings error at the next start.

If you also use Claude Code against Anthropic, wrap the Grafilab configuration in a function so plain claude stays untouched and gclaude runs on Grafilab.

~/.zshrc or ~/.bashrc
gclaude() {
ANTHROPIC_BASE_URL="https://llm.grafilab.ai" \
ANTHROPIC_AUTH_TOKEN="sk-grafilab-..." \
ANTHROPIC_MODEL="grafilab/qwen3.6-flash" \
ANTHROPIC_DEFAULT_HAIKU_MODEL="grafilab/qwen3.6-flash" \
CLAUDE_CODE_SUBAGENT_MODEL="grafilab/qwen3.6-flash" \
command claude "$@"
}

The finally block matters on PowerShell: $env: assignments outlive the function and would leak into the next plain claude you run in that window. The bash form sets the variables for one command only, so it needs no cleanup.

If you keep both setups, remember that a settings-file env value wins over a shell export of the same variable.

  1. Test the endpoint before opening Claude Code, so a failure points at the gateway rather than at the CLI.

    Terminal window
    curl -X POST "$ANTHROPIC_BASE_URL/v1/messages" \
    -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
    -H "content-type: application/json" \
    -d '{"model": "grafilab/qwen3.6-flash", "max_tokens": 16, "messages": [{"role": "user", "content": "Say hi."}]}'

    A body starting with {"id":"msg_ means the URL and the key both work.

  2. Start a session and send a prompt.

    Terminal window
    claude "say hi"
  3. Run /status and read the Status tab. Two lines confirm the routing:

    Anthropic base URL: https://llm.grafilab.ai
    Auth token: ANTHROPIC_AUTH_TOKEN

    If the base URL line is missing, the variable did not reach the session — start claude from the shell that has the exports, or move them into a settings file.

  4. Run /cost. Token counts and spend appear as usual; the cache-write figure stays at zero, which is expected and explained below.

Then open API in the console and select View Usage on the claude-code key to see the same requests from Grafilab’s side. Rollups can lag by up to about 30 minutes.

Grafilab implements a subset of the Messages protocol. Everything Claude Code needs for editing, running commands, and calling your MCP servers is there; these are the edges you may notice.

WhatWhat you seeWhat to do
No token-counting endpointGrafilab does not implement /v1/messages/count_tokens, so Claude Code cannot ask the gateway to measure a prompt and its context-remaining figure is an estimateNothing. Expect the context readout to be approximate
Server-side tools rejectedweb_search_*, bash_*, and any tool whose type is not custom return 400. Claude Code’s built-in WebSearch tool fails rather than degradingAdd "WebSearch" to permissions.deny in your settings file so Claude Code never offers it
cache_control ignoredPrompt-cache writes are never reported: cache_creation_input_tokens is always 0 and /cost shows no cache writes. Cache reads are still reported where the model supports cachingNothing, but do not read the zero as a bug
thinking ignoredExtended-thinking toggles have no effect unless the model reasons natively. Claude Code sends thinking for model names it does not recognise, including Grafilab aliases; Grafilab accepts and drops the field instead of erroringNothing
top_k, output_config, context_management ignoredAccepted and dropped, so effort and context-editing settings are inert rather than fatalNothing
anthropic-version not readSending it is harmlessNothing
Gateway model discoveryCLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 makes Claude Code read /v1/models at startup, but it keeps only ids containing claude or anthropic, so most Grafilab ids never reach the /model pickerSet the model with ANTHROPIC_MODEL or claude --model
429 means an empty walletGrafilab returns 429 for insufficient balance, not for throughputTop up in the console
Features that call Anthropic directlyRemote Control and voice dictation need a claude.ai identity and are unavailable while a gateway credential is setUnset the variables for a session that needs them
SymptomCauseFix
authentication_error on every requestThe token carries a newline or a trailing space, usually from copying out of a terminalRe-copy from API in the console and quote the value
Claude Code shows the login screen although the curl test passedA reachable base URL is not a credential, and a project env block applies only after the folder trust promptSet ANTHROPIC_AUTH_TOKEN as a shell export or in ~/.claude/settings.json
not_found_error naming the modelThe alias is misspelled, or it is a background or subagent model still set to an Anthropic nameRe-list with GET /v1/models; set ANTHROPIC_DEFAULT_HAIKU_MODEL and CLAUDE_CODE_SUBAGENT_MODEL
404 on every requestThe base URL ends in /v1Use the bare origin https://llm.grafilab.ai
A request hangs, then dies with nothing renderedUpstream calls have a 120-second bound on time to first tokenRetry; a request that produced no output was not billed
A reply stops halfway with an errorA stream that fails after its first frame stays on HTTP 200 and delivers event: error in-bandRetry. Partial output was billed for what was generated
500 from the gatewayAn infrastructure failure, deliberately returned as 500 rather than 502 or 504Safe to retry unless the response carries x-should-retry: false, which marks work already done and billed