> ## Documentation Index
> Fetch the complete documentation index at: https://docs.magicmarkets.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream many events

> Register many events on one socket, respect the cap, read fast, and recover from a silent drop.

Register many events on one socket. Do not open a socket per event.

## Register in bulk

```python theme={"dark"}
for ev in events[:20]:
    ws.send(json.dumps(["register_event", ev["sport"], ev["event_id"]]))
```

Each registration answers with an `offer` snapshot and an ok `response`. Registering an event with no prices returns an empty snapshot, not an error.

## The cap

The registered-event cap is counted across **all** your connections, not per socket. Exceeding it returns `customer_event_limit_exceeded` in-band. Unregister events you no longer need:

```python theme={"dark"}
ws.send(json.dumps(["unregister_event", sport, event_id]))
ws.send(json.dumps(["list_registered_events"]))     # what the server thinks you hold
```

The server also sends an `info` entry every few seconds. Its `registered_events` field is the count on this connection. Registering an event you already hold returns `already_registered`.

## Read fast

If you read too slowly, the server closes the connection with code 1008. A client that has stopped reading never receives that frame and sees only a raw TCP close. If you do heavy work per message, hand frames to a queue and process them off the read loop.

```python theme={"dark"}
import queue, threading

frames = queue.Queue(maxsize=10000)

def reader(ws):
    while True:
        frames.put(ws.recv())

def worker():
    while True:
        for entry in json.loads(frames.get())["data"]:
            handle(entry)

threading.Thread(target=reader, args=(ws,), daemon=True).start()
threading.Thread(target=worker, daemon=True).start()
```

## Reconnect and re-register

Registrations do not survive a reconnect. A silent drop looks like a clean EOF, so treat any exit from the read loop as a reconnect trigger.

```python theme={"dark"}
import time
from websockets.sync.client import connect

registered = set()      # (sport, event_id)
delay = 1
while True:
    try:
        with connect(url) as ws:
            delay = 1
            events = resync(ws)                  # collect until sync
            for sport, event_id in registered:
                ws.send(json.dumps(["register_event", sport, event_id]))
            pump(ws)
    except Exception as e:
        print("reconnecting after", e)
        time.sleep(delay)
        delay = min(delay * 2, 60)
```

Rebuild your book from the fresh snapshot after each reconnect. Do not assume your cached state survived.

## clear\_events

If the server loses its upstream feed it sends `clear_events`. Drop every event, offer and in-play state you hold. A fresh snapshot and `sync` follow when the feed recovers. If offers for an event do not resume after that `sync`, send `list_registered_events` and register the event again.

## Multiple processes

All your sockets share one registered-event cap. REST calls from every process share one rate-limit budget for the account. The stream itself has no message-rate limit. If you run several processes, give each its own API key so you can revoke one without stopping the rest, and coordinate registrations so they do not fight over the cap.

The runnable version of this pattern lives in the [Claude skill repository](/developers/tools/claude-skill) as `06-multi-stream.py`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.