Python SDK — metered-realtime
The official async Python SDK for Metered Realtime Messaging. Speaks the same wire protocol as the JavaScript SDK and the raw WebSocket path, with framing, reconnection, ack correlation, presence diffing, perfect-negotiation WebRTC, and TURN credential injection done for you — so a Python peer is indistinguishable from a browser peer in the same room.
pip install "metered-realtime[webrtc]"
Built on asyncio, websockets, and aiortc. Requires Python 3.10+. Pub/sub-only installs (pip install metered-realtime) pull in just websockets; the webrtc extra adds the media stack.
What it's for
Python is where server-side and edge realtime lives — the places a browser can't reach:
- AI voice agents that join a room to run speech-to-text → LLM → text-to-speech
- IoT / telemetry bridges and edge gateways that relay camera/sensor data
- Recording bots, transcription workers, headless automation
- Server-side pub/sub — a backend that publishes to, and listens on, channels
A Python MeteredPeer shares a channel with browser and React Native peers transparently: it exchanges audio/video/data with them over real WebRTC and sees the same presence.
Pick your path
The SDK ships two public classes. Pick by what you're building:
| You're building… | Use | Why |
|---|---|---|
| AI agent in a call, media bridge, recording bot, anything exchanging audio/video/data with peers | MeteredPeer | Joins a channel, discovers peers via presence, manages each peer connection, fans your media out automatically, recovers ICE on network changes |
| Presence + chat, a roster, a coordination bus | MeteredPeer | The PeerJoined / PeerLeft events drive your logic; peer.send(data) broadcasts or peer.send_to(id, data) directs |
| IoT telemetry, pub/sub message bus, fan-out without media | SignallingClient | Pub/sub + directed messages without the WebRTC overhead. Smaller, simpler, multiple channels per connection |
Not sure? Start with MeteredPeer. Drop to SignallingClient later if you find you don't need the channel-+-peer model.
The event model — one mechanism, three surfaces
Every emitter (MeteredPeer, RemotePeer, DataChannel, SignallingClient) carries typed, frozen-dataclass events. Subscribe in whichever style fits:
from metered_realtime import MeteredPeer, PeerJoined, Data
async with MeteredPeer(api_key="pk_live_…") as peer:
@peer.on(PeerJoined) # 1. decorator, keyed by event TYPE
def _(ev: PeerJoined) -> None:
print("joined:", ev.peer.id)
await peer.join("room-42")
async for msg in peer.events(Data): # 2. async-iterator stream
if msg.data == "stop":
break
@peer.on(EventType) registers a handler (sync or async def — async handlers are scheduled and tracked for you). peer.events(EventType) returns a buffered async iterator. And async with MeteredPeer(...) as peer: closes the peer on exit.
What the SDK does for you
These are the things you'd write yourself against the raw protocol — the SDK handles them:
- Framing + ack correlation — every
subscribe/publish/sendawaits the server ack - Auto-reconnect with exponential backoff, jitter, and close-code-aware behaviour (terminal codes stop retrying; rate-limit codes slow-backoff)
- Token refresh — your
token_provideris re-invoked on every reconnect, so rotated JWTs + TURN creds land automatically - Inactivity watchdog — a half-dead socket is closed and reconnected instead of silently stalling
- Presence diff — raw
presenceframes become per-peerPeerJoined/PeerLeft - Perfect negotiation — SDP + ICE handled with a tie-breaker that prevents glare, adapted to the aiortc backend
- ICE recovery — automatic per-peer connection recovery on network change / TURN failover
- Identity-preserving reconnect — when the signalling socket drops, your
RemotePeerreferences survive; only the underlying connection is swapped (see Reconnect Best Practices)
Media: you provide the track
There is no getUserMedia server-side, so you supply the media — which is the SDK's strength for agents and bridges. Built-in helpers (media reference):
AudioSource— push synthesized/streamed audio (your TTS output) into the roomiter_frames— consume a received track frame-by-frame (feed your STT)from_file/from_rtsp/from_camera/from_microphone/screen_share— sources backed by a file, IP camera, or local device
Any aiortc-compatible track works too — pass it straight to add_track.
Where to start
| If you want to… | Go here |
|---|---|
| Get something running in 5 minutes | Getting Started |
| Wire up an AI voice agent | Guide: AI Agent Communication |
| Bridge an IoT / edge camera | Guide: IoT Telemetry |
| Handle reconnects properly (production-grade) | Guide: Reconnect Best Practices |
| Move from publishable keys to JWTs | Guide: Authentication |
| Look up a method / event | API Reference: MeteredPeer · SignallingClient |
Interop with other SDKs
The Python SDK speaks the protocol byte-for-byte. A Python MeteredPeer, a browser @metered-ca/realtime peer, a React Native peer, and a raw-WebSocket client can all be peers in the same channel — the canonical case being a Python AI agent in a call with a browser human.