Build a Chatbot with Claude: A Practical Guide
Building a chatbot with Claude means sending a sequence of user and assistant messages to Anthropic's Messages API, managing conversation history yourself, and streaming the response back to your frontend. Unlike older chat frameworks, there's no built-in "session" concept on the model provider's side — you own the conversation state, the system prompt, and how you handle errors, retries, and rate limits.
This guide walks through the actual pieces you need: message structure, system prompts, memory, streaming, and the operational stuff (auth, keys, rate limits) that determines whether your chatbot survives contact with real users.
The core request loop
Every Claude chatbot boils down to one repeated pattern: collect the conversation so far, send it as an array of messages, get a response, append it to history, repeat.
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": "You are a support agent for Acme Cloud. Be concise and cite docs when relevant.",
"messages": [
{ "role": "user", "content": "How do I reset my API key?" },
{ "role": "assistant", "content": "Go to Settings > API Keys and click Regenerate." },
{ "role": "user", "content": "Does the old key stop working immediately?" }
]
}
Each request is stateless from the model's perspective — you resend the full history every time. That's the whole trick. There's no hidden thread ID to manage on Anthropic's side.
Designing the system prompt
The system field is where your chatbot gets its personality, constraints, and domain knowledge. A few practical rules:
- Be specific about scope. "You are a support agent for Acme Cloud, only answer questions about our product" reduces off-topic drift far more than vague personas.
- Set the output format explicitly. If you want short answers, say so — Claude defaults to fairly thorough responses.
- Put static reference material in the system prompt, not the first user message. It's cheaper to reason about and keeps the conversation history clean.
- Avoid stuffing huge unstructured docs in. If you have more than a few thousand tokens of reference material, consider retrieval instead of pasting everything in every request.
Managing conversation memory
Claude has no memory between API calls — your backend is the memory. Two things to get right early:
Trimming history. As conversations grow, you'll hit context limits and pay more per turn. A common approach is to keep the last N turns verbatim and summarize older ones into a single system-level note. Don't wait until you hit token limits in production to build this — retrofit it early.
Persisting across sessions. If users expect the bot to remember them after closing the tab, you need to store conversation history in your own database (keyed by user ID or session ID) and reload it on the next request. Claude itself won't do this for you.
async function sendMessage(history, userInput) {
const messages = [...history, { role: "user", content: userInput }];
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
system: "You are a helpful support agent for Acme Cloud.",
messages
})
});
const data = await res.json();
return [...messages, { role: "assistant", content: data.content[0].text }];
}
Streaming for a real chat feel
Users expect chatbots to feel responsive, and waiting for a full response before showing anything reads as sluggish. Claude supports streaming responses via server-sent events, so you can render tokens as they arrive instead of waiting for the full completion.
On the frontend, this typically means reading a stream and appending text chunks to the UI as they come in rather than swapping in a finished message. If you're building this for the first time, it's worth reading through the streaming docs in detail — see /docs/streaming for the request format and event types.
Adding tools for real actions
A chatbot that can only talk is limited. If you want it to look up an order status, check inventory, or call an internal API, you'll use tool use (also called function calling): you define a tool schema, Claude decides when to call it, your backend executes the actual function, and you feed the result back into the conversation. This is what turns a Q&A bot into something that can actually resolve requests. See /docs/tools for the request shape.
Handling auth, keys, and rate limits without building it yourself
Once your chatbot logic works, you still need production plumbing: an API key system for your own app (not the raw Anthropic key), usage tracking per user or team, and a way to avoid one runaway conversation burning your entire rate limit budget.
This is the part SubToAPI handles so you don't build it from scratch. It sits in front of your existing Claude access and gives you:
- Application-scoped API keys (
sub_live_...) instead of sharing one raw key across your codebase - Streaming and tool use exposed through a standard HTTPS API
- Usage metadata per key, so you can see which chatbot instance or customer is driving cost
- Team seats if more than one person needs to manage keys and monitor usage
Setup is the same Messages-API shape shown above, just pointed at https://api.subtoapi.app/v1/messages with your sub_live_ key. Check /docs/quickstart to get a key working in a few minutes, or /docs/messages for the full request reference. Plans start at €9/month for solo builders, with team and scale tiers at /pricing, and a free trial at /signup if you want to test it against your existing chatbot code first.
Testing before you ship
Before pointing real users at your chatbot:
- Test with adversarial inputs — off-topic questions, attempts to override the system prompt, empty messages
- Check behavior when
max_tokensis hit mid-response - Simulate rate limit errors and confirm your app degrades gracefully instead of crashing
- Log full request/response pairs (redacted of PII) for at least the first few weeks so you can debug real user complaints
Questions
Do I need a framework to build a chatbot with Claude, or can I just call the API directly? You can call the Messages API directly with curl or fetch — no framework is required. Frameworks help with things like vector search or agent orchestration, but a basic chatbot is just message arrays and a system prompt.
How do I make Claude remember earlier parts of a long conversation? Claude doesn't retain memory between calls — your backend resends the conversation history each time. For long conversations, trim or summarize older turns to stay within context limits and control cost.
What's the difference between the system prompt and the first user message? The system prompt sets persistent behavior, tone, and constraints for the whole conversation and isn't part of the visible chat history. The first user message is just the opening turn, and Claude treats it like any other user input.