fastapi-ws-batch
Batch and coalesce outgoing FastAPI WebSocket messages, instead of sending one message per event.
The problem
A naive real-time app sends a WebSocket message the instant something happens. If a client emits 30 updates a second (cursor moves, game ticks, live state changes), your server sends 30 separate messages a second — each with its own framing overhead — for just that one client. With more clients, that multiplies fast.
This is a real problem hit while building a real-time multiplayer game: naive per-event sends were flooding the socket. Batching outgoing messages into small windows (~50ms) instead of sending them immediately cut network traffic by roughly 80% in production, with no perceptible added latency.
fastapi-ws-batch packages that fix so you don't have to build it yourself.
Install
pip install fastapi-ws-batch
Usage
Before — one send per event:
@app.websocket("/ws/{room_id}")
async def websocket_endpoint(websocket: WebSocket, room_id: str):
await websocket.accept()
while True:
data = await websocket.receive_json()
await websocket.send_json(data) # sent immediately, every time
After — batched automatically:
from fastapi_ws_batch import BatchedConnection
@app.websocket("/ws/{room_id}")
async def websocket_endpoint(websocket: WebSocket, room_id: str):
await websocket.accept()
conn = BatchedConnection(websocket, flush_interval=0.05)
await conn.start()
try:
while True:
data = await websocket.receive_json()
await conn.add_event(data) # queued, flushed every 50ms as one batch
except WebSocketDisconnect:
pass
finally:
await conn.stop()
The receiving client now gets a batch of events every ~50ms instead of a message per event — same information, far fewer round-trips.
Two ways to send
add_event(data) — every message is kept, in order. Use this when every message matters (chat messages, discrete game actions).
add_latest(key, data) — only the newest value per key survives until the next flush; older ones for the same key are silently dropped. Use this for state where only the current value matters (a player's live position, a cursor location):
await conn.add_latest(player_id, {"x": 10, "y": 20})
If a player's position updates 20 times within a 50ms window, only the final position is actually sent — not all 20.
Early flush on bursts
conn = BatchedConnection(websocket, flush_interval=0.05, batch_size=50)
If 50 messages queue up before the next scheduled flush, it flushes immediately instead of waiting out the full interval — so a sudden burst doesn't sit buffered any longer than necessary.
How it works
add_event()/add_latest()queue messages instead of sending them right away.- A background task flushes the queue every
flush_intervalseconds, sending everything queued as one batch viasend_json. - If a failed send happens (e.g. the socket drops mid-send), the batch is restored to the queue instead of being lost, and retried on the next tick — unless something newer for the same key has already replaced it.
conn.stop()flushes anything left and stops the background task cleanly — always call it when the connection ends (e.g. in afinallyblock), so nothing queued is lost.
API
BatchedConnection(
websocket, # any object with an async send_json(dict) method
flush_interval, # seconds between automatic flushes
batch_size=None, # optional: force an early flush once this many messages queue up
)
await conn.start() # begin the background flush loop
await conn.add_event(data) # queue an in-order message
await conn.add_latest(key, data) # queue a message, keeping only the latest per key
await conn.stop() # flush remaining messages and stop cleanly
Why this exists
Existing FastAPI WebSocket libraries handle connection lifecycle and room/broadcast management well, but none of them batch or coalesce outgoing messages — every example sends immediately, per event. This library is the missing piece: put it in front of your existing send logic to cut traffic without changing your app's structure.
License
MIT
Metadata
Release files for fastapi-ws-batch 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fastapi_ws_batch-0.1.0.tar.gz | 5.7 kB | Without provenance | Details for fastapi_ws_batch-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fastapi_ws_batch-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for fastapi_ws_batch-0.1.0-py3-none-any.whl |
Total release size: 10.9 kB
Release files / fastapi_ws_batch-0.1.0.tar.gz
| Download URL | fastapi_ws_batch-0.1.0.tar.gz |
|---|---|
| Size | 5.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5767f46fd8b87ba6d05d9ecc247e9b5bd5ce8d3ccb62979fe0c3bd758ec2c0d7
|
|
BLAKE2b-256 checksum How to use checksums |
647fb4b50351af57892a6d7d574e2bf5e3a71663d82c1ff16e3987cf72e92f2a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Release files / fastapi_ws_batch-0.1.0-py3-none-any.whl
| Download URL | fastapi_ws_batch-0.1.0-py3-none-any.whl |
|---|---|
| Size | 5.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
66b5ed2d4517a835f3ff735a9fb860185752361c27c56d2c296542771590cb66
|
|
BLAKE2b-256 checksum How to use checksums |
77d570a9643c1cc0a3e32eb553c9b7b6442d9de0255c2ca29078bde063ce30e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Security for fastapi-ws-batch 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.