Use Claude Code with a custom API endpoint
Claude Code reads its endpoint from two environment variables. Set them once and every session routes through your own key (billed at whatever rate your provider charges).
curl https://aicomp.ai/v1/models \
-H "Authorization: Bearer sk-your-gateway-key"
You should get back a JSON list of model IDs. If you see
Invalid token, the key is wrong or was not copied in full.
Step 1 — get your key and base URL
After signing up, open the dashboard and generate an API key. The dashboard also shows your API base URL. It looks like https://aicomp.ai — without a /v1 on the end, because Claude Code appends /v1/messages itself. You need both values.
Step 2 — set the environment variables
Add these to ~/.zshrc or ~/.bashrc:
export ANTHROPIC_BASE_URL="https://aicomp.ai"
export ANTHROPIC_API_KEY="sk-your-gateway-key"
Then reload your shell:
source ~/.zshrc
If you would rather not touch your profile permanently, prefix a single session instead:
ANTHROPIC_BASE_URL="https://aicomp.ai" \
ANTHROPIC_API_KEY="sk-your-gateway-key" \
claude
Step 3 — verify it actually works
Before you trust it with real work, confirm the endpoint responds:
curl https://aicomp.ai/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":64,
"messages":[{"role":"user","content":"say OK"}]}'
A normal completion response means you are routing correctly.
Troubleshooting
| Symptom | Cause |
|---|---|
| 401 authentication error | Key is wrong, or belongs to a different account. Regenerate it. |
404 on /v1/messages | Path doubling. If your base URL already ends in /v1, do not add it again. |
| Model not found | The gateway may use a slightly different model ID. Check its model list. |
| Still billed by Anthropic | The variable is not in the environment Claude Code actually reads. Restart the terminal completely. |
Why bother
Claude Code is the single heaviest token consumer most developers run, because it resends accumulated context on every turn. Whatever your monthly bill is today, the per-token rate is the only lever that scales with it. See the Claude Code cost calculator for your own number.
Why changing one URL is enough
An OpenAI-compatible client does not have a provider hardcoded into it. It holds a base
URL, and at request time it concatenates that base URL with a resource path —
/chat/completions, /models, then sends the same JSON body it would
have sent anywhere else, with your key in an Authorization: Bearer header. The
base URL is therefore the only thing that decides where the request physically goes.
That is why this change is one line and not a rewrite. Your message array, your tool
definitions, your streaming handling and your response parsing are all unchanged, because the
wire format is unchanged. What changes is who answers, and what they charge per token. The two
values that travel with the new destination are the base URL itself and the API key; the model
ID is the third, and it has to be one the new endpoint actually serves, which you can list with
a single call to /v1/models rather than guess at.
The practical consequence is that this is reversible. Keep the base URL in configuration rather than inline, and moving between providers is a config change you can undo in a minute — which is also the right way to test it: point a single non-critical call at the new endpoint first, confirm it in the usage log, then move the rest.
What it does not change
Swapping the base URL is narrower than it looks, and the gap between expectation and reality is where most of the frustration comes from.
- Not every endpoint on the surface. Compatibility usually covers
/chat/completionsand/models. Embeddings, audio, files, fine-tuning and the newer response APIs are a separate question (assume they are absent until you have listed them). - Not the model's own limits. Context window, output cap, rate limits and moderation behaviour belong to the model and the provider serving it. A shorter context window on the far side shows up as truncation, not as an error.
- Not your timeout and retry semantics. Those live in your client. A gateway adds a hop, so an aggressive timeout that was fine against a direct provider can start failing here, set it explicitly rather than inheriting a default.
- Not your error handling. Status codes mean roughly the same thing but the bodies differ between providers. Anything that parses an error message string is the first thing to break.
- Not the identity of the caller. The endpoint sees one key. If several people or services share it, their spend is indistinguishable in the usage log. Give each one its own key before you need to tell them apart.
What it costs
| Model | Gateway rate in / out per 1M tokens | Official list in / out per 1M tokens | Diff |
|---|---|---|---|
| claude-haiku-4-5 | $0.5 / $2.5 | $1 / $5 | 50% |
| claude-sonnet-5 | $1 / $5 | $2 / $10 | 50% |
| claude-opus-5 | $2.5 / $12.5 | $5 / $25 | 50% |
Rates checked 2026-11-02. Gateway rates move with upstream promotions, verify the current number in your dashboard before committing to a budget.
| Workload | claude-haiku-4-5-20251001 | claude-sonnet-5 | claude-opus-5 |
|---|---|---|---|
| Chat assistance 50 requests a day, 2k in / 500 out | $2 | $4 | $11 |
| Bulk processing 5,000 short jobs a day, 800 in / 300 out | $115 | $230 | $575 |
| Agent loop 20 turns a day, 60k in / 800 out | $13 | $26 | $64 |
One month at 20 working days, gateway rates checked 2026-11-02. These are the token volumes, not a quote Your own shape may be larger or smaller.
The three rows are the same three models doing very different work. Chat assistance is dominated by output, the agent loop by input, and bulk processing by neither in particular — which is why the cheapest model changes depending on which row you are on. Model choice matters more here than anywhere else, because agent loops resend context on every turn. The full breakdown is in what Claude Code costs per month.
FAQ
Does Claude Code really work with a custom endpoint?
Yes. Claude Code reads ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY from the environment, so setting those two variables routes every session through the endpoint you choose, no change to how you use the tool.
Which model should I point Claude Code at?
Sonnet-class is the default choice for agent work; Haiku-class is worth it for mechanical edits, and Opus-class for the hardest debugging. The spread between them is large enough to change your monthly bill by a factor of several (see the table above).
How much does it cost per month?
It is pure token spend, driven mostly by how much context each turn resends rather than by message count. See the dedicated breakdown in what Claude Code costs per month, or use the Claude Code cost calculator with your real session length.
Why am I still being billed by Anthropic?
The variables are not reaching the process. Export them from your shell profile and restart the terminal completely, a reloaded profile in an already-running shell is the usual cause.
Can I switch back?
Yes. Unset the two variables, or keep them in a separate profile file you source only when you want the alternate route. Nothing about your project changes.
How do I set the base URL for Claude Code?
Export ANTHROPIC_BASE_URL before launching it That one variable is the entire change, and everything else about how you use Claude Code is untouched. Put it in your shell profile rather than typing it inline so it survives new terminals, then restart the terminal completely: a reloaded profile inside an already-running shell is the most common reason the setting looks like it was ignored.
What is ANTHROPIC_BASE_URL?
It is the environment variable Claude Code reads to decide which host to send API requests to. Unset, it points at Anthropic's own API; set to a gateway, every session routes there instead. It pairs with ANTHROPIC_API_KEY, which supplies the credential for whichever host you picked, both need to change together, or requests fail authentication.
Does the rest of the API work through the same base URL?
Only the parts the endpoint actually implements. Chat completions and the model listing are the dependable surface; embeddings, files, batch and the admin routes are separate features that a gateway may or may not expose. Before building on anything beyond completions, call the model list and check the provider's own documentation for that route. Assuming parity because the base URL works is how integrations break late rather than early.
Should the base URL go in my shell profile or per project?
Per project, if more than one project exists. A shell profile is fine on a single machine with one provider, but it silently applies to every repository you open, including ones where you meant to use a different account. A per-project environment file, or a tool like direnv that loads on directory entry, keeps the routing decision visible and reviewable, and makes it obvious in code review that a project depends on a custom endpoint.
Next
- Claude Sonnet 5 pricing — the usual default for agent coding
- Claude Opus 5 pricing — for the hardest reasoning
- Do the same in Cursor
- Do the same in Cline (the VS Code agent), through its OpenAI Compatible provider
- Do the same in Aider — terminal pair programming on the same endpoint
- Price your own session length in the Claude Code cost calculator
- Eight ways to cut the bill