News & Updates

How to Create a Python WebSocket API: Practical Example

By Mitchell Cross 10 min read 4175 views

How to Create a Python WebSocket API: Practical Example

When you need real‑time communication between a client and a server, WebSocket often feels like the magic ingredient that turns a static app into an interactive experience. Python, with its rich ecosystem, makes wiring up a WebSocket API surprisingly approachable. Below is a step‑by‑step walk‑through that shows not just the code, but the thinking behind each part.

Why Choose WebSocket Over Traditional HTTP?

HTTP follows a request‑response pattern: the client asks, the server answers, and the connection usually closes. That works fine for loading pages, but it falls short when you want live updates—think chat messages, stock tickers, or multiplayer game states.

WebSocket keeps the TCP connection open, allowing both sides to push data whenever they need to. The result is lower latency and less overhead, because you avoid the constant round‑trip of new HTTP requests.

Preparing the Environment

First, make sure you have Python 3.8+ installed. The example relies on FastAPI for the web framework and uvicorn as the ASGI server. Both are lightweight and well‑documented.

  • pip install fastapi uvicorn websockets

Optional, but recommended: set up a virtual environment to keep dependencies tidy.

Setting Up the Basic FastAPI App

Below is the skeleton of a FastAPI application that will host our WebSocket endpoint.

from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.get("/")

async def root():

return {"message": "Welcome to the WebSocket demo"}

This route simply confirms that the server is running. The real work starts with the WebSocket route.

Creating the WebSocket Endpoint

We’ll add a new path /ws that accepts WebSocket connections. The function receives a WebSocket object, which we must first accept before exchanging messages.

@app.websocket("/ws")

async def websocket_endpoint(websocket: WebSocket):

await websocket.accept()

try:

while True:

data = await websocket.receive_text()

# Echo the received message back to the client

await websocket.send_text(f"Server received: {data}")

except Exception as e:

await websocket.close()

A few subtle points:

  • Accepting the connection is mandatory; otherwise the client will time out.
  • The while True loop keeps the channel open, listening for incoming text frames.
  • Any exception (like a client disconnect) gracefully closes the socket.

Adding Simple Business Logic

You probably don’t want a raw echo service. Let’s pretend we’re building a tiny chat room. A shared list will hold all active connections, and each incoming message gets broadcast to every participant.

connected_clients = set()

@app.websocket("/chat")

async def chat_endpoint(websocket: WebSocket):

await websocket.accept()

connected_clients.add(websocket)

try:

while True:

data = await websocket.receive_text()

for client in connected_clients:

if client != websocket:

await client.send_text(data)

finally:

connected_clients.remove(websocket)

await websocket.close()

Notice the finally block—no matter how the loop exits, we clean up the client set to avoid memory leaks.

Testing the API Locally

Run the server with:

uvicorn myapp:app --reload

Open two browser tabs pointing to a simple HTML page that establishes a WebSocket connection to ws://localhost:8000/chat. When you type a message in one tab, it appears in the other—proof that the broadcast works.

Deploying to Production

While uvicorn with --reload is perfect for development, a production setup usually pairs it with a process manager like Gunicorn and an ASGI worker class.

gunicorn -k uvicorn.workers.UvicornWorker myapp:app

Don’t forget to secure the endpoint with TLS (HTTPS/WSS). Most cloud providers let you terminate TLS at a load balancer, then forward plain WebSocket traffic to your app.

Common Pitfalls and How to Avoid Them

  • Blocking code: Avoid long‑running synchronous functions inside the WebSocket loop; they will block all connections. Use asyncio utilities or run heavy tasks in a background worker.
  • Uncaught exceptions: An unexpected error will break the loop and drop the client. Wrap receive/send calls in try/except blocks, and consider logging the error for later debugging.
  • Resource leaks: Forgetting to remove a client from the shared set can lead to stale references and memory growth over time.

Extending the Example

From here you can branch out in many directions:

  • Integrate authentication tokens and reject unauthorized connections.
  • Persist messages in a database so new participants see recent chat history.
  • Use JSON payloads instead of plain text to carry richer data structures.
  • Scale out with a message broker like Redis Pub/Sub when you need dozens of server instances.

The core pattern—accept, loop, broadcast, clean up—remains the same, no matter how sophisticated the surrounding features become.

Python Websocket Server _ Python Websockets.Serve – ITOC
Difference Between Api And Socket at Ryan Boland blog
FastAPI and WebSockets — Building real-time features and notifications ...
AWS WebSocket API (Real-time chat application using python) - YouTube

Written by Mitchell Cross

Mitchell Cross is a Chief Correspondent with over a decade of experience covering breaking trends, in-depth analysis, and exclusive insights.