OpenAI Python SDK
The official openai package works with FreeTheAI as is. Only the base URL and the key change.
Install and configure
pip install openai
export FTAI_KEY="ftai_your_key_here"import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.freetheai.org/v1",
api_key=os.environ["FTAI_KEY"],
)You can also set the environment variables the SDK reads by default, OPENAI_BASE_URL=https://api.freetheai.org/v1 and OPENAI_API_KEY=ftai_..., and create the client with OpenAI().
Chat completion
reply = client.chat.completions.create(
model="fta/zai/glm-5.3",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain recursion in one paragraph."},
],
)
print(reply.choices[0].message.content)
print(reply.usage)Streaming
stream = client.chat.completions.create(
model="fta/kimi/k3",
messages=[{"role": "user", "content": "Plan a three-step study schedule for Go."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)Async
import asyncio
import os
from openai import AsyncOpenAI
client = AsyncOpenAI(base_url="https://api.freetheai.org/v1", api_key=os.environ["FTAI_KEY"])
async def main():
reply = await client.chat.completions.create(
model="fta/zai/glm-5.3",
messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(reply.choices[0].message.content)
asyncio.run(main())Free accounts run one request at a time, so send requests one after another rather than with asyncio.gather.
Responses API
response = client.responses.create(
model="fta/zai/glm-5.3",
input="Write a haiku about the sea.",
)
print(response.output_text)Tool calls
See Tool calls for a complete loop.
Fields the SDK does not know
Pass FreeTheAI-specific or model-specific fields with extra_body:
reply = client.chat.completions.create(
model="fta/zai/glm-5.3",
messages=[{"role": "user", "content": "Quick answer: 17 * 23?"}],
extra_body={"thinking": {"type": "disabled"}},
)Errors
The SDK raises typed exceptions. err.body holds FreeTheAI's error object, and its message is safe to show to people.
import openai
def error_message(err: openai.APIStatusError) -> str:
body = err.body if isinstance(err.body, dict) else {}
return body.get("message") or str(err)
try:
reply = client.chat.completions.create(
model="fta/zai/glm-5.3",
messages=[{"role": "user", "content": "Hi"}],
)
except openai.AuthenticationError as err: # 401
print("Check your key:", error_message(err))
except openai.PermissionDeniedError as err: # 403, for example the daily check-in
print("Not allowed:", error_message(err))
except openai.NotFoundError as err: # 404, unknown model
print("Unknown model:", error_message(err))
except openai.RateLimitError as err: # 429
print("Limit reached:", error_message(err))
except openai.APIStatusError as err: # 400, 413, 502, 503, ...
print(err.status_code, error_message(err), err.response.headers.get("x-error-id"))The SDK retries some errors on its own (2 retries by default). Set max_retries and timeout on the client to change that. Every message and its fix: Errors.

