Claude API WebSocket Integration Tutorial (Bridge Pattern)
Does Claude's API support WebSockets?
No — not directly. Anthropic's Claude API, and services built on top of it like SubToAPI, expose Claude over plain HTTPS with Server-Sent Events (SSE) for streaming. There is no wss:// endpoint you can connect to and no persistent socket protocol on the model side. If you've searched for a Claude API WebSocket integration tutorial hoping to find a native socket, that endpoint doesn't exist today.
What you almost certainly want instead — and what this tutorial covers — is how to put a WebSocket layer in front of Claude's HTTP streaming API, so your browser or mobile client gets real-time token-by-token updates over a socket connection while your backend talks to Claude over standard HTTPS streaming. This "bridge" pattern is how most production chat UIs, voice assistants, and collaborative tools wire Claude into a real-time frontend.
Why you need a bridge, not a direct connection
Browsers can't safely hold your Claude API key, and Claude's streaming responses are chunked HTTP, not a socket protocol. So the architecture looks like this:
Browser (WebSocket client)
⇅
Your server (WebSocket server + HTTP client to Claude/SubToAPI)
⇅
Claude API (HTTPS + SSE streaming)
Your server does two jobs: it opens a streaming HTTPS request to Claude (or to SubToAPI's /v1/messages endpoint), and it re-emits each chunk it receives to one or more connected WebSocket clients. This keeps your API key server-side, lets you fan out one Claude response to multiple viewers (useful for live demos or shared sessions), and gives you a single place to apply rate limiting, logging, or auth.
Step 1: Set up the server
Use Node.js with the ws package for the WebSocket server. We'll call out to SubToAPI, which gives you a standard sub_live_... API key and the same streaming semantics as Anthropic's API, documented at /docs/streaming.
npm install ws node-fetch
// server.js
import { WebSocketServer } from "ws";
import fetch from "node-fetch";
const wss = new WebSocketServer({ port: 8080 });
const SUBTOAPI_KEY = process.env.SUBTOAPI_KEY;
wss.on("connection", (socket) => {
socket.on("message", async (raw) => {
const { prompt } = JSON.parse(raw.toString());
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${SUBTOAPI_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 1024,
stream: true,
messages: [{ role: "user", content: prompt }],
}),
});
for await (const chunk of response.body) {
const text = chunk.toString("utf8");
// Forward each SSE event line to the websocket client
for (const line of text.split("\n")) {
if (line.startsWith("data:")) {
socket.send(line.replace("data:", "").trim());
}
}
}
socket.send(JSON.stringify({ type: "done" }));
});
});
This server opens one WebSocket port. Each incoming message triggers a fresh streaming call to SubToAPI, and every SSE data: line is relayed to the client as soon as it arrives — so the client sees tokens appear in near real time, same as it would with a native streaming API, but over a socket connection.
Step 2: Connect from the browser
const socket = new WebSocket("ws://localhost:8080");
socket.onopen = () => {
socket.send(JSON.stringify({ prompt: "Explain event loops in one paragraph." }));
};
let output = "";
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === "done") {
console.log("Stream finished:", output);
return;
}
if (data.delta?.text) {
output += data.delta.text;
document.getElementById("response").textContent = output;
}
};
This gives you a chat UI that updates character-by-character as Claude generates a response, driven entirely over a WebSocket, with the actual model call happening server-side.
Step 3: Handle multiple clients and sessions
For anything beyond a demo, track sessions by connection and support cancellation:
wss.on("connection", (socket) => {
let abortController = null;
socket.on("message", async (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.type === "cancel" && abortController) {
abortController.abort();
return;
}
abortController = new AbortController();
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 1024,
stream: true,
messages: msg.messages,
}),
signal: abortController.signal,
});
// ...stream forwarding as above
});
socket.on("close", () => abortController?.abort());
});
Aborting the underlying HTTPS request when a client disconnects or cancels avoids wasting tokens on a response nobody is reading — worth doing since you're billed for generated tokens either way.
Step 4: Add tool use over the same bridge
If your app uses tool calling, the pattern doesn't change — you just forward tool-use blocks the same way you forward text deltas, and handle the tool execution loop on your server before sending the follow-up request. See /docs/tools for the request/response shape, and /docs/messages for the full Messages API reference. The WebSocket layer stays a thin relay regardless of whether the underlying conversation is plain text or multi-turn tool use.
When this pattern is worth it
Build a WebSocket bridge when you need live multi-user viewing of a single response, a persistent connection for a chat widget that sends many messages per session, or integration with an existing WebSocket-based app (game server, collaborative editor, trading dashboard). If you're building a simple request/response chat feature, plain HTTP streaming without a WebSocket layer is simpler and has fewer moving parts — don't add the bridge unless you actually need socket semantics.
Either way, you still need a Claude-compatible backend with a real API key, streaming support, and usage visibility. SubToAPI gives you that as a drop-in HTTPS endpoint — sign up at /signup and follow /docs/quickstart to get a key, then wire it into the bridge server above.
Questions
Does Anthropic offer a native WebSocket API for Claude? No. Claude is accessed over HTTPS with optional SSE streaming. Any WebSocket interface you see in a chat app is a bridge built by the developer, not a feature of the underlying API.
Can I stream Claude responses without building a WebSocket server? Yes — plain HTTP streaming (SSE) works directly in most frontend frameworks and is simpler to set up. Only add a WebSocket layer if you need bidirectional push, multi-client fan-out, or integration with an existing socket-based system.
Will this bridge pattern work with any Claude-compatible provider? Yes, as long as the provider supports streaming responses over HTTPS, which SubToAPI and Anthropic's direct API both do. The bridge code only needs to parse SSE chunks and relay them — it doesn't depend on a specific vendor.