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.
Prerequisites
Section titled “Prerequisites”- Claude Code installed and working. Run
claude --versionto 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.
Pick a model id
Section titled “Pick a model id”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.
curl https://llm.grafilab.ai/v1/models \ -H "Authorization: Bearer $GRAFILAB_API_KEY" | jq -r '.data[].id'from openai import OpenAI
client = OpenAI(api_key="sk-grafilab-...", base_url="https://llm.grafilab.ai/v1")for model in client.models.list(): print(model.id)import OpenAI from "openai";
const client = new OpenAI({ apiKey: "sk-grafilab-...", baseURL: "https://llm.grafilab.ai/v1" });for await (const model of client.models.list()) { console.log(model.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.
Quick start
Section titled “Quick start”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.
export ANTHROPIC_BASE_URL="https://llm.grafilab.ai"export ANTHROPIC_AUTH_TOKEN="sk-grafilab-..."export ANTHROPIC_MODEL="grafilab/qwen3.6-flash"
claude$env:ANTHROPIC_BASE_URL = "https://llm.grafilab.ai"$env:ANTHROPIC_AUTH_TOKEN = "sk-grafilab-..."$env:ANTHROPIC_MODEL = "grafilab/qwen3.6-flash"
claudeANTHROPIC_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:
export ANTHROPIC_DEFAULT_HAIKU_MODEL="grafilab/qwen3.6-flash"export CLAUDE_CODE_SUBAGENT_MODEL="grafilab/qwen3.6-flash"$env:ANTHROPIC_DEFAULT_HAIKU_MODEL = "grafilab/qwen3.6-flash"$env: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.
Make it stick
Section titled “Make it stick”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.
A settings file, for always-on
Section titled “A settings file, for always-on”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.
{ "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.
A shell wrapper, for side by side
Section titled “A shell wrapper, for side by side”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.
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 "$@"}function gclaude { $env:ANTHROPIC_BASE_URL = "https://llm.grafilab.ai" $env:ANTHROPIC_AUTH_TOKEN = "sk-grafilab-..." $env:ANTHROPIC_MODEL = "grafilab/qwen3.6-flash" $env:ANTHROPIC_DEFAULT_HAIKU_MODEL = "grafilab/qwen3.6-flash" $env:CLAUDE_CODE_SUBAGENT_MODEL = "grafilab/qwen3.6-flash" try { claude @args } finally { Remove-Item Env:ANTHROPIC_BASE_URL, Env:ANTHROPIC_AUTH_TOKEN, Env:ANTHROPIC_MODEL, Env:ANTHROPIC_DEFAULT_HAIKU_MODEL, Env:CLAUDE_CODE_SUBAGENT_MODEL -ErrorAction SilentlyContinue }}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.
Verify
Section titled “Verify”-
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. -
Start a session and send a prompt.
Terminal window claude "say hi" -
Run
/statusand read the Status tab. Two lines confirm the routing:Anthropic base URL: https://llm.grafilab.aiAuth token: ANTHROPIC_AUTH_TOKENIf the base URL line is missing, the variable did not reach the session — start
claudefrom the shell that has the exports, or move them into a settings file. -
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.
Known limitations
Section titled “Known limitations”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.
| What | What you see | What to do |
|---|---|---|
| No token-counting endpoint | Grafilab 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 estimate | Nothing. Expect the context readout to be approximate |
| Server-side tools rejected | web_search_*, bash_*, and any tool whose type is not custom return 400. Claude Code’s built-in WebSearch tool fails rather than degrading | Add "WebSearch" to permissions.deny in your settings file so Claude Code never offers it |
cache_control ignored | Prompt-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 caching | Nothing, but do not read the zero as a bug |
thinking ignored | Extended-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 erroring | Nothing |
top_k, output_config, context_management ignored | Accepted and dropped, so effort and context-editing settings are inert rather than fatal | Nothing |
anthropic-version not read | Sending it is harmless | Nothing |
| Gateway model discovery | CLAUDE_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 picker | Set the model with ANTHROPIC_MODEL or claude --model |
429 means an empty wallet | Grafilab returns 429 for insufficient balance, not for throughput | Top up in the console |
| Features that call Anthropic directly | Remote Control and voice dictation need a claude.ai identity and are unavailable while a gateway credential is set | Unset the variables for a session that needs them |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
authentication_error on every request | The token carries a newline or a trailing space, usually from copying out of a terminal | Re-copy from API in the console and quote the value |
| Claude Code shows the login screen although the curl test passed | A reachable base URL is not a credential, and a project env block applies only after the folder trust prompt | Set ANTHROPIC_AUTH_TOKEN as a shell export or in ~/.claude/settings.json |
not_found_error naming the model | The alias is misspelled, or it is a background or subagent model still set to an Anthropic name | Re-list with GET /v1/models; set ANTHROPIC_DEFAULT_HAIKU_MODEL and CLAUDE_CODE_SUBAGENT_MODEL |
404 on every request | The base URL ends in /v1 | Use the bare origin https://llm.grafilab.ai |
| A request hangs, then dies with nothing rendered | Upstream calls have a 120-second bound on time to first token | Retry; a request that produced no output was not billed |
| A reply stops halfway with an error | A stream that fails after its first frame stays on HTTP 200 and delivers event: error in-band | Retry. Partial output was billed for what was generated |
500 from the gateway | An infrastructure failure, deliberately returned as 500 rather than 502 or 504 | Safe to retry unless the response carries x-should-retry: false, which marks work already done and billed |

