Get API key

chatbotapis.comGuide

Build a Chatbot with an OpenAI-Compatible API

Learning how to build a chatbot using openai api calls comes down to one loop: send the message list, read the reply, append it, repeat. This tutorial builds that loop with history, streaming and error handling, using any OpenAI-compatible endpoint.

Message cards looping between a laptop and a cloud, showing a chatbot request and reply cycle

Updated

No card needed. After sign-up, claim $0.50 of free test credit with one click (once per person).

Get API key

Key points

  1. A chatbot is a list of messages you resend on every request; the API itself keeps no memory.
  2. Streaming makes replies appear as they are generated, with only a few extra lines of code.
  3. Trim old messages to keep cost and context under control.
  4. The same code works with any OpenAI-compatible endpoint after you change the base URL, key and model.

What you need

You need a key, a base URL and a model name. The OpenAI SDKs for Python and Node accept a custom base URL, which means the same code runs against any OpenAI-compatible service. For this tutorial the values are:

  • Base URL: https://api.chatbotapis.com/v1
  • Model: uncensored
  • Key: from the API key page. New users can claim $0.50 of trial credit there with one click after sign-up, no card needed, valid for 7 days.

The service is a hosted open-weight model, tuned to answer without content refusals for lawful adult use, not the official API of OpenAI or any other vendor. It is text only. Install the Python SDK with pip install openai.

Step 1: one request

Start with a single call to check that everything is connected.

from openai import OpenAI

client = OpenAI(base_url="https://api.chatbotapis.com/v1", api_key="YOUR_KEY")

response = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Give me three names for a coffee shop."},
    ],
)
print(response.choices[0].message.content)

Each message has a role: system for standing instructions, user for what the person says, and assistant for earlier replies. The same call as curl:

curl https://api.chatbotapis.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"uncensored","messages":[{"role":"user","content":"Give me three names for a coffee shop."}]}'

Step 2: add memory

The API does not remember your last request. To make a conversation, keep a list and send all of it every time. After each reply, append both sides.

history = [{"role": "system", "content": "You are a friendly support bot for a bike shop. Keep answers under 80 words."}]

def ask(text):
    history.append({"role": "user", "content": text})
    r = client.chat.completions.create(model="uncensored", messages=history)
    answer = r.choices[0].message.content
    history.append({"role": "assistant", "content": answer})
    return answer

print(ask("What size tire fits a 700c wheel?"))
print(ask("And what pressure should I run?"))

The second question works because the first exchange is in history. In a web app, keep one list per user session, in memory for a prototype or in a database for production.

Step 3: stream the reply, in Python and Node

Waiting for a full reply feels slow. With stream=True the server sends tokens as they are produced, over server-sent events, and the SDK gives you an iterator.

def ask_stream(text):
    history.append({"role": "user", "content": text})
    stream = client.chat.completions.create(model="uncensored", messages=history, stream=True)
    parts = []
    for chunk in stream:
        if chunk.choices and chunk.choices[0].delta.content:
            piece = chunk.choices[0].delta.content
            parts.append(piece)
            print(piece, end="", flush=True)
    print()
    history.append({"role": "assistant", "content": "".join(parts)})

Note the guard on chunk.choices: the final chunk can carry usage and no choices. A streamed request holds its concurrency slot until the stream ends, which matters if many users chat at once.

The same bot in Node

Install with npm install openai. This version streams to the console.

import OpenAI from "openai";
import readline from "node:readline/promises";

const client = new OpenAI({ baseURL: "https://api.chatbotapis.com/v1", apiKey: "YOUR_KEY" });
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const history = [{ role: "system", content: "You are a helpful assistant." }];

while (true) {
  const text = await rl.question("you> ");
  if (text === "") break;
  history.push({ role: "user", content: text });
  const stream = await client.chat.completions.create({ model: "uncensored", messages: history, stream: true });
  let answer = "";
  for await (const chunk of stream) {
    const piece = chunk.choices[0]?.delta?.content ?? "";
    answer += piece;
    process.stdout.write(piece);
  }
  process.stdout.write("\n");
  history.push({ role: "assistant", content: answer });
}
rl.close();

Step 4: control the length and the cost

