Claude API Integration with Python Flask: A Guide
Integrating the Claude API into a Flask application means setting up an HTTP client that sends requests to a messages endpoint, handling the JSON response (or a stream of events), and exposing that logic through your own Flask routes. This article walks through a working setup: installing dependencies, writing the request logic, handling streaming with server-sent events, and dealing with errors so your app doesn't crash on a bad API response.
If you're building a chatbot, internal tool, or any product feature that needs an LLM behind a Flask backend, the pattern is the same regardless of which Claude access method you use: you send a POST request with a model name, messages array, and max_tokens, then either read the full JSON response or consume a stream. The rest of this guide shows both approaches.
Setting Up Your Flask Project
Start with a minimal project structure:
mkdir claude-flask-app && cd claude-flask-app
python -m venv venv
source venv/bin/activate
pip install flask requests python-dotenv
Store your API key in a .env file rather than hardcoding it:
CLAUDE_API_KEY=your_api_key_here
Load it in your Flask app with python-dotenv, and never commit .env to version control.
A Basic Route That Calls the Claude API
Here's a simple /chat endpoint that accepts a user message and returns Claude's reply:
import os
import requests
from flask import Flask, request, jsonify
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
API_KEY = os.getenv("CLAUDE_API_KEY")
API_URL = "https://api.example.com/v1/messages" # replace with your provider's endpoint
@app.route("/chat", methods=["POST"])
def chat():
data = request.get_json()
user_message = data.get("message", "")
payload = {
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [{"role": "user", "content": user_message}]
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(API_URL, json=payload, headers=headers, timeout=30)
if response.status_code != 200:
return jsonify({"error": response.text}), response.status_code
result = response.json()
return jsonify(result)
if __name__ == "__main__":
app.run(debug=True, port=5000)
This pattern works whether you call Anthropic's API directly or route through a proxy like SubToAPI. If you're using SubToAPI, the only changes are the endpoint (https://api.subtoapi.app/v1/messages) and the key format (sub_live_...). The request and response shape follow the same conventions described in the messages docs, so existing Flask code needs almost no rewriting.
Handling Streaming Responses in Flask
For chat interfaces, streaming tokens as they arrive gives a much better user experience than waiting for the full response. Flask supports this with a generator function and the stream_with_context helper:
from flask import Response, stream_with_context
import json
@app.route("/chat/stream", methods=["POST"])
def chat_stream():
data = request.get_json()
user_message = data.get("message", "")
payload = {
"model": "claude-sonnet-4",
"max_tokens": 1024,
"stream": True,
"messages": [{"role": "user", "content": user_message}]
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
def generate():
with requests.post(API_URL, json=payload, headers=headers, stream=True) as r:
for line in r.iter_lines():
if line:
yield f"{line.decode('utf-8')}\n\n"
return Response(stream_with_context(generate()), mimetype="text/event-stream")
On the frontend, an EventSource or a fetch call reading the response body chunk by chunk will render tokens as they arrive. The exact event format depends on your API provider — see the streaming docs for the event structure if you're using SubToAPI, since it follows the same server-sent events pattern used across most Claude-compatible APIs.
Adding Tool Use to Your Flask Endpoint
If your Flask app needs Claude to call functions — looking up a database record, hitting an internal API, or running a calculation — you define tools in the request payload and handle the tool_use stop reason in your route:
payload = {
"model": "claude-sonnet-4",
"max_tokens": 1024,
"tools": [
{
"name": "get_order_status",
"description": "Look up an order status by order ID",
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"]
}
}
],
"messages": [{"role": "user", "content": user_message}]
}
When the response comes back with stop_reason: "tool_use", your Flask code executes the requested function, then sends a follow-up request with the tool result appended to the messages array. Full request/response shapes for this flow are covered in the tools docs.
Why Use a Proxy Instead of Calling Anthropic Directly
Some teams call the Claude API directly with a personal or organization account. That works for prototypes, but it gets messy once you need per-application keys, usage tracking across services, or to give teammates access without sharing one shared secret. SubToAPI sits between your Flask app and your existing Claude access, giving you sub_live_... keys scoped per application, request logs, and team seats — without changing the request format your Flask routes already use. Setup takes a few minutes: create a key in the dashboard, drop it into your .env file, and point API_URL at https://api.subtoapi.app/v1/messages. Check the quickstart guide for the exact steps, or start a free trial at signup.
Error Handling Basics
At minimum, your Flask routes should handle:
- Timeouts — wrap requests in a
try/except requests.exceptions.Timeoutblock and return a 504 to the client. - Rate limits (429) — check
response.status_code == 429and implement a retry with backoff, or surface a clear message to the frontend. - Invalid input — validate
request.get_json()before building the payload to avoid sending malformed requests.
Wrapping the API call in a small helper function keeps this logic out of your route handlers and makes it reusable across multiple endpoints.
Questions
Do I need a special Flask extension to use the Claude API? No. The requests library and Flask's built-in Response and stream_with_context are enough to handle both standard and streaming requests — no dedicated SDK is required.
Can I stream Claude responses to a browser from Flask? Yes. Use a generator function that yields chunks from the API response and return it as a text/event-stream Response object; the frontend consumes it with EventSource or a streaming fetch call.
What's the difference between calling Anthropic directly and using SubToAPI in a Flask app? The request and response format stay the same — only the base URL and key change. SubToAPI adds per-application API keys, usage visibility, and team seats on top of your existing Claude access, detailed in the pricing page.