Anthropic SDK
Written for anthropic 1.11.0 (Python) and @anthropic-ai/sdk 0.131.0 (npm), from their documentation and source. Both SDKs work against zdr.sh by changing the base URL to the origin only: they append /v1/messages themselves, so https://api.zdr.sh/v1 would post to /v1/v1/messages.
Python#
python
JavaScript and TypeScript#
typescript
Migrating an Anthropic codebase#
What works with no change beyond the base URL and the key:
messages.createandmessages.stream, withsystem,messages,tools,tool_choice,stop_sequences,temperature,top_pandmax_tokens- Extended thinking:
thinkingblocks withthinking_deltaandsignature_delta, with the signature issued by zdr.sh messages.count_tokens, answered by the serviceanthropic-versionandanthropic-betaheaders;?beta=trueacceptedcache_controlbreakpoints, and the usage fieldscache_read_input_tokensandcache_creation_input_tokens- The errors, in the Anthropic envelope with the request id
What differs:
- The model ids are the zdr.sh ones. A
claude-id maps through the key's Claude mapping, which is on by default, and the response names the mapped id.models.listnever lists aclaude-id. - Server tools such as
web_search_20250305are dropped when the model cannot run them, withx-token-noticenaming them. - Token counting happens on the server; there is no client-side count to fall back to.
- There are no audio or file APIs. Embeddings are served in the OpenAI format at
POST /v1/embeddings: Embeddings. Idempotency-Keymakes a retry bill once. Chat completions has the rules.x-token-costis extra, and a trailer on a stream.
Notes#
- Credential.
api_keyis sent asX-Api-Key,auth_tokenasAuthorization: Bearer. zdr.sh accepts either. With no credential the request throws at request time, not at construction. - Base URL. Hard-code it; both SDKs fall back to their vendor when the value is empty or absent.
ANTHROPIC_BASE_URLand the matching key variables work as fallbacks when you do not pass arguments. - Counting.
client.messages.count_tokens(...)reaches/v1/messages/count_tokens, so no token counting happens on the client. - Extra fields.
extra_body,extra_queryandextra_headersin Python, or the same options in TypeScript, carry anything the typed parameters do not. - Streaming without the helper.
client.messages.create({..., stream: true})withfor awaitgives raw events; breaking out of the loop or callingstream.controller.abort()cancels the request, and what was produced is what you pay for.