mrkeyoor.com_
Wed 23 Sept 00:32 UTC
PyPIWeb Backendupdated 22 Sept 2026

python-socketio review

python-socketio implements both ends of the Socket.IO protocol in Python. Servers can use synchronous WSGI integrations or asyncio with ASGI, aiohttp, Tornado, and other documented hosts; clients get matching sync and async classes. Its event layer supplies acknowledgements, rooms, namespaces, reconnect handling, connection sessions, polling fallback, and WebSocket transport over python-engineio. Version 5.16.4 removes an incomplete binary packet when a client disconnects mid-transfer. This is Socket.IO protocol revision 5 over Engine.IO revision 4, so ordinary WebSocket clients cannot talk to it directly. Our import test succeeded, but the installed distribution did not advertise inline typing through py.typed.

Verdict

Choose python-socketio because a compatible Socket.IO peer or its event features are a real requirement. Walk away for plain WebSocket, durable delivery, package-owned typing, or horizontal scaling without a queue and transport plan.

We installed it

Lab card: what happened when we installed python-socketioScreenshot of python-socketio documentation
Install✓ · 0.3s6 packages on disk · 2 MB
Importimport socketio in 0.56s · pure Python · requires Python >=3.8
Known vulns0(pip-audit)

Answers from our run

Does python-socketio install cleanly?

Yes. In a fresh container with an empty cache, pip install python-socketio finished in 0.3s, leaving 6 packages and 2 MB on disk. pip-audit reported no known vulnerabilities.

What does python-socketio need to run?

Python >=3.8, and nothing compiled: it is pure Python. In our run import socketio succeeded in 0.56s.

python-socketio or websockets: which should you use?

websockets: Use it when both peers speak plain WebSocket and an asyncio protocol library is enough without Socket.IO packet semantics. Choose python-socketio because a compatible Socket.IO peer or its event features are a real requirement.

When should you not use python-socketio?

The other side speaks RFC WebSocket only. Socket.IO adds its own Engine.IO handshake, packet types, and event framing, so the protocols are not interchangeable

API stability4/5The 5.x line keeps the same Server, AsyncServer, Client, AsyncClient, event decorator, room, namespace, session, WSGIApp, and ASGIApp concepts. Its README publishes the protocol matrix instead of implying cross-generation compatibility. Stability has a hard boundary at the wire protocol: Python 5.x maps to Socket.IO protocol 5 and Engine.IO protocol 4, so the JavaScript client generation must be tested as part of an upgrade.
Docs5/5The official site returned HTTP 200 and splits server, client, middleware, deployment, API, and manager material into focused pages. It documents sync and async examples, authentication, CORS, sessions, rooms, acknowledgements, WSGI and ASGI wrapping, Gunicorn's long-polling limit, multi-worker queues, and each concurrency option. The explicit warning against new eventlet projects is the kind of negative guidance deployment docs often omit.
Maintenance5/5PyPI uploaded 5.16.4 on August 6, 2026, and GitHub shows another repository push on August 9. The repository is not archived and reports 15 open issues and pull requests together. The current patch fixes cleanup of incomplete binary packets on disconnect; nearby releases address Redis and RabbitMQ manager failures, resource allocation, free-threaded Python CI, and custom JSON handling in managers.
Ecosystem5/5The guide's latest weekly figure is 6,846,911 downloads, and GitHub reports 4,368 stars. The package speaks the same protocol generation as JavaScript Socket.IO 3.x and 4.x, supplies Python servers and clients, wraps WSGI and ASGI applications, and documents managers for common queue systems. Its integrations cover several deployment styles, though each extra server or broker introduces another compatibility surface.

Use it if

  • A browser, Node, mobile, or Python peer already uses Socket.IO 3.x or 4.x and the backend needs a compatible Python endpoint
  • Named events, acknowledgement callbacks, rooms, namespaces, reconnects, and HTTP polling fallback save application code you would otherwise own
  • You need a client as well as a server, with a choice between synchronous handlers and native asyncio handlers
  • A Redis, RabbitMQ, Kafka, or other documented client manager can coordinate emits across your worker processes
Skip it if

Setup reality

python-socketio 5.16.4 installed in 0.3 seconds in our fresh Python 3.12 container. The resulting environment had 6 packages and occupied 2 MB. The package declared 8 direct dependencies, required Python 3.8 or newer, and contained pure Python. import socketio completed in 0.56 seconds. pip-audit reported no known vulnerabilities. We found no py.typed marker, so type-checker behavior depends on external stubs or local annotations rather than package-owned inline types.

The base server install is only one layer. A synchronous client needs the client extra for requests and websocket-client; an asyncio client needs asyncio-client for aiohttp. Servers still need a WSGI or ASGI host, and message-queue managers bring their own Redis, RabbitMQ, Kafka, or other client packages. Match protocols before chasing proxy bugs: python-socketio 5.x pairs with JavaScript Socket.IO 3.x and 4.x, Socket.IO protocol 5, and Engine.IO protocol 4.

Choose Server plus WSGIApp for threaded WSGI, or AsyncServer plus ASGIApp for asyncio. Async event handlers must await emits, session calls, and room operations. The connect handler receives request data and the client's auth payload, which your application must verify before accepting the session. Same-origin checks are enabled. Set a narrow cors_allowed_origins list for a separate frontend instead of allowing every origin, and configure the reverse proxy to preserve WebSocket upgrades and long-lived connections.

