Build a streaming Claude API chatbot with Streamlit in Python (2026). Uses st.session_state for conversation history and st.write_stream for real-time token display.
Streamlit is the fastest way to build a Python UI for a Claude API chatbot — no HTML or JavaScript required. This guide shows the complete minimal app, the streaming pattern, and the conversation-history pattern.
pip install anthropic streamlit
import anthropic
import streamlit as st
st.title("Claude Chatbot")
# Initialize conversation history
if "messages" not in st.session_state:
st.session_state.messages = []
# Render existing messages
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# Chat input
if prompt := st.chat_input("Ask Claude anything..."):
# Show user message
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Stream Claude response
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
with st.chat_message("assistant"):
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=st.session_state.messages
) as stream:
response_text = st.write_stream(stream.text_stream)
# Persist assistant reply
st.session_state.messages.append({"role": "assistant", "content": response_text})
export ANTHROPIC_API_KEY="sk-ant-..."
streamlit run app.py
# .streamlit/secrets.toml (gitignored — never commit this)
ANTHROPIC_API_KEY = "sk-ant-..."
# In app.py — works locally and on Community Cloud
client = anthropic.Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"])
import anthropic
import streamlit as st
st.title("Claude Chatbot")
with st.sidebar:
model = st.selectbox("Model", [
"claude-sonnet-4-6",
"claude-haiku-4-5-20251001",
"claude-opus-4-7",
])
system_prompt = st.text_area(
"System prompt",
value="You are a helpful assistant. Be concise.",
height=120
)
if st.button("Clear conversation"):
st.session_state.messages = []
st.rerun()
if "messages" not in st.session_state:
st.session_state.messages = []
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
if prompt := st.chat_input("Message..."):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
client = anthropic.Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"])
with st.chat_message("assistant"):
with client.messages.stream(
model=model,
max_tokens=1024,
system=system_prompt,
messages=st.session_state.messages
) as stream:
response_text = st.write_stream(stream.text_stream)
st.session_state.messages.append({"role": "assistant", "content": response_text})
with client.messages.stream(...) as stream:
response_text = st.write_stream(stream.text_stream)
final = stream.get_final_message()
st.caption(
f"↑ {final.usage.input_tokens} tokens "
f"↓ {final.usage.output_tokens} tokens "
f"model: {final.model}"
)
st.session_state.messages.append({"role": "assistant", "content": response_text})
| Platform | Cost | Custom domain | Private repo | Best for |
|---|---|---|---|---|
| Streamlit Community Cloud | Free | No | No | Quick demos, open source |
| Railway | $5/mo hobby | Yes | Yes | Persistent apps, teams |
| Google Cloud Run | Pay-per-request | Yes | Yes | Production, scale-to-zero |
| Fly.io | Free tier + $3/mo | Yes | Yes | Always-on, low latency |
To estimate Claude API costs for your chatbot based on conversation length, use the Claude API Cost Calculator. For a more production-ready async setup, see the WebSocket streaming guide.