Switching from Anthropic direct: migrating the SDK

/v1/messages, x-api-key, anthropic-version — all unchanged. What differs is the model ids, and one rule about retired ones.

The wire format is already the right one

Claude models answer on POST https://token-share.app/v1/messages, authenticated with x-api-key and carrying anthropic-version: 2023-06-01. Those are the header names and the path the Anthropic SDK already sends, so pointing its base URL here is the entire transport change.

Request bodies port over unchanged: messages, system, max_tokens, tools, tool_choice, stop_sequences, temperature, stream. Responses come back from Anthropic upstream, so content blocks, stop_reason and the usage object are theirs, not a reconstruction.

The catalog also answers GET /v1/models. Send anthropic-version and the listing comes back in the Anthropic shape, which means a client that discovers models the Anthropic way keeps working.

The official @anthropic-ai/sdk against this gateway.
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  baseURL: 'https://token-share.app',
  apiKey: process.env.TOKEN_SHARE_KEY,
});

const message = await client.messages.create({
  model: 'claude-sonnet-5',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Explain this stack trace.' }],
});

for (const block of message.content) {
  if (block.type === 'text') console.log(block.text);
}

Model ids, and the retired ones

Twelve Claude ids are routed here. Five are pool ids — claude-opus-5, claude-opus-4-8, claude-opus-4-6, claude-sonnet-5, claude-sonnet-4-6 — billed at 0.1×, and the other seven are stable-* lanes at 0.4×. The ids are ours, so copy them from the catalog rather than from an upstream release note.

A few ids were retired in favour of a dedicated lane, and calling one is refused with 400 rather than quietly redirected. The message names the replacement: send claude-haiku-4-5 and you are told to use stable-claude-haiku-4-5. That is deliberate — the replacement is billed at 0.4× instead of 0.1×, so opting into it has to be your decision rather than a silent upgrade that costs more.

Claude requests draw only on the recharge balance. An empty recharge balance, or holding only earned or promotional balance, returns 402 with code claude_minimum_balance before anything is forwarded.

Claude Code and the decorated ids

Claude Code appends decorations to a model name: [1m] to ask for the long-context variant, and a trailing (budget) to carry a thinking budget, sometimes both as name[1m](1024). The allowlist parses those off and judges the base id, so a decorated id whose base is in the catalog is accepted.

It is a base-id comparison, not a substring match, so a variant that is not in the catalog is still refused. That keeps a real typo from being silently promoted to a model you did not intend to pay for.

Set the SDK's base URL through ANTHROPIC_BASE_URL and its key through ANTHROPIC_API_KEY, and any tool built on the Anthropic SDK follows. There is no separate integration path for CLI tools.

Common questions

Are these the same models Anthropic serves?
Yes. Requests are forwarded upstream and the responses are Anthropic's. What differs is billing and which credential the request is served on — a shared account pool for the plain ids, a key we hold directly for the stable-* lanes.
Does prompt caching still work?
Yes. cache_control travels with the request on /v1/messages, and cache reads and cache writes are billed as their own buckets at their own per-token rates rather than at the input rate. Each id lists its own cache-read and cache-creation prices in the catalog.
My request came back 400 naming a different model id. What happened?
You called an id that has been retired in favour of a dedicated lane. The message names the replacement so the fix is one line. It is refused rather than redirected because the replacement carries a different multiplier — serving it silently would bill you at a higher rate for an id you did not ask for, and echo back a model name you never sent.