Use the Anthropic Python SDK with a custom Base URL.
The Anthropic Python SDK defaults to https://api.anthropic.com. To route it through MonkeyAI, override base_url= on the client constructor or export ANTHROPIC_BASE_URL. The SDK then sends x-api-key plus anthropic-version: 2023-06-01 to /v1/messages. This guide verifies that path against production on 2026-08-09 and walks through a real client.messages.create() call.
What was actually verified on production?
Six probes were sent to https://monkeytheme.com/v1/messages with a deliberately invalid test key, on 2026-08-09. Every probe returned HTTP 401 with a structured JSON body and the x-request-id header in the response. The sub2api backend log shows ingress_reject_reason=invalid_api_key with latency_ms=0-3 on every request, meaning the relay gates on auth before any upstream call.
| Probe | Headers | Response | What it confirms |
|---|---|---|---|
POST /v1/messages | x-api-key only | 401 INVALID_API_KEY | SDK-default auth path works; no anthropic-version required |
POST /v1/messages | x-api-key + anthropic-version: 2023-06-01 | 401 INVALID_API_KEY | SDK-style dual-header request reaches the relay intact |
POST /v1/messages | Authorization: Bearer *** | 401 INVALID_API_KEY | Bearer also accepted on Messages, same as OpenAI routes |
GET /v1/models | x-api-key | 401 INVALID_API_KEY | Models endpoint also gates on auth (probe, not real list) |
GET /v1/models | anthropic-version alone | 401 API_KEY_REQUIRED | anthropic-version is NOT used as an auth header |
POST /v1/messages | x-api-key + tools + stream=true | 401 INVALID_API_KEY | Auth gate runs before schema parse or stream branch |
Install and configure the SDK
The Anthropic Python SDK is on PyPI as anthropic. The README documents ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY environment variables; the constructor also accepts base_url= and api_key= directly.
python -m venv .venv source .venv/bin/activate pip install anthropic export ANTHROPIC_BASE_URL="https://monkeytheme.com" export ANTHROPIC_API_KEY="***"
Do not commit the key into source. os.environ is fine for the snippet below; for production prefer a secrets manager or ~/.zshrc-level shell export.
import os
import anthropic
client = anthropic.Anthropic(
api_key=os.environ["ANTHROPIC_API_KEY"],
base_url=os.environ["ANTHROPIC_BASE_URL"],
)
base_url to the API root without a trailing /v1 or /messages. The SDK builds the full URL itself: {base_url}/v1/messages. Appending /v1 by hand causes a 404 because the resulting path becomes /v1/v1/messages.Verify the key before sending real prompts
The Anthropic SDK does not expose models.list(). To confirm a key is alive, send a single-token client.messages.create() call with max_tokens=1. If the key is bad you get a 401 with a request ID; if the key is good you get a 1-token Claude reply with a model field that confirms the upstream model name.
resp = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1,
messages=[{"role": "user", "content": "Reply with OK"}],
)
print(resp.model, resp.stop_reason, resp.usage)
If resp.model prints a Claude model name visible to your account, the SDK wiring is correct. The Anthropic SDK README pins anthropic-version to 2023-06-01 on every request; you do not need to set it yourself.
How should errors be handled?
The SDK raises anthropic.APIStatusError for non-2xx responses. For connection and timeout failures it raises anthropic.APIConnectionError. Log the request ID from the exception; do not log the API key.
import anthropic
try:
resp = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=64,
messages=[{"role": "user", "content": "hi"}],
)
except anthropic.APIStatusError as exc:
print("HTTP", exc.status_code, "request_id", exc.request_id)
raise
except anthropic.APIConnectionError as exc:
print("network/TLS:", repr(exc.__cause__))
raise
A 401 with code=INVALID_API_KEY from MonkeyAI is the same code the OpenAI route returns. The relay does not distinguish between Anthropic and OpenAI keys at this layer; both are MonkeyAI keys, and they live in the same backend.
Streaming, tools and retries
Streaming on /v1/messages uses Server-Sent Events. The Anthropic SDK returns an iterator you can for-loop over. Retries on 429 and 5xx are on by default; set max_retries=0 while debugging so a transient 429 does not silently mask the real failure.
with client.messages.stream(
model="claude-3-5-sonnet-20241022",
max_tokens=256,
messages=[{"role": "user", "content": "Summarise this in one sentence."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
Tools work the same way they do against api.anthropic.com. If a tool_use block comes back, the SDK parses it into resp.content. If you instead see a 400 from upstream after a 200 from the relay, the upstream account likely rejected the tool schema, not MonkeyAI.
Common configuration mistakes
- Setting
base_urltohttps://monkeytheme.com/v1with the SDK on top. The SDK already adds/v1; the resulting path becomes/v1/v1/messagesand nginx returns 404. - Setting
api_keyto the OpenAI-stylesk-***value. The Anthropic SDK passesx-api-key; the relay accepts it on/v1/messages, but the same string is the MonkeyAI account key, so do not confuse it with Anthropic's own keys. - Stripping
anthropic-versionmanually. The SDK sets it; removing it does not help and breaks upstream compatibility checks. - Calling
client.models.list()on the Anthropic client. There is no such method on the Anthropic SDK; userequestsorcurlagainstGET /v1/modelsdirectly. - Treating a 401 INVALID_API_KEY as evidence of upstream outage. The 401 came from the relay's auth gate, not from the upstream account. Check the API key first.
Sources
FAQ
What Base URL should I use?
Set base_url to https://monkeytheme.com. The SDK appends /v1/messages.
Does the SDK send Authorization Bearer?
No. The Anthropic Python SDK sends x-api-key by default. MonkeyAI also accepts Authorization: Bearer on /v1/messages.
Does anthropic-version matter?
The Anthropic SDK sets it on every request. MonkeyAI does not require it but passes it upstream; stripping it on the client side breaks upstream, not MonkeyAI.
Does models.list() exist on the Anthropic SDK?
No. The Anthropic SDK does not expose a models list. Use GET /v1/models via curl or the requests library instead.
Does 401 prove the SDK wiring is correct?
No. 401 INVALID_API_KEY proves the request reached the relay and auth was rejected. Run a real client.messages.create() with your own key to verify generation.
Related guides
OpenAI Python SDK
The OpenAI-compatible equivalent for /v1/chat/completions and /v1/responses.
Open page