Server-Sent Events
ActiveServer-Sent Events (SSE) is a server-push technology over plain HTTP that lets a server stream data to a browser or client via a persistent connection. The client subscribes using the EventSource API; the server responds with Content-Type: text/event-stream and writes newline-delimited events indefinitely. SSE is unidirectional – server to client only. It became dominant in 2023–2026 as the streaming transport for every major LLM API (OpenAI, Anthropic, Google Gemini).
In one line
Server-Sent Events (SSE) is defined in the WHATWG HTML Living Standard §9.2. A client opens a persistent HTTP connection using the EventSource API; the server responds with Content-Type: text/event-stream and streams newline-delimited events indefinitely. Events carry optional id, event, data, and retry fields. The browser auto-reconnects using Last-Event-ID. SSE is unidirectional (server to client). It is the standard streaming transport for LLM APIs – OpenAI, Anthropic Claude, and Google Gemini all use text/event-stream for token streaming.
Quick Reference
| Field | Size | Description |
|---|---|---|
| Content-Type | text/event-stream | Server MUST respond with Content-Type: text/event-stream. Charset defaults to UTF-8. No other encoding is permitted per spec §9.2.3. |
| Event fields | 4 fields | data: (payload), event: (type name), id: (last event ID), retry: (reconnect delay ms). Fields are colon-separated. Blank line terminates an event. |
| Auto-reconnect | Last-Event-ID header | On disconnect, browser automatically reconnects and sends Last-Event-ID header with the id of the last received event. Server uses this to resume from the right position. |
| Retry field | milliseconds | retry: 3000 tells the client to wait 3 seconds before reconnecting. Server can update the retry interval dynamically by sending new retry: fields. |
| Stop reconnect | HTTP 204 | Server responds 204 No Content to tell the client to permanently stop reconnecting. Any other non-2xx response also stops reconnection. |
| Event types | Unlimited | Default event type is 'message'. Custom types set with event: field. Client listens with addEventListener('typename', handler). |
| HTTP/2 advantage | No connection limit | HTTP/1.1 browsers limit 6 connections per origin. HTTP/2 multiplexes all SSE streams on one connection – unlimited SSE subscriptions per origin. |
| CORS | withCredentials | Cross-origin SSE requires CORS. Set withCredentials: true in EventSourceInit to send cookies. Server must return Access-Control-Allow-Origin and Access-Control-Allow-Credentials: true. |
Key Characteristics
Unidirectional push
Server streams events to client. Client cannot send data back over the SSE connection. Use fetch/XHR for client-to-server communication alongside SSE.
Auto-reconnect
The browser EventSource API automatically reconnects on disconnect, exponential backoff by default, respecting the retry: field from the server. No client code needed.
LLM streaming
Every major LLM API streams tokens via SSE: OpenAI Chat Completions, Anthropic Claude, Google Gemini, Mistral. The pattern: data: {"delta": "token"} then data: [DONE].
Plain HTTP
SSE works over any HTTP connection including HTTP/1.1, HTTP/2, and HTTP/3. No protocol upgrade. No WebSocket handshake. Proxies and CDNs handle it without special configuration.
Message Format
# SSE request – client opens EventSource
GET /events HTTP/1.1
Host: api.example.com
Accept: text/event-stream
Cache-Control: no-cache
Authorization: Bearer <token>
# On reconnect, browser sends:
Last-Event-ID: 42
# JavaScript client
const source = new EventSource('/events', { withCredentials: true });
source.onmessage = (e) => console.log(e.data);
source.addEventListener('update', (e) => handleUpdate(JSON.parse(e.data)));
source.onerror = (e) => console.error('SSE error', e);# SSE response – text/event-stream format
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
X-Accel-Buffering: no
# Simple message event
data: Hello, world!
# Named event with JSON payload
event: update
data: {"userId": "usr_1", "action": "login"}
# Event with ID (enables Last-Event-ID resumption)
id: 42
event: price
data: {"symbol": "BTC", "price": 67432.10}
# Set reconnect delay to 5 seconds
retry: 5000
# Keep-alive comment (prevents proxy timeouts)
: ping
# LLM token streaming pattern
data: {"choices":[{"delta":{"content":"Hello"}}]}
data: {"choices":[{"delta":{"content":" world"}}]}
data: [DONE]