<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gyre</title>
    <description>The latest articles on DEV Community by Gyre (@corecode).</description>
    <link>https://dev.to/corecode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4223842%2F0532f5c5-04cc-47e0-95d1-ef5b4158f73d.png</url>
      <title>DEV Community: Gyre</title>
      <link>https://dev.to/corecode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/corecode"/>
    <language>en</language>
    <item>
      <title>How Claude Code's ANTHROPIC_BASE_URL works, and how to check that a relay really serves Claude</title>
      <dc:creator>Gyre</dc:creator>
      <pubDate>Tue, 03 Nov 2026 08:51:25 +0000</pubDate>
      <link>https://dev.to/corecode/how-claude-codes-anthropicbaseurl-works-and-how-to-check-that-a-relay-really-serves-claude-49j2</link>
      <guid>https://dev.to/corecode/how-claude-codes-anthropicbaseurl-works-and-how-to-check-that-a-relay-really-serves-claude-49j2</guid>
      <description>&lt;p&gt;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).&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The environment variables Claude Code reads
&lt;/h2&gt;

&lt;p&gt;From the official docs (&lt;a href="https://code.claude.com/docs/en/env-vars" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt;, &lt;a href="https://code.claude.com/docs/en/llm-gateway-connect" rel="noopener noreferrer"&gt;connect to an LLM gateway&lt;/a&gt;):&lt;/p&gt;

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://relay.example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ANTHROPIC_AUTH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sk-your-key"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Check with /status
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  3. When a cheap plan swaps the model
&lt;/h2&gt;

&lt;p&gt;"Works in Claude Code" and "Claude answers" are different claims. Z.ai's GLM Coding Plan, for example, connects Claude Code through the same &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; / &lt;code&gt;ANTHROPIC_AUTH_TOKEN&lt;/code&gt; mechanism and maps the Opus / Sonnet / Haiku variables to GLM models (&lt;a href="https://docs.z.ai/devpack/tool/claude" rel="noopener noreferrer"&gt;Z.ai docs&lt;/a&gt;). 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 (&lt;a href="https://code.claude.com/docs/en/llm-gateway" rel="noopener noreferrer"&gt;LLM gateway&lt;/a&gt;). The problem is a relay that does the same thing without saying so.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Check A: the open-source check-relay.py
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/CoreCodeAI/claude-relay-check" rel="noopener noreferrer"&gt;claude-relay-check&lt;/a&gt; is a single Python file using only the standard library; a run costs a few cents. Per its README it checks three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shape&lt;/code&gt;: the response is in Anthropic Messages format (&lt;code&gt;type&lt;/code&gt;, an &lt;code&gt;id&lt;/code&gt; starting with &lt;code&gt;msg_&lt;/code&gt;, &lt;code&gt;usage&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache&lt;/code&gt;: it sends a ~18,000-token prompt with &lt;code&gt;cache_control&lt;/code&gt; twice and checks that the write and the read match.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;signature&lt;/code&gt;: 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 (&lt;a href="https://platform.claude.com/docs/en/build-with-claude/preserved-thinking" rel="noopener noreferrer"&gt;Preserved thinking&lt;/a&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSLO&lt;/span&gt; https://raw.githubusercontent.com/CoreCodeAI/claude-relay-check/main/check-relay.py
python3 check-relay.py &lt;span class="nt"&gt;--base-url&lt;/span&gt; https://relay.example &lt;span class="nt"&gt;--key&lt;/span&gt; sk-your-key &lt;span class="nt"&gt;--model&lt;/span&gt; claude-sonnet-5-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;--base-url&lt;/code&gt; and &lt;code&gt;--key&lt;/code&gt; 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 &lt;code&gt;ERROR&lt;/code&gt;, which is not a verdict either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Check B: ModelTrace for the tier
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/xqy2006/ModelTrace" rel="noopener noreferrer"&gt;ModelTrace&lt;/a&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Check C: look at the cache yourself
&lt;/h2&gt;

&lt;p&gt;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 (&lt;a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;Prompt caching&lt;/a&gt;). Send the same request twice and compare &lt;code&gt;usage&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 - &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/body.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
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?"}]}))
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;1 2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/v1/messages"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ANTHROPIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"anthropic-version: 2023-06-01"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; @/tmp/body.json |
  python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import json,sys; d=json.load(sys.stdin); print(d.get("model"), d.get("usage"))'&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expect &lt;code&gt;cache_creation_input_tokens&lt;/code&gt; on the first call and about the same &lt;code&gt;cache_read_input_tokens&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What to look for on a price list
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Disclosure
&lt;/h2&gt;

&lt;p&gt;I run CoreCode (&lt;a href="https://corecode.live/en/" rel="noopener noreferrer"&gt;corecode.live&lt;/a&gt;) 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 &lt;a href="https://corecode.live/docs/en/verify/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and the script above works against any relay, CoreCode included. It is not an Anthropic service.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>claude</category>
      <category>anthropic</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