Connection sessions vanish at disconnect, including an unexpected disconnect followed by reconnect. Keep login state and durable subscriptions elsewhere. Multiple server processes need a shared client manager so an emit in one process reaches clients owned by another. Gunicorn multi-worker deployments must also force WebSocket because its load balancing cannot keep Engine.IO long-poll requests on one worker. Acknowledgements can time out and only confirm the client handler replied; they do not make a business event durable.

Patterns

Mount an asyncio Socket.IO server create-asgi-app

import socketio

sio = socketio.AsyncServer(
    async_mode="asgi",
    cors_allowed_origins=["https://app.example.com"],
)
app = socketio.ASGIApp(sio)

Run app with an ASGI server. List the browser origins you own instead of using a wildcard origin policy.

Put Socket.IO in front of FastAPI wrap-existing-asgi-app

import socketio
from my_api import api

sio = socketio.AsyncServer(async_mode="asgi")
app = socketio.ASGIApp(sio, api)

ASGIApp handles the Socket.IO path and forwards other traffic to api. Confirm proxy routing sends both polling and WebSocket requests to this wrapper.

Reject an invalid connection authenticate-a-client

from socketio.exceptions import ConnectionRefusedError

@sio.event
async def connect(sid, environ, auth):
    token = (auth or {}).get("token")
    account = await verify_token(token)
    if account is None:
        raise ConnectionRefusedError("authentication failed")
    await sio.save_session(sid, {"account_id": account.id})

The auth object comes from the client and must be verified. The saved Socket.IO session disappears when this connection ends.

Return an acknowledgement payload handle-and-ack-event

@sio.event
async def rename_project(sid, data):
    session = await sio.get_session(sid)
    name = validate_name(data["name"])
    await rename_for_account(session["account_id"], data["project_id"], name)
    return {"accepted": True}

Returning data replies only when the sender requested an acknowledgement. Validate event payloads because python-socketio does not impose an application schema.

Address a single connection emit-to-one-client

await sio.emit(
    "report_ready",
    {"report_id": report_id},
    to=recipient_sid,
)

A sid identifies one current connection. Maintain your own account-to-connection mapping if a user can open several tabs or devices.

Call a client with a timeout wait-for-client-ack

from socketio.exceptions import TimeoutError

try:
    reply = await sio.call(
        "confirm_export",
        {"export_id": export_id},
        to=recipient_sid,
        timeout=10,
    )
except TimeoutError:
    reply = None

call targets one client and waits for its acknowledgement. A reply does not prove that later work was stored or completed.

Authorize a room before joining join-and-emit-room

@sio.event
async def join_project(sid, data):
    project_id = data["project_id"]
    session = await sio.get_session(sid)
    await require_project_access(session["account_id"], project_id)
    await sio.enter_room(sid, f"project:{project_id}")
    await sio.emit("member_joined", {"sid": sid}, room=f"project:{project_id}", skip_sid=sid)

A room name groups connections; it does not enforce permission. Perform authorization before enter_room and before sensitive room emits.

Mutate temporary session data update-connection-session

@sio.event
async def choose_workspace(sid, data):
    async with sio.session(sid) as session:
        session["workspace_id"] = data["workspace_id"]

The context manager saves changes for this connection and namespace. Reconnect creates a new session, so persistent preferences belong in a database.

Share clients across ASGI workers coordinate-with-redis

import os
import socketio

manager = socketio.AsyncRedisManager(os.environ["REDIS_URL"])
sio = socketio.AsyncServer(
    async_mode="asgi",
    client_manager=manager,
    transports=["websocket"],
)
app = socketio.ASGIApp(sio)

Install the Redis client separately and give every worker the same queue configuration. WebSocket-only mode removes long-polling fallback.

Publish from a non-server process emit-from-job-worker

import os
import socketio

manager = socketio.RedisManager(os.environ["REDIS_URL"], write_only=True)
manager.emit(
    "job_finished",
    {"job_id": job_id},
    room=f"account:{account_id}",
)

The external process and Socket.IO servers need the same manager channel. Pub/sub delivery is live fan-out, not a stored job result.

Use the synchronous Python client connect-sync-client

import socketio

client = socketio.Client(reconnection=True)
client.connect(
    "https://realtime.example.com",
    auth={"token": token},
)
client.emit("subscribe", {"project_id": "42"})
client.wait()

Install the client extra. Leaving transports unset permits Engine.IO to start with polling and upgrade when the server and proxy support WebSocket.

Handle events with AsyncClient connect-async-client

import socketio

client = socketio.AsyncClient()

@client.on("report_ready")
async def on_report_ready(data):
    await fetch_report(data["report_id"])

await client.connect("https://realtime.example.com", auth={"token": token})
await client.wait()

Install the asyncio-client extra. Keep blocking file or network calls out of the async event handler.

Alternatives

PackageRegistryPick it when
websocketsPyPIUse it when both peers speak plain WebSocket and an asyncio protocol library is enough without Socket.IO packet semantics
Flask-SocketIOPyPIUse it for a Flask application that wants framework-specific session, handler, and deployment integration around python-socketio
channelsPyPIUse it when Django, ASGI consumers, channel layers, and Django authentication should define the realtime architecture
aiohttpPyPIUse it when an aiohttp service only needs its native HTTP and WebSocket primitives and controls both ends of the wire format

More web backend guides

urllib3 · requests · ws · anyio · httpx · undici · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.