Working Django code to integrate the Claude API in 2026. View-based chat handler, streaming responses, session conversation history, and Django REST framework API endpoint patterns.
Django is Python's most widely deployed full-stack web framework. This guide covers every pattern you need to integrate the Claude API into a Django project — from a minimal function-based view to streaming SSE and conversation history.
pip install anthropic django
# myapp/views.py
import json
import anthropic
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
# Module-level client — one instance per worker, not per request
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
@csrf_exempt
@require_POST
def chat(request):
data = json.loads(request.body)
user_message = data.get("message", "")
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": user_message}]
)
return JsonResponse({"reply": response.content[0].text})
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("chat/", views.chat, name="chat"),
]
# myapp/views.py
@csrf_exempt
@require_POST
def chat_with_history(request):
data = json.loads(request.body)
user_message = data.get("message", "")
# Load history from session (persists across requests per user)
history = request.session.get("chat_history", [])
history.append({"role": "user", "content": user_message})
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="You are a helpful assistant.",
messages=history,
)
assistant_reply = response.content[0].text
history.append({"role": "assistant", "content": assistant_reply})
# Keep last 20 turns to stay within context limits
request.session["chat_history"] = history[-20:]
return JsonResponse({"reply": assistant_reply})
# Clear conversation
@csrf_exempt
@require_POST
def clear_history(request):
request.session.pop("chat_history", None)
return JsonResponse({"status": "cleared"})
Requires SESSION_ENGINE in settings.py (Django's database-backed sessions are the default).
# myapp/views.py
from django.http import StreamingHttpResponse
def stream_chat(request):
user_message = request.GET.get("message", "Hello")
def event_stream():
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": user_message}],
) as stream:
for text in stream.text_stream:
# SSE format: "data:
"
yield f"data: {json.dumps({'text': text})}
"
yield "data: [DONE]
"
response = StreamingHttpResponse(
event_stream(),
content_type="text/event-stream"
)
response["Cache-Control"] = "no-cache"
response["X-Accel-Buffering"] = "no" # disable Nginx buffering
return response
<!-- Frontend JavaScript -->
const evtSource = new EventSource(`/chat/stream/?message=${encodeURIComponent(msg)}`);
evtSource.onmessage = (event) => {
if (event.data === "[DONE]") { evtSource.close(); return; }
const { text } = JSON.parse(event.data);
document.getElementById("output").textContent += text;
};
# pip install djangorestframework
# settings.py: INSTALLED_APPS += ["rest_framework"]
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class ClaudeChatView(APIView):
def post(self, request):
user_message = request.data.get("message")
if not user_message:
return Response(
{"error": "message is required"},
status=status.HTTP_400_BAD_REQUEST
)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": user_message}],
)
return Response({"reply": response.content[0].text})
# myapp/urls.py
from .views import ClaudeChatView
urlpatterns += [path("api/chat/", ClaudeChatView.as_view())]
# myapp/management/commands/batch_claude.py
from django.core.management.base import BaseCommand
from myapp.models import Article # your model
class Command(BaseCommand):
help = "Summarize all unsummarized articles with Claude"
def handle(self, *args, **options):
articles = Article.objects.filter(summary="")
for article in articles:
response = client.messages.create(
model="claude-haiku-4-5-20251001", # cheapest model for bulk
max_tokens=256,
messages=[{
"role": "user",
"content": f"Summarize in 2 sentences: {article.body[:4000]}"
}]
)
article.summary = response.content[0].text
article.save()
self.stdout.write(f"Summarized: {article.pk}")
| Framework | Best for | Streaming support | Overhead |
|---|---|---|---|
| Django | Full web apps with ORM, auth, admin | StreamingHttpResponse + SSE | Highest — batteries included |
| Flask | Lightweight APIs and microservices | Response(stream_with_context(...)) | Minimal — bring your own ORM |
| FastAPI | Async-first APIs, OpenAPI docs auto-gen | StreamingResponse + async generators | Medium — async by default |
Use the Claude API Cost Calculator to estimate monthly cost before deploying. For the FastAPI equivalent, see the FastAPI guide. For Flask, see the Flask guide.