Build a Chatbot with the Claude API
Connect to the Claude API and build a terminal chatbot with streaming.
Introduction
30 Second Summary
Every time you chat with an AI assistant, something interesting happens behind the scenes. Your message gets sent to an API, the AI processes it, and a response comes back. But the real magic is how the conversation continues. The AI does not actually remember anything on its own. Your app has to send the entire conversation history with every single message.
In this project, you will build a terminal-based chatbot powered by the Claude API. You will learn how to authenticate, send messages, manage conversation history, and stream responses in real time.
What You'll Build
A Python script that connects to the Anthropic Messages API to hold multi-turn conversations with Claude directly in your terminal.
By the end of this project, you'll have:
- A working chatbot that authenticates with the Anthropic API and sends messages to Claude.
- A conversation loop that maintains full chat history so Claude remembers what you said.
- Real-time streaming output so responses appear word-by-word as Claude generates them.
- Secret Mission: Add conversation export to save your chat history as a Markdown file.
Are there any prerequisites?
None. This guide walks you through installing Python, creating your Anthropic account, and setting up everything from scratch. You will need to purchase at least $5 in API credits to activate your account, but the project itself costs only a few cents to complete.
Set Up Python and Your Anthropic Account
Before building your chatbot, you need three things in place. Python runs your script. The Anthropic API connects your script to Claude.
In this step, you will install Python, create an Anthropic Console account, and configure your API key so your code can authenticate with Claude.
In this step, get ready to:
- Install Python 3 on your Mac via Homebrew.
- Create an Anthropic Console account and generate an API key.
- Set your API key as an environment variable for authentication.
Install Python 3 via Homebrew
Python is the language your chatbot script will run in. Homebrew is a package manager for macOS that makes installing developer tools simple.
- Press Cmd+Space to open Spotlight.
- Type Terminal and press Enter to open it.
First, check whether Homebrew is already installed on your Mac.
- Check if Homebrew is installed by running this command:
brew --version
✔️ I see a version number
Homebrew is ready to go. Skip ahead to the Python install below.
ⓧ Command not found
Homebrew is not installed yet. No worries, you can install it with one command.
- Install Homebrew by running this command:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
What does this command do?
This downloads and runs Homebrew's official install script from GitHub. The script will explain what it plans to do and pause for your confirmation before making any changes.
The installation may take a few minutes. Once it finishes, you should see a success message in your Terminal.
Installation asking for a password?
Homebrew needs your Mac login password to install system files. Type your password (it will not appear on screen) and press Enter.
help me install Homebrew on my Mac
- Install Python 3.13 by running this command:
brew install python@3.13
This may take a minute or two while Homebrew downloads and configures Python.
- Verify the installation by running this command:
python3 --version
You should see a version number like Python 3.13.3 or similar printed in your Terminal.
Don't see a version number?
If you see command not found, try closing and reopening Terminal, then run python3 --version again. Homebrew sometimes needs a fresh Terminal session to register new commands.
help me troubleshoot my Python 3 installation via Homebrew on macOS
Create your Anthropic Console account
The Anthropic Console is where you manage your API keys and billing. You need an account and credits before your code can talk to Claude.
- Open your browser and go to console.anthropic.com.
- Click Sign Up and create your account using your email or an existing Google/GitHub login.
- Once signed in, click Settings in the left sidebar.
- Click API keys.
- Click Create Key.
- Give your key a name like chatbot-project and click Create Key.
- Copy the key and paste it somewhere safe (you will not be able to see it again).
Keep your API key secret
Your API key is like a password. Never share it publicly, commit it to Git, or paste it in a message. If it leaks, anyone can use your credits.
- In the left sidebar, click Billing.
- Purchase at least $5 in API credits to activate your account.
Why do I need to buy credits?
Anthropic uses prepaid credits for API usage. Your account needs a minimum $5 credit balance to make API calls. The actual cost of this project is tiny (well under $0.50) because you will use claude-haiku-4-5, the fastest and cheapest model at $1 per million input tokens.
New users may receive a small amount of free credits to test the API. Check your balance before purchasing.
Set your API key as an environment variable
Your Python script needs access to your API key to authenticate with Claude. Instead of pasting the key directly into your code, you store it as an environment variable. This keeps it out of your source files.
- Set your API key for the current Terminal session by running this command (replace your-api-key-here with the key you copied earlier):
export ANTHROPIC_API_KEY='your-api-key-here'
What does this command do?
The export command creates an environment variable called ANTHROPIC_API_KEY in your Terminal session. The Anthropic Python SDK automatically looks for this variable when it creates a client, so you never have to type your key in code.
This only lasts for your current Terminal session. To make it persist across future sessions, add the same line to your shell profile.
- Add the export line to your ~/.zshrc file by running this command (replace your-api-key-here with your actual key):
echo "export ANTHROPIC_API_KEY='your-api-key-here'" >> ~/.zshrc
What is ~/.zshrc?
The ~/.zshrc file is your shell profile. It runs automatically every time you open a new Terminal window. Adding your API key here means you will not have to re-export it every session.
- Verify the variable is set by running this command:
echo $ANTHROPIC_API_KEY
You should see your API key printed in the Terminal. This confirms the Anthropic SDK will be able to find it.
Seeing a blank line instead of your key?
Make sure you ran the export command in the same Terminal window where you are running echo. If you opened a new Terminal tab, the variable only exists in the original tab until you reload your shell profile.
Run source ~/.zshrc to reload your profile in the current window.
help me set up my ANTHROPIC_API_KEY environment variable on macOS
Your development environment is fully set up. Next, you will install the Anthropic SDK and send your first message to Claude.
Install the SDK and Send Your First Message
Your Python environment is ready and your API key is set. Now it's time to actually talk to Claude.
The Anthropic Python SDK is a library that wraps the Claude API into simple Python functions. In this step, you'll install it, write a short script, and confirm that Claude responds to your very first message.
In this step, get ready to:
- Install the Anthropic Python SDK.
- Write a script that sends a message to Claude.
- Confirm Claude responds successfully.
Create your project folder and install the SDK
You need a dedicated folder for your chatbot project and the SDK installed inside it. The SDK handles authentication, request formatting, and response parsing so you can focus on what to say to Claude.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir claude-chatbot
cd claude-chatbot
- Confirm you're in the new folder by running:
pwd
You should see a path ending in /claude-chatbot.
- Install the Anthropic Python SDK by running:
pip3 install anthropic
You should see output ending with Successfully installed anthropic-... (the version number will vary).
Seeing a permission error or command not found?
If you see command not found: pip3, your Python installation may not be linked correctly. Try running python3 -m pip install anthropic instead.
If you see a permissions error, try adding --user to the end of the command: pip3 install anthropic --user.
Why can't I install the Anthropic SDK?
Create your first API script
Every Claude API call needs two things: a client that handles authentication, and a message structure that tells Claude what to respond to. Your first script will combine both into a single API request.
- Create a new file called chatbot.py by running:
touch chatbot.py
- Confirm the file exists by running:
ls
You should see chatbot.py in the output.
- Open chatbot.py in your preferred text editor and paste the following code:
import anthropic
# Create a client (automatically reads ANTHROPIC_API_KEY from environment)
client = anthropic.Anthropic()
# Send a single message to Claude
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Hello, Claude!"}
]
)
# Print Claude's response text
print(response.content[0].text)
What does this code do?
- import anthropic loads the SDK you just installed.
- anthropic.Anthropic() creates a client that automatically reads your ANTHROPIC_API_KEY environment variable. No need to pass the key directly.
- client.messages.create() sends a request to the Messages API. You specify the model, a token limit, and a list of messages.
- response.content[0].text extracts the text from Claude's response. The API returns a list of content blocks, and you grab the text from the first one.
Why claude-haiku-4-5?
Claude Haiku 4.5 is the fastest and most affordable Claude model. It costs $1 per million input tokens and $5 per million output tokens. For learning API integration patterns, speed and cost matter more than raw capability.
- Save chatbot.py.
Run the script and confirm your setup
Running the script sends your message to the Claude API and prints back whatever Claude says. This confirms your API key, SDK, and network connection are all working together.
- Run your script by entering this command:
python3 chatbot.py
After a moment, you should see a friendly response from Claude printed in your terminal. It will be a greeting or a helpful reply to your "Hello, Claude!" message.
Seeing an authentication error?
If you see AuthenticationError, your API key is not being found. Make sure you ran the export ANTHROPIC_API_KEY='your-key' command in the same Terminal window where you're running the script.
If you see RateLimitError or a billing error, confirm that you purchased API credits in your Anthropic Console under Billing. Your account needs at least $5 in credits to activate.
If you see ModuleNotFoundError: No module named 'anthropic', your pip install may have gone to a different Python version. Try running python3 -m pip install anthropic and then run the script again.
Why isn't my chatbot.py script working?
✔️ Awesome, I've got everything!
Great. Double check you've saved chatbot.py and that your script printed a response from Claude.
ⓧ I'd like to double check the full code
Here's the complete chatbot.py file. Make sure yours matches this exactly:
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Hello, Claude!"}
]
)
print(response.content[0].text)
You just sent your first message to Claude and got a response back. Right now the script sends one message and exits. Next up, you'll turn this into a real conversation where Claude remembers everything you've said.
Add Multi-Turn Conversation Support
Your script can send a message to Claude and get a response. That proves everything is connected and working.
Right now, your chatbot forgets everything after each run. A real conversation needs memory. In this step, you will refactor your script into a loop that tracks the full conversation history, so Claude can reference what you said earlier.
In this step, get ready to:
- Refactor your script to maintain a conversation history list.
- Build a conversation loop with quit commands.
- Add a system prompt and test multi-turn context.
Set up the conversation structure
The Messages API is stateless. It does not remember anything between requests. Every time you call the API, you need to send the entire conversation so far.
This means your code is responsible for tracking what was said. You will store messages in a Python list and pass that list to every API call.
- Open chatbot.py in your text editor and delete all the existing code.
- Replace it with this new structure that sets up the conversation foundation:
import anthropic
def main():
client = anthropic.Anthropic()
system_prompt = "You are a friendly and helpful AI assistant. You enjoy having conversations and helping people with their questions. Keep your responses concise but informative."
conversation_history = []
print("Claude Chatbot")
print("=" * 40)
print("Type your message and press Enter.")
print("Type 'quit' or 'exit' to end the conversation.")
print("=" * 40)
print()
What does this code do?
You have wrapped everything in a main() function. This is standard Python practice for scripts that run directly.
The system_prompt string defines the chatbot's personality. Claude reads this before every response to understand how it should behave.
The conversation_history list starts empty. Every message you send and every response Claude gives will be appended here, building up the full conversation over time.
What is a system prompt?
A system prompt is a set of instructions that shapes how Claude responds. It is separate from the conversation messages and gets sent with every API request.
Think of it as setting the stage before the conversation begins. You can change the personality, tone, or focus of your chatbot just by editing this one string.
Build the conversation loop
The loop is where the actual chatting happens. It reads your input, sends the full history to Claude, and stores the response for next time.
- Add the following code directly below the last print() line in your main() function, keeping the same indentation level:
while True:
user_input = input("You: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("\nGoodbye! Thanks for chatting.")
break
conversation_history.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
system=system_prompt,
messages=conversation_history,
)
assistant_message = response.content[0].text
print(f"\nClaude: {assistant_message}\n")
conversation_history.append({"role": "assistant", "content": assistant_message})
if __name__ == "__main__":
main()
What does this code do?
- input("You: ") waits for you to type something and press Enter.
- If you type nothing and press Enter, continue skips back to the top of the loop.
- Typing "quit" or "exit" triggers break to end the loop.
- Your message gets appended to conversation_history BEFORE the API call. Claude's response gets appended AFTER. This keeps the full conversation available for the next turn.
- The system=system_prompt parameter passes your personality instructions to Claude with every request.
- if __name__ == "__main__" ensures main() only runs when you execute the file directly.
- Save chatbot.py.
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete chatbot.py file at this point:
import anthropic
def main():
client = anthropic.Anthropic()
system_prompt = "You are a friendly and helpful AI assistant. You enjoy having conversations and helping people with their questions. Keep your responses concise but informative."
conversation_history = []
print("Claude Chatbot")
print("=" * 40)
print("Type your message and press Enter.")
print("Type 'quit' or 'exit' to end the conversation.")
print("=" * 40)
print()
while True:
user_input = input("You: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("\nGoodbye! Thanks for chatting.")
break
conversation_history.append({"role": "user", "content": user_input})
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
system=system_prompt,
messages=conversation_history,
)
assistant_message = response.content[0].text
print(f"\nClaude: {assistant_message}\n")
conversation_history.append({"role": "assistant", "content": assistant_message})
if __name__ == "__main__":
main()
Seeing an indentation error?
Python is strict about indentation. The while True: loop and everything above it inside main() must be indented with 4 spaces. The code inside the loop needs 8 spaces.
Make sure the if __name__ == "__main__": line is at the very bottom of the file with NO indentation. It lives outside the function.
Help me fix an indentation error in my chatbot.py file
Test your multi-turn conversation
Now for the fun part. You will run the chatbot and verify that Claude remembers what you said earlier in the conversation.
- Run your chatbot by executing this command:
python3 chatbot.py
You should see the welcome banner with instructions printed in your terminal, followed by a You: prompt waiting for your input.
- Type a message like My name is Alex and press Enter.
- Wait for Claude's response to appear.
- Type a follow-up like What is my name? and press Enter.
Claude should correctly recall your name. This confirms that conversation history is being sent with each request.
- Type quit to exit the chatbot.
Why does Claude remember now?
Each API call receives the entire conversation_history list. By the time you ask your second question, the list already contains your first message and Claude's first response. Claude reads all of it before generating the next answer.
This is how every Claude-powered application works, whether it is a web app, a Slack bot, or a custom tool. Your code manages the memory.
Claude not remembering previous messages?
Make sure you are appending both the user message AND the assistant response to conversation_history. If you only append user messages, Claude never sees its own prior responses.
Also check that the user append happens BEFORE the API call, and the assistant append happens AFTER.
Help me debug why Claude is not remembering my previous messages
Your chatbot now holds real conversations and remembers everything you say. Next up, you will make the responses stream in word-by-word for a much more natural feel.
Add Streaming for Real-Time Output
Your chatbot holds full conversations and remembers everything from the session. There is one problem though. Right now, Claude's response appears all at once after the entire response finishes generating.
Real chat applications show text as it is being generated, word by word. This is called streaming. In this step, you will replace the single API call with a streaming connection that prints each chunk of text the moment it arrives.
In this step, get ready to:
- Replace the standard API call with a streaming connection.
- Capture the streamed response and store it in conversation history.
- Test that streaming and multi-turn memory both work together.
Replace the API call with streaming
The Anthropic SDK provides a client.messages.stream() method that returns text in small chunks as Claude generates them. Instead of waiting for the full response, you iterate over these chunks and print each one immediately.
- In chatbot.py, find this block of code inside your while True loop (everything after the line that appends the user message to history):
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
system=system_prompt,
messages=conversation_history,
)
assistant_message = response.content[0].text
print(f"\nClaude: {assistant_message}\n")
conversation_history.append({"role": "assistant", "content": assistant_message})
- Delete that entire block (from response = client.messages.create( down to the conversation_history.append line) and replace it with the following streaming code:
# Print the label before streaming starts
print("\nClaude: ", end="", flush=True)
# Accumulate the full response as chunks arrive
full_response = ""
# Stream the response from Claude
with client.messages.stream(
model="claude-haiku-4-5",
max_tokens=1024,
system=system_prompt,
messages=conversation_history,
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
full_response += text
# Add spacing after the streamed response
print("\n")
# Save the complete response to conversation history
conversation_history.append({"role": "assistant", "content": full_response})
What does this code do?
This code replaces the single blocking API call with a streaming connection. Here is what each piece does:
- print("\nClaude: ", end="", flush=True) prints the label without a newline, so streamed text appears on the same line.
- client.messages.stream() opens a streaming connection to Claude. The with keyword ensures the connection closes cleanly when done.
- stream.text_stream is an iterator that yields small text chunks as Claude generates them.
- full_response += text accumulates every chunk into one string so you can save the complete response to history after streaming finishes.
- flush=True forces Python to display each chunk immediately instead of buffering output.
Why accumulate into a string instead of using get_final_message()?
The Messages API is stateless. It does not remember your conversation. You must send the full history with every request.
By building full_response as you stream, you have the complete text ready to append to conversation_history the moment the stream ends. This keeps the logic simple and self-contained.
- Save chatbot.py.
Seeing a syntax error when you save?
- Make sure the indentation of the new code matches the original. Everything inside the while True loop should be indented by 8 spaces (two levels).
- Check that you deleted the entire old block. If any lines from response = client.messages.create( or assistant_message remain, they will conflict with the new streaming code.
- Verify the with block ends with ) as stream: on the same line as the closing parenthesis.
Help me fix a syntax error in my streaming chatbot code.
Test streaming and conversation memory
Time to see streaming in action and confirm that multi-turn memory still works with the new approach.
- Run your chatbot by entering this command:
python3 chatbot.py
- Type a message like What are three fun facts about octopuses? and press Enter.
- Watch the response appear word by word instead of all at once.
- Send a follow-up that references your first message, like Which of those is the most surprising?.
- Confirm that Claude's reply references the octopus facts from your first message.
- Type quit to exit.
You should see text appearing character by character in your terminal with a smooth typing effect. Claude should also correctly remember your earlier messages.
Response appears all at once instead of streaming?
- Make sure you are using client.messages.stream() and not client.messages.create(). The create() method waits for the full response before returning.
- Check that both print() calls include flush=True. Without flushing, Python buffers the output and shows it all at the end.
- Verify that end="" is set in both print statements. Without it, each chunk would appear on its own line.
Help me debug why my chatbot is not streaming responses.
✔️ Awesome, I've got everything!
Great. Double check you have saved chatbot.py before moving on.
ⓧ I'd like to double check the full code
Here is the complete chatbot.py file. Compare it against yours to spot any differences:
import anthropic
def main():
client = anthropic.Anthropic()
system_prompt = "You are a friendly and helpful AI assistant. You enjoy having conversations and helping people with their questions. Keep your responses concise but informative."
conversation_history = []
print("Claude Chatbot")
print("=" * 40)
print("Type your message and press Enter.")
print("Type 'quit' or 'exit' to end the conversation.")
print("=" * 40)
print()
while True:
user_input = input("You: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("\nGoodbye! Thanks for chatting.")
break
conversation_history.append({"role": "user", "content": user_input})
print("\nClaude: ", end="", flush=True)
full_response = ""
with client.messages.stream(
model="claude-haiku-4-5",
max_tokens=1024,
system=system_prompt,
messages=conversation_history,
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
full_response += text
print("\n")
conversation_history.append({"role": "assistant", "content": full_response})
if __name__ == "__main__":
main()
Your chatbot now feels like a real conversation. Responses flow in naturally, and Claude remembers everything you have discussed. You have built a fully functional AI chatbot using the core patterns that power every Claude integration.
Secret mission
Add Conversation Export
Your chatbot conversations disappear the moment you close Terminal. Add a /save command that exports your full chat history to a Markdown file you can read, share, or reference later.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs from leaving your files in place. API credits are only consumed when you actively run the chatbot. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Python 3.13 installed via Homebrew.
- Anthropic Console account with API key.
- Prepaid API credits (minimum $5 purchase).
- The anthropic Python package.
- The claude-chatbot project folder with chatbot.py.
- Environment variable ANTHROPIC_API_KEY in your ~/.zshrc file.
Keep everything running
No action needed. Choose this if you plan to keep experimenting with your chatbot or extending it with new features.
- Your project folder stays on your Desktop.
- Your API key remains active for future use.
- Your prepaid credits remain in your account until you use them.
Pause - I'll come back to this later
Remove your API key from the active environment to avoid accidental usage. Your files and account stay intact for when you return.
- Open ~/.zshrc in a text editor and comment out the ANTHROPIC_API_KEY line by adding # at the start.
- Close your Terminal window to clear the current session variable.
- Keep your claude-chatbot folder in place for next time.
Delete - I don't want to use this again
Remove all project resources and credentials from your machine. Your Anthropic account and remaining credits stay active unless you close the account separately.
- Delete the project folder by running rm -rf ~/Desktop/claude-chatbot in Terminal.
- Open ~/.zshrc and remove the export ANTHROPIC_API_KEY line entirely.
- Uninstall the Python package by running pip3 uninstall anthropic in Terminal.
- To revoke your API key, go to console.anthropic.com, open Settings, and delete the key.
Nice Work!
Nice Work!
You just built a fully functional AI chatbot from scratch using the Claude API. This is the same foundation that powers production Claude integrations everywhere.
What you learned:
- Authenticated with the Claude API using environment variables and the Anthropic Python SDK.
- Built a multi-turn conversation loop that manages message history and sends full context with every API request.
- Added streaming responses so Claude's output appears word-by-word in real time.
- Secret Mission: Exported your conversation history to a formatted Markdown file with timestamps.
Ready to quiz yourself?