Anthropic SDK
FreeTheAI answers the Anthropic Messages format for every free model, so the official Anthropic SDKs work with a different base URL, your FreeTheAI key, and an fta/ model ID.
- Base URL:
https://api.freetheai.org(without/v1; the SDK adds/v1/messages) - Key: your
ftai_key as the API key - Model: an
fta/ID such asfta/zai/glm-5.3. Claude model names do not work.
Not Claude
These requests run on the model you name, not on Claude. Features that only exist in Claude models may not work.
Install and configure
python
# pip install anthropic
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api.freetheai.org",
api_key=os.environ["FTAI_KEY"],
)ts
// npm install @anthropic-ai/sdk
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({
baseURL: 'https://api.freetheai.org',
apiKey: process.env.FTAI_KEY,
})The SDKs also read ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY from the environment.
Create a message
python
message = client.messages.create(
model="fta/zai/glm-5.3",
max_tokens=1024,
system="You are a helpful assistant.",
messages=[{"role": "user", "content": "Explain DNS in three sentences."}],
)
for block in message.content:
if block.type == "text":
print(block.text)ts
const message = await client.messages.create({
model: 'fta/zai/glm-5.3',
max_tokens: 1024,
system: 'You are a helpful assistant.',
messages: [{ role: 'user', content: 'Explain DNS in three sentences.' }],
})
for (const block of message.content) {
if (block.type === 'text') console.log(block.text)
}Streaming
python
with client.messages.stream(
model="fta/kimi/k3",
max_tokens=2048,
messages=[{"role": "user", "content": "Write a short poem about rain."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)ts
const stream = client.messages.stream({
model: 'fta/kimi/k3',
max_tokens: 2048,
messages: [{ role: 'user', content: 'Write a short poem about rain.' }],
})
stream.on('text', (text) => process.stdout.write(text))
await stream.finalMessage()Tool use
python
import json
tools = [{
"name": "get_weather",
"description": "Get the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}]
messages = [{"role": "user", "content": "Is it cold in Reykjavik right now?"}]
while True:
message = client.messages.create(
model="fta/zai/glm-5.3", max_tokens=1024, tools=tools, messages=messages,
)
messages.append({"role": "assistant", "content": message.content})
if message.stop_reason != "tool_use":
print("".join(b.text for b in message.content if b.type == "text"))
break
results = []
for block in message.content:
if block.type == "tool_use":
weather = {"city": block.input["city"], "temperature_c": -2} # your lookup here
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(weather),
})
messages.append({"role": "user", "content": results})Errors
The SDK raises anthropic.APIStatusError subclasses (AuthenticationError, PermissionDeniedError, NotFoundError, RateLimitError, ...). FreeTheAI's error body is the same on every endpoint; see Errors.
Claude Code
Claude Code uses the same endpoint. See the Claude Code guide for a tested settings.json.