History grows, and you pay for it on every request. The context window here is 100,000 tokens (prompt plus completion), which is generous but not infinite, and the price is $0.15 per 1M input tokens and $0.50 per 1M output tokens. A ten-turn chat with a 300-token system prompt, 150-token user messages and 250-token replies sends about 22,500 input and 2,500 output tokens in total, which costs about $0.0046. The page on LLM API pricing puts such numbers in a wider context.

A simple fix is to keep the system prompt and only the latest messages:

def trimmed(messages, keep=10):
    return messages[:1] + messages[1:][-keep:]

r = client.chat.completions.create(model="uncensored", messages=trimmed(history))

For long-running chats, ask the model to summarize older turns into one message, and replace them. The optional max_tokens parameter caps the reply, and temperature controls variety: lower for support bots, higher for creative ones.

Step 5: handle errors and limits

Real users cause real failures. Wrap calls and handle rate limits. This API returns HTTP 429 with the code rate_limited when you exceed requests per minute, concurrency when too many requests run at once, and 429 with a Retry-After: 5 header on temporary overload. A full key allows 8 concurrent requests and 300 requests per minute; a trial key allows 2 and 60.

import time
from openai import RateLimitError, APIConnectionError

def safe_ask(messages, tries=4):
    for attempt in range(tries):
        try:
            return client.chat.completions.create(model="uncensored", messages=messages)
        except RateLimitError:
            time.sleep(5 * (attempt + 1))
        except APIConnectionError:
            time.sleep(2)
    raise RuntimeError("service unavailable after retries")

Failed or refused requests are not charged, so retries do not cost you. Log the X-Request-Id header with each failure; it helps when you ask support about a specific call.

Writing the system prompt

The system message is where the bot gets its personality and its rules, and it is sent with every request, so it deserves a little care.

  • State the role and the audience. "You are a support assistant for a bike shop. Customers are beginners." is better than "Be helpful."
  • Set format limits. Word counts, no markdown in a chat widget, plain sentences for voice output.
  • List what to do when unsure. For example, ask one clarifying question instead of guessing.
  • Keep it short. Every token is billed per request. 300 tokens is plenty for most bots.
  • Test with awkward input. Typos, very long messages, off-topic questions and attempts to override the instructions. Adjust the prompt after each failure.

Change one thing at a time, and keep a small set of test conversations you re-run after each edit. Set seed and a low temperature when you want repeatable comparisons between prompt versions.

Where to go next

  • JSON mode. Pass response_format={"type": "json_object"} when the bot must return structured data.
  • Tool calling. Define tools so the bot can look up orders or run calculations, and return results with role: "tool".
  • A web front end. Put the loop behind a small server route, keep the key on the server, and stream to the browser. Never ship the key in client-side code.
  • Persona and safety. Write the system prompt for your use, keep personal data out of it, and add your own moderation if your audience needs it. The hard limit here is no sexual content involving minors.

For a web front end, a small server route keeps the key private and streams to the browser. A minimal FastAPI example:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from openai import OpenAI

app = FastAPI()
client = OpenAI(base_url="https://api.chatbotapis.com/v1", api_key="YOUR_KEY")

@app.post("/chat")
def chat(payload: dict):
    stream = client.chat.completions.create(
        model="uncensored", messages=payload["messages"], stream=True
    )
    def gen():
        for chunk in stream:
            if chunk.choices and chunk.choices[0].delta.content:
                yield chunk.choices[0].delta.content
    return StreamingResponse(gen(), media_type="text/plain")

In production, load the key from an environment variable, validate the incoming messages, and cap their length so a single visitor cannot send an 8 MB body of text, which is the maximum body size this API accepts.

Everything above, plus parameters and endpoint details, is in the docs. Top-ups and bonuses are on the pricing page. Prompts are not used for training.

Questions and answers

Do I need the official OpenAI service to follow this tutorial?

No. The OpenAI SDKs work with any OpenAI-compatible endpoint. Change the base URL, key and model and the same code runs.

How does a chatbot remember the conversation?

It does not, the API is stateless. Your code keeps the list of messages and sends the whole list, within the context window, on every request.

How do I make replies appear word by word?

Set stream=True and iterate over the chunks, printing each delta as it arrives.

How much does a chatbot cost to run?

With $0.15 per 1M input and $0.50 per 1M output tokens, a ten-turn conversation like the one above costs about half a cent. History length is the main driver.

Your key is one form away

Create an account, copy the key, change the base URL. That is the whole setup.

Get API keyRead the docs

ChatBotApis

chatbotapis.com