Official SDKs
FreeTheAI has official, open-source clients for Python, JavaScript and TypeScript, and Go. They speak the same OpenAI-compatible API as everything else on this site, and they know how the FreeTheAI API answers, so common situations need no code of your own:
- Busy models wait for the server's
Retry-After, or move straight to your fallback models. - One request at a time is learned on the first refusal; extra calls queue instead of failing.
- Long replies are fetched as a stream and assembled, so they don't hit idle timeouts.
- Cut-off streams raise
StreamInterruptedErrorwith the partial text, never a silently short answer. - The daily check-in and daily limit raise
CheckinRequiredErrorandDailyLimitErrorright away, with the link or the reset time. - Mistyped model IDs come back with suggestions, and a missing
fta/prefix is added for you.
| Language | Package | Source and docs |
|---|---|---|
| Python | freetheai on PyPI | GitHub, docs |
| JavaScript and TypeScript | freetheai on npm (no dependencies; Node.js 18+, Bun, Deno) | GitHub, docs |
| Go | github.com/Free-The-Ai/freetheai-go (standard library only) | GitHub, docs |
Prefer the OpenAI or Anthropic SDKs you already use? They work too: see OpenAI Python, OpenAI JavaScript, and Anthropic.
Install
pip install freetheainpm install freetheaigo get github.com/Free-The-Ai/freetheai-go@latestThe official SDKs read your key from FREETHEAI_API_KEY (or take it as an option):
export FREETHEAI_API_KEY="ftai_your_key_here"First request
from freetheai import FreeTheAI
client = FreeTheAI() # reads FREETHEAI_API_KEY; or FreeTheAI(api_key="ftai_...")
reply = client.chat.completions.create(
model="fta/zai/glm-5.3",
messages=[{"role": "user", "content": "Write a haiku about open models."}],
)
print(reply["choices"][0]["message"]["content"])import { FreeTheAI } from 'freetheai'
const client = new FreeTheAI() // reads FREETHEAI_API_KEY; or new FreeTheAI({ apiKey: 'ftai_...' })
const reply = await client.chat.completions.create({
model: 'fta/zai/glm-5.3',
messages: [{ role: 'user', content: 'Write a haiku about open models.' }],
})
console.log(reply.choices[0]?.message.content)package main
import (
"context"
"fmt"
"log"
"github.com/Free-The-Ai/freetheai-go"
)
func main() {
client, err := freetheai.NewClient() // reads FREETHEAI_API_KEY; or freetheai.WithAPIKey("ftai_...")
if err != nil {
log.Fatal(err)
}
reply, err := client.Chat.Completions.New(context.Background(), freetheai.ChatCompletionParams{
Model: "fta/zai/glm-5.3",
Messages: []freetheai.Message{{Role: "user", Content: "Write a haiku about open models."}},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(reply.Choices[0].Message.Content)
}The Python client returns plain dictionaries in the Chat Completions shape.
Streaming
messages = [{"role": "user", "content": "Tell me a short story."}]
for text in client.chat.completions.stream_text(model="fta/kimi/k3", messages=messages):
print(text, end="", flush=True)for await (const text of client.chat.completions.streamText({
model: 'fta/kimi/k3',
messages: [{ role: 'user', content: 'Tell me a short story.' }],
})) {
process.stdout.write(text)
}stream, err := client.Chat.Completions.NewStreaming(ctx, params)
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for stream.Next() {
for _, choice := range stream.Current().Choices {
fmt.Print(choice.Delta.Content)
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}Fallback models
Give the client models to try, in order, when the first one is busy, filtered, or down. The reply's model says which one answered.
client = FreeTheAI(fallback_models=["fta/zai/glm-5.3-flash", "fta/mmx/minimax-m3"])const client = new FreeTheAI({ fallbackModels: ['fta/zai/glm-5.3-flash', 'fta/mmx/minimax-m3'] })client, err := freetheai.NewClient(freetheai.WithFallbackModels("fta/zai/glm-5.3-flash", "fta/mmx/minimax-m3"))Web search
answer = client.answer("What changed in Python 3.14?")
print(answer["answer"], answer["sources"])
results = client.search("python 3.14 release notes", num_results=5)const answer = await client.answer({ query: 'What changed in Node.js 22?' })
console.log(answer.answer, answer.sources)
const results = await client.search({ query: 'node 22 release notes', num_results: 5 })See Search and Answer for the response fields.
Errors
Each SDK raises a specific error class for each FreeTheAI error, with the error ID to quote when there is one:
| Error | When |
|---|---|
AuthenticationError, IncompleteKeyError | 401: a missing, wrong, or partly pasted key |
CheckinRequiredError | 403: no check-in today (has the check-in link) |
EmailNotVerifiedError | 403: the email is not confirmed |
ModelNotFoundError | 404: unknown model, with suggestions |
ContentFilterError, ImagesNotSupportedError | 400: filtered, or images sent to a text-only model |
RequestTooLargeError | 413: body over 8 MB |
ConcurrencyLimitError | 429: a reply is still running |
DailyLimitError | 429: today's free requests are used (has the reset time) |
ProviderBudgetError | 429: a model family's daily budget is used |
ModelBusyError | 503: the model is busy right now |
StreamInterruptedError | A stream broke (has the partial text and error ID) |
from freetheai import FreeTheAI, CheckinRequiredError, DailyLimitError
client = FreeTheAI()
try:
reply = client.chat.completions.create(
model="fta/zai/glm-5.3",
messages=[{"role": "user", "content": "Hi"}],
)
except CheckinRequiredError as err:
print("Check in first:", err.checkin_url)
except DailyLimitError as err:
print("Out of free requests until", err.resets_at)In Go, every error is a typed value, so errors.As works (for example *freetheai.CheckinRequiredError). Every message and its fix is on the errors page.
Cookbook
The FreeTheAI Cookbook has runnable examples for each SDK, the OpenAI clients, curl, streaming, tool calling, web search, LangChain, LlamaIndex, the Vercel AI SDK, retrieval over local files, and structured JSON output.

