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

# Python

> A complete, reconnecting WebSocket client using the websockets library.

Install the `websockets` package and run the script below. It connects, reads the `ack` frame, subscribes to two stations, and prints every observation as it arrives.

```bash theme={null}
pip install websockets
```

```python theme={null}
import asyncio, json
from urllib.parse import urlencode

import websockets

KEY = "mts_live_YOUR_KEY"
CHANNELS = ["metar.obs.eglc", "metar.obs.rksi"]

async def stream():
    url = "wss://stream.metar.ws/v1/ws?" + urlencode({"key": KEY})
    async with websockets.connect(url) as ws:
        ack = json.loads(await ws.recv())
        print("connected:", ack["plan"], ack["limits"])

        await ws.send(json.dumps({"action": "subscribe", "channels": CHANNELS}))

        async for frame in ws:
            msg = json.loads(frame)
            if msg["type"] == "publication":
                d = msg["data"]
                print(f'{d["station"]}: {d["temp_c"]}°C at {d["report_time"]}')
            elif msg["type"] == "error":
                print("error:", msg["code"], msg.get("channel", ""))

async def main():
    while True:  # reconnect loop; deploys and blips are normal
        try:
            await stream()
        except Exception as e:
            print(f"disconnected: {e} - reconnecting in 3s")
            await asyncio.sleep(3)

asyncio.run(main())
```

<Note>
  On the Sandbox plan, subscribe to `sandbox.demo` instead and trigger a message with **Send test alert** on the Billing page in the portal.
</Note>

<CardGroup cols={3}>
  <Card title="Node.js" icon="node-js" href="/examples/nodejs">
    The same client using ws
  </Card>

  <Card title="Go" icon="code" href="/examples/go">
    The same client using gorilla/websocket
  </Card>

  <Card title="Browser" icon="window" href="/examples/browser">
    A minimal client for internal dashboards
  </Card>
</CardGroup>
