Before you start
- Get a key from the API key page. New users can claim $0.50 of trial credit with one click, no payment details needed, valid for 7 days.
- Install the SDK with
pip install openai. - Export your key:
export API_KEY=..., or paste it into the code while testing.
That is all. The endpoint speaks the OpenAI protocol, so there is no separate client library to learn.
The bot in 20 lines
from openai import OpenAI
client = OpenAI(base_url="https://api.botchataiunfiltered.com/v1", api_key="YOUR_API_KEY")
history = [{"role": "system", "content": "You are a blunt, friendly chat partner. Keep replies short."}]
while True:
user = input("you> ").strip()
if user in {"", "/quit"}:
break
history.append({"role": "user", "content": user})
stream = client.chat.completions.create(model="uncensored", messages=history, stream=True)
reply = ""
print("bot> ", end="")
for chunk in stream:
if chunk.choices:
piece = chunk.choices[0].delta.content or ""
reply += piece
print(piece, end="", flush=True)
print()
history.append({"role": "assistant", "content": reply})Run it, type a message, and the answer prints as it is generated. Press Enter on an empty line or type /quit to leave.
How the loop works
Three details do most of the work.
History is a list you own. The server remembers nothing between requests. Each turn you append the user message, send the whole list, then append the assistant reply. Remove the history appends and the bot forgets everything after each answer.
Streaming returns chunks. With stream=True the reply arrives over SSE as small deltas. The if chunk.choices check matters because the final chunk can carry usage data and no text. That check keeps the loop from crashing on it.
The system message sets the voice. It is the first entry and stays there. Change that one string to change the bot's character, tone or language. For longer prompts and rules, see the production checklist for uncensored AI chat.
The three roles are worth a sentence each. system carries instructions that apply to the whole chat. user is what the person typed. assistant is what the model said before. Sending earlier assistant messages back is how you make the model aware of its own past replies, and you can also edit them to steer a conversation, for example to shorten a rambling answer before it becomes a pattern.
Other runtimes: Node and a web endpoint
Node
Save this as bot.mjs, run npm install openai, then node bot.mjs.
import OpenAI from "openai";
import readline from "node:readline/promises";
const client = new OpenAI({ baseURL: "https://api.botchataiunfiltered.com/v1", apiKey: process.env.API_KEY });
const history = [{ role: "system", content: "You are a blunt, friendly chat partner. Keep replies short." }];
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
while (true) {
const user = (await rl.question("you> ")).trim();
if (user === "" || user === "/quit") break;
history.push({ role: "user", content: user });
const res = await client.chat.completions.create({ model: "uncensored", messages: history });
const reply = res.choices[0].message.content;
console.log("bot>", reply);
history.push({ role: "assistant", content: reply });
}
rl.close();A web endpoint with FastAPI
To use the bot from a web page or an app, wrap the same loop in a small server and keep the key on the server side. This FastAPI version keeps one history per session ID.
from fastapi import FastAPI
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI(base_url="https://api.botchataiunfiltered.com/v1", api_key="YOUR_API_KEY")
app = FastAPI()
SYSTEM = {"role": "system", "content": "You are a blunt, friendly chat partner. Keep replies short."}
sessions = {}
class Message(BaseModel):
session: str
text: str
@app.post("/chat")
def chat(msg: Message):
history = sessions.setdefault(msg.session, [])
history.append({"role": "user", "content": msg.text})
r = client.chat.completions.create(
model="uncensored",
messages=[SYSTEM] + history[-20:],
max_tokens=500,
)
reply = r.choices[0].message.content
history.append({"role": "assistant", "content": reply})
return {"reply": reply}Run it with uvicorn app:app. The dictionary lives in memory, so move it to a database when you have real users. Add a per-session message limit so a single visitor cannot spend your credit.
What a conversation costs
Because the history is resent, input grows each turn while output stays flat. Take a chat of 50 turns where each user message is 100 tokens, each reply is 150 tokens and the system message is 50 tokens.
- Input on turn k is 50 + 250 x (k - 1) + 100 tokens. Summed over 50 turns that is 313,750 input tokens.
- Output is 150 x 50 = 7,500 tokens.
- Cost: 313,750 x $0.15 / 1M = $0.0471, plus 7,500 x $0.50 / 1M = $0.00375, so about $0.051 for the whole chat.
Pay-as-you-go from prepaid credit means this bot costs nothing while idle, and credit never expires. The pricing page has the top-up amounts from $20 and the bonus tiers. Failed or refused requests are not charged.
Capping history changes the picture. If the bot only sends the last 20 messages, input on turn k is 50 + 250 x min(k - 1, 10) + 100 tokens. Over 50 turns that totals 118,750 input tokens, or 118,750 x $0.15 / 1M = $0.0178, plus the same $0.00375 for output. The chat costs about $0.022 instead of $0.051, less than half, and the saving grows with chat length.
Make it survive real use
Two changes turn the toy into something you can leave running: cap how much history you send, and retry on 429. A full key allows 8 concurrent requests and 300 requests per minute, and a trial key allows 2 and 60. Over a limit the API answers with HTTP 429. On temporary overload it adds a Retry-After: 5 header.
import time
from openai import OpenAI, RateLimitError
client = OpenAI(base_url="https://api.botchataiunfiltered.com/v1", api_key="YOUR_API_KEY", max_retries=0)
def ask(history, tries=4):
for attempt in range(tries):
try:
return client.chat.completions.create(
model="uncensored",
messages=[history[0]] + history[-20:], # system message plus last 20 turns
max_tokens=600,
)
except RateLimitError as err:
wait = float(err.response.headers.get("retry-after", 5))
print(f"429 ({err.code}), waiting {wait}s")
time.sleep(wait)
raise RuntimeError("still rate limited after retries")The error code tells you which limit you hit: rate_limited means too many requests per minute, concurrency means too many at once. A streamed request holds its slot until the stream ends, so close or finish every stream. Slicing the history to the last 20 messages keeps the bill flat. A summary of older turns can replace what you drop. For other ways to read errors and balance, the API docs cover the response headers, including X-Balance-USD.
Where to take it next
Once the loop works, the next steps are small. Move the system message into a file so you can edit the persona without touching code. Save history to a JSON file on exit and load it on start, which gives the bot a memory across runs. Add a /reset command that clears everything except the first message. When you want the model to call your own functions, pass tools and tool_choice, and send results back as role: "tool" messages; the API docs show the shape.
For the operational side, such as logging, alerts and abuse handling, read the production checklist.
Troubleshooting
Test with curl first
Before you debug code, confirm the key and the URL work with one request. If this returns JSON with a reply, the problem is in your program and not in the connection.
curl https://api.botchataiunfiltered.com/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"uncensored","messages":[{"role":"user","content":"Say hello in five words."}]}'The response headers include X-Request-Id, which is useful when you ask for support, and X-Balance-USD, which shows what is left of your credit.
Common mistakes and quick fixes
- The URL is missing
/v1. The base URL ends in/v1. The SDK adds/chat/completionsitself. - The model name is wrong. Use
uncensored, exactly. This API serves one model. - The key is sent in the wrong place. Auth is an
Authorization: Bearerheader, which the SDK sets fromapi_key. - The bot forgets the conversation. The history is not being appended or is being rebuilt on every turn.
- Replies stop mid-sentence. You set
max_tokenstoo low. It is optional, and the completion can use the rest of the 100,000 token window. - Requests fail in bursts. You hit the concurrency or per-minute limit. Add the retry shown above and queue calls.
