← Blog

Claude API Streaming Response in Python Flask

2026-10-17 · 4 min read · SubToAPI Team

Streaming Claude responses from a Flask backend

If you're building a Flask app that calls the Claude API, you almost always want to stream the response rather than wait for the full completion. Streaming lets your frontend show tokens as they're generated — the same experience users get in Claude's own chat interface — instead of staring at a spinner for 5–15 seconds while a long answer finishes.

The short answer: Claude's API supports streaming via server-sent events (SSE) when you set "stream": true in your request. Flask can forward that stream to the browser using a generator function and a Response object with mimetype="text/event-stream". The tricky part isn't calling Claude — it's correctly proxying the stream through Flask without buffering it, and parsing Anthropic's SSE event types on the way through.

Setting up the Flask route

Here's a minimal working example using the anthropic Python SDK and Flask's generator-based streaming:

from flask import Flask, Response, request
import anthropic
import json

app = Flask(__name__)
client = anthropic.Anthropic(api_key="sk-ant-...")

@app.route("/chat", methods=["POST"])
def chat():
    user_message = request.json.get("message", "")

    def generate():
        with client.messages.stream(
            model="claude-sonnet-4-5",
            max_tokens=1024,
            messages=[{"role": "user", "content": user_message}],
        ) as stream:
            for text in stream.text_stream:
                yield f"data: {json.dumps({'text': text})}\n\n"
            yield "data: [DONE]\n\n"

    return Response(generate(), mimetype="text/event-stream")

if __name__ == "__main__":
    app.run(threaded=True)

A few things that matter here:

Avoiding buffering issues

Streaming breaks most often not because of Claude's API but because something in the stack buffers the response before it reaches the client. Common culprits:

If you test locally with curl -N and see the text/event-stream data arrive in bursts instead of token-by-token, one of the above is almost always the cause.

Consuming the stream from the frontend

On the browser side, use the EventSource API or a fetch call with a readable stream reader:

const response = await fetch("/chat", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ message: "Explain async generators in Python" }),
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = decoder.decode(value);
  // parse the "data: ..." lines and append text to the UI
}

EventSource is simpler for GET requests but doesn't support custom headers or POST bodies natively, so most chat-style apps use fetch with a manual reader instead.

Handling tool use and errors in a stream

If your Claude requests involve tool calls, the stream will include content_block_start, content_block_delta, and content_block_stop events for tool-use blocks, not just text. The anthropic SDK's stream.text_stream helper only surfaces text deltas — if you need tool call data too, iterate over stream directly and check event.type. Also wrap the generator body in a try/except and yield an error event on failure, otherwise a dropped connection or rate limit mid-stream will just silently truncate the response with no signal to the frontend.

def generate():
    try:
        with client.messages.stream(...) as stream:
            for event in stream:
                if event.type == "content_block_delta" and event.delta.type == "text_delta":
                    yield f"data: {json.dumps({'text': event.delta.text})}\n\n"
    except Exception as e:
        yield f"data: {json.dumps({'error': str(e)})}\n\n"

When to put an API layer in front of this

This pattern works well for a single app calling Claude directly with an Anthropic key. It gets more complicated once you have multiple services, need per-team usage limits, or want to track token spend without building your own metering. That's the gap SubToAPI fills: it turns your existing Claude access into an HTTPS API with its own sub_live_... keys, so your Flask app (or any other service) calls https://api.subtoapi.app/v1/messages with standard streaming support, and usage/billing metadata comes back without you having to build logging and quota tracking yourself. The request and streaming shape matches what's shown above — see the streaming docs and messages reference for exact payloads, or the quickstart to get an API key from the signup page.

FAQ

Does the Claude API support streaming out of the box? Yes. Set "stream": true (or use the SDK's .stream() method) and Anthropic returns server-sent events with incremental content deltas, tool-use events, and a final message-stop event.

Why does my Flask app buffer the stream instead of sending it live? Usually a proxy, compression middleware, or a sync Gunicorn worker is buffering the response. Disable proxy_buffering, skip gzip on the streaming route, and use threaded or async workers.

Can I stream Claude responses without managing my own Anthropic API key? Yes — services like SubToAPI let you stream through a standard API key and dashboard without handling Anthropic account setup, billing, or rate-limit logic yourself.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →