mrkeyoor.com_
Thu 06 Aug 00:58 UTC
PyPIWeb Backendupdated 05 Aug 2026

websockets

websockets is the reference-quality Python implementation of the WebSocket protocol (RFC 6455 plus the RFC 7692 compression extension), built on asyncio. A server is one coroutine per connection: async for message in ws reads, await ws.send() writes, and the library handles the handshake, ping/pong keepalive, backpressure, and clean closes for you. It also ships a threading-based sync API for scripts, a trio integration, and a Sans-I/O protocol core that other servers embed; uvicorn uses it as a WebSocket backend.

Verdict

The best maintained WebSocket library in Python: 0 open issues and PRs at review time is not a fluke, it reflects an actively tended project. Use it for anything WebSocket-only, learn the new asyncio API, and distrust tutorials written before 2024.

API stability4/5The move from the legacy API to the new asyncio implementation was a real migration (import paths and handler signatures changed), but the new API has been stable across recent majors and deprecations are announced well ahead.
Docs5/5readthedocs has a tutorial, how-to guides for deployment behind common proxies, a full reference, and an FAQ; documentation is an explicit project priority and it shows.
Maintenance5/5Pushed July 2026, 17.0.1 released the same month, 0 open issues and PRs at review time, Tidelift-supported; the maintainer triages relentlessly.
Ecosystem4/5141.8M weekly downloads and 5.7K stars; uvicorn can use it as its WebSocket implementation, though there is little plugin ecosystem because the library is intentionally self-contained.

Use it if

  • You are building a dedicated WebSocket server or client on asyncio and want strict RFC compliance with backpressure handled correctly
  • You need long-lived clients that survive network blips: async for ws in connect(url) reconnects automatically with exponential backoff
  • You need a synchronous client for scripts, tests, or notebooks where starting an event loop is overkill (websockets.sync.client)
  • You fan out messages to many connections and want the broadcast() helper instead of gathering thousands of send() coroutines
Skip it if

Setup reality

pip install websockets pulls prebuilt wheels with a C speedups extension for Linux, macOS, and Windows; no build step, but Python 3.11+ is required. The real friction is the API split: new code must import from websockets.asyncio.server and websockets.asyncio.client, while most StackOverflow answers use the deprecated legacy spelling, and handler signatures differ between them (new handlers take just the connection, legacy ones took connection and path). Behind a proxy or load balancer you will also end up tuning ping_interval and ping_timeout to stop idle disconnects.

Patterns

Minimal asyncio echo serverecho-server

import asyncio
from websockets.asyncio.server import serve

async def echo(websocket):
    async for message in websocket:
        await websocket.send(message)

async def main():
    server = await serve(echo, "localhost", 8765)
    await server.serve_forever()

asyncio.run(main())

New-style handlers take a single connection argument. If a tutorial shows handler(websocket, path), it targets the deprecated legacy API.

Connect, send, and receive as an asyncio clientasync-client

from websockets.asyncio.client import connect

async def hello():
    async with connect("ws://localhost:8765") as websocket:
        await websocket.send("Hello")
        reply = await websocket.recv()
        print(reply)

The async context manager closes the connection cleanly on exit, including on exceptions. Use wss:// URLs to get TLS with default certificate verification.

Synchronous client without an event loopsync-client

from websockets.sync.client import connect

with connect("ws://localhost:8765") as websocket:
    websocket.send("Hello world!")
    message = websocket.recv()
    print(message)

The sync API runs on threads and is meant for scripts and tests; do not mix it into an asyncio application, use websockets.asyncio there.

Client that reconnects with backoffauto-reconnect-client

from websockets.asyncio.client import connect
from websockets.exceptions import ConnectionClosed

async def listen():
    async for websocket in connect("wss://example.com/feed"):
        try:
            async for message in websocket:
                handle(message)
        except ConnectionClosed:
            continue  # reconnects with exponential backoff

Iterating over connect() retries failed connections and reconnects after drops. Without the except clause, an abnormal close ends the loop instead of reconnecting.

Broadcast a message to every connected clientbroadcast-to-clients

from websockets.asyncio.server import broadcast, serve

CONNECTIONS = set()

async def handler(websocket):
    CONNECTIONS.add(websocket)
    try:
        await websocket.wait_closed()
    finally:
        CONNECTIONS.remove(websocket)

def notify_all(message):
    broadcast(CONNECTIONS, message)

broadcast() is a plain function that returns immediately and applies no backpressure; clients whose write buffer is full get skipped rather than slowing everyone down.

Answer a load balancer health check over HTTPhttp-health-check

from http import HTTPStatus
from websockets.asyncio.server import serve

def health_check(connection, request):
    if request.path == "/healthz":
        return connection.respond(HTTPStatus.OK, "OK\n")

async with serve(handler, "", 8765, process_request=health_check) as server:
    await server.serve_forever()

Returning a response from process_request short-circuits the WebSocket handshake; returning None lets the upgrade continue. This is the extent of HTTP support, by design.

Tell clean closes from failureshandle-disconnects

from websockets.exceptions import ConnectionClosedError

async def handler(websocket):
    try:
        async for message in websocket:
            await process(message)
    except ConnectionClosedError:
        log.warning("client dropped without closing handshake")

A clean close ends the async for loop with no exception; ConnectionClosedError only fires on abnormal closes. Code that catches broad ConnectionClosed treats normal disconnects as errors.

Exchange JSON messagessend-receive-json

import json

async def handler(websocket):
    async for raw in websocket:
        event = json.loads(raw)
        await websocket.send(json.dumps({"ack": event["id"]}))

recv() returns str for text frames and bytes for binary frames; json.loads handles both, but if you require one type, check it or set decode explicitly on recv().

Tune ping/pong keepalive for proxieskeepalive-tuning

from websockets.asyncio.server import serve

server = await serve(
    handler, "", 8765,
    ping_interval=20,  # seconds between pings
    ping_timeout=20,   # close if no pong within this
)

Defaults are 20/20. Idle-timeout proxies (nginx default 60s) kill quiet connections; keep ping_interval below the proxy timeout instead of disabling keepalive with None.

Protect a server with HTTP Basic Authbasic-auth-server

from websockets.asyncio.server import basic_auth, serve

server = await serve(
    handler, "", 8765,
    process_request=basic_auth(
        realm="feed",
        credentials=("bot", "s3cret"),
    ),
)

Browsers cannot set Authorization headers on WebSocket connections from JavaScript; Basic Auth works for server-to-server clients, while browser apps usually pass a token in the query string or first message.

Cap message size and queue lengthlimit-message-size

from websockets.asyncio.server import serve

server = await serve(
    handler, "", 8765,
    max_size=2**20,   # 1 MiB per message
    max_queue=32,     # buffered incoming messages
)

max_size protects against memory exhaustion from a single giant frame; oversized messages close the connection with code 1009. Defaults exist but review them before exposing a server publicly.

Alternatives

PackageRegistryPick it when
aiohttpPyPIYou need HTTP routes and WebSocket endpoints in the same asyncio server
wsprotoPyPIYou want a Sans-I/O WebSocket protocol core to embed in your own I/O layer
python-socketioPyPIYour clients speak Socket.IO (rooms, acks, reconnection semantics), not raw WebSocket