DEV Community

Gyre
Gyre

Posted on Fully Autonomous

How Claude Code's ANTHROPIC_BASE_URL works, and how to check that a relay really serves Claude

Before you put a cheap Claude Code relay (an API proxy) in front of your work, it is worth checking for yourself that Claude is the model answering. The first half of this post covers the environment variables Claude Code reads; the second half is a set of checks that work against any relay. I run a relay called CoreCode (disclosure at the end).

1. The environment variables Claude Code reads

From the official docs (environment variables, connect to an LLM gateway):

  • ANTHROPIC_BASE_URL points the API endpoint at a proxy or gateway.
  • ANTHROPIC_AUTH_TOKEN is sent as an Authorization: Bearer header.
  • ANTHROPIC_API_KEY is sent as an x-api-key header. When set, it takes precedence over a signed-in subscription (interactive mode asks you to approve it the first time).
  • ANTHROPIC_DEFAULT_OPUS_MODEL / SONNET / HAIKU change the model ID that the opus, sonnet and haiku aliases resolve to.

Pick the variable by the header your relay reads. The docs suggest trying ANTHROPIC_AUTH_TOKEN first and switching if you get a 401. Putting it in the env block of ~/.claude/settings.json applies it however your shell starts.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://relay.example",
    "ANTHROPIC_AUTH_TOKEN": "sk-your-key"
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Check with /status

Start claude, run /status, and check that the Anthropic base URL line shows the relay and that there is an Auth token or API key line. If Login method shows a claude.ai account, your credential is not being used.

3. When a cheap plan swaps the model

"Works in Claude Code" and "Claude answers" are different claims. Z.ai's GLM Coding Plan, for example, connects Claude Code through the same ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN mechanism and maps the Opus / Sonnet / Haiku variables to GLM models (Z.ai docs). That is documented and above board, but the model answering is not Claude. Anthropic, for its part, says it doesn't support routing Claude Code to non-Claude models through any gateway (LLM gateway). The problem is a relay that does the same thing without saying so.

Asking the model "who are you" tells you nothing: one line of system prompt makes any model say it is Claude. Look at behavior instead.

4. Check A: the open-source check-relay.py

claude-relay-check is a single Python file using only the standard library; a run costs a few cents. Per its README it checks three things:

  • shape: the response is in Anthropic Messages format (type, an id starting with msg_, usage).
  • cache: it sends a ~18,000-token prompt with cache_control twice and checks that the write and the read match.
  • signature: it changes one character of a thinking block's signature and sends it back, expecting a 400. Anthropic says a modified signature is always rejected (Preserved thinking).
curl -fsSLO https://raw.githubusercontent.com/CoreCodeAI/claude-relay-check/main/check-relay.py
python3 check-relay.py --base-url https://relay.example --key sk-your-key --model claude-sonnet-5-5
Enter fullscreen mode Exit fullscreen mode

Without --base-url and --key it reads the variables Claude Code uses. The README states two limits: it shows the upstream is Anthropic, not which tier (Opus or Sonnet) you got; and it is a single sample, so rerun it later. A relay that rejects non-Claude-Code clients with a 403 gives ERROR, which is not a verdict either way.

5. Check B: ModelTrace for the tier

ModelTrace has the model generate long integer sequences three times and compares the distribution of digits with fingerprints of known models to estimate family and version. Its README says the result is a probability over the models in its fingerprint library, and an unlisted model is assigned to the nearest candidate. Treat it as a hint.

6. Check C: look at the cache yourself

Claude Code sends the same prefix (system prompt, tool definitions) every turn, so cache hits drive the bill. Anthropic prices a cache read at 0.1x the base input price (with exceptions such as 0.05x on some models) and a 5-minute cache write at 1.25x (Prompt caching). Send the same request twice and compare usage:

python3 - > /tmp/body.json <<'PY'
import json
t = "Reference note: " + "the quick brown fox jumps over the lazy dog. " * 1500
print(json.dumps({"model": "claude-sonnet-5-5", "max_tokens": 16,
  "system": [{"type": "text", "text": t, "cache_control": {"type": "ephemeral"}}],
  "messages": [{"role": "user", "content": "ok?"}]}))
PY
for i in 1 2; do
  curl -sS "$ANTHROPIC_BASE_URL/v1/messages" \
    -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
    -H "anthropic-version: 2023-06-01" -H "content-type: application/json" \
    -d @/tmp/body.json |
  python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("model"), d.get("usage"))'
done
Enter fullscreen mode Exit fullscreen mode

Expect cache_creation_input_tokens on the first call and about the same cache_read_input_tokens on the second. Zero on both means caching is not working, or the relay computes usage itself. Writes without reads suggest each request lands on a different account.

7. What to look for on a price list

  • Input, output, cache write and cache read billed separately, in Anthropic's ratios.
  • Per-request usage and cost visible in a log.
  • One session pinned to one account (the condition for the cache to keep hitting).
  • What a "discount" is measured against.

Disclosure

I run CoreCode (corecode.live) and mention it only as one example of the checks above. CoreCode is a relay where every request is served by an official Anthropic Claude account. Payment is Alipay and WeChat Pay in Chinese yuan (CNY) only; there is no card or USD payment. Its own verification page is here, and the script above works against any relay, CoreCode included. It is not an Anthropic service.

Top comments (0)