Claude API Chatbot with Streamlit

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.

💥 50p impulse-buy: Power Prompts PDF (first 10 buyers) 30 battle-tested Claude Code prompts · 8-page PDF · paste into CLAUDE.md and never re-type a prompt again · 50p impulse-buy, no commitment

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.

Installation

pip install anthropic streamlit

Minimal chatbot (app.py)

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})

Run locally

export ANTHROPIC_API_KEY="sk-ant-..."
streamlit run app.py

Use Streamlit secrets instead of env vars

# .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"])

System prompt and model selector sidebar

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})

Token usage display

    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})

Deployment comparison

PlatformCostCustom domainPrivate repoBest for
Streamlit Community CloudFreeNoNoQuick demos, open source
Railway$5/mo hobbyYesYesPersistent apps, teams
Google Cloud RunPay-per-requestYesYesProduction, scale-to-zero
Fly.ioFree tier + $3/moYesYesAlways-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.

Frequently asked questions

How do I use the Claude API with Streamlit in Python?
Install `anthropic` and `streamlit`. Store the conversation in `st.session_state.messages`. On each user input, call `client.messages.stream()` and pass the accumulated history. Use `st.write_stream()` to display tokens as they arrive.
How do I store conversation history in a Streamlit Claude chatbot?
Use `st.session_state.messages` — a list of `{role, content}` dicts that persists across reruns within the same session. Append the user message before calling Claude, then append the full assistant reply after streaming completes.
How do I stream Claude API responses in Streamlit?
Use `with st.chat_message('assistant'): st.write_stream(stream)` where `stream` is a generator yielding text chunks from `client.messages.stream()`. `st.write_stream()` handles the incremental display automatically.
Where do I put the ANTHROPIC_API_KEY in a Streamlit app?
For local dev, use a `.streamlit/secrets.toml` file with `ANTHROPIC_API_KEY = 'sk-ant-...'` and read it via `st.secrets['ANTHROPIC_API_KEY']`. For Streamlit Community Cloud, add it in the app's Secrets settings panel.
How do I deploy a Claude Streamlit app to Streamlit Community Cloud?
Push your app to a public GitHub repo, connect it at share.streamlit.io, then add `ANTHROPIC_API_KEY` in Settings → Secrets. Community Cloud is free for public repos. For private repos or custom domains, use Streamlit for Teams or host on Cloud Run / Railway.

Free tools

Cost Calculator → API Cookbook → Diff Summarizer → Skills Browser →

More examples

Claude API Python QuickstartClaude API Node.js / TypeScript QuickstartClaude API Streaming in PythonClaude API Streaming in Node.js / TypeScriptClaude API Tool Use in PythonClaude API Tool Use in Node.js / TypeScript