01 — REALTIME MESSAGING

Realtime Messaging API

Managed WebSocket pub/sub, presence and direct messaging, with SDKs for JavaScript, Python and Flutter.

It’s the messaging layer. You build the interface and Metered runs the connections.

Free on every account, no credit card required

A Canadian company building real-time software since 2013. 25,000+ apps have used Metered TURN.

import { SignallingClient } from "@metered-ca/realtime";

const client = new SignallingClient({
  apiKey: "pk_live_…",
});

await client.connect();
await client.subscribe("alerts/critical");

// Messages from every other subscriber
client.on("message", ({ from, data }) => {
  console.log(from, data);
});

await client.publish("alerts/critical", {
  sensor: "T-12",
  value: 92,
});

THE TOUR

See it in 75 seconds.

75 seconds · with music · captions available Demo app with illustrative video

02 — HOW IT WORKS

Pub/sub messaging over managed WebSockets

Clients hold one WebSocket to wss://rms.metered.ca/v1 and exchange single JSON frames. Your backend drives the same service over a REST control plane, without keeping a socket open.

CHANNELS

Publish and subscribe

A publish reaches every other subscriber of the channel. Keys are scoped to channel patterns such as app_42/*.

DIRECT

Message one peer

Send to a single peer ID with no channel in between: the path WebRTC offers, answers and ICE candidates take.

PRESENCE

Know who’s online

A full roster when you subscribe, then join and leave events, each peer carrying the identity your backend signed.

REST

Control plane

Mint tokens, publish from your server, list a channel’s peers, disconnect a peer and read usage.

wire protocol, no SDK
// connect
wss://rms.metered.ca/v1?token=<jwt>

// ← welcome, the first frame after auth
{ "type": "welcome", "peerId": "peer-3",
  "maxMessageSize": 65536, … }

// → join a channel
{ "type": "subscribe", "channel": "room-42",
  "requestId": "s1" }

// ← acknowledged, then the full roster
{ "type": "ack", "requestId": "s1" }
{ "type": "presence", "channel": "room-42",
  "joined": [{ "peerId": "peer-7" }], "left": [] }

// → broadcast to everyone else on the channel
{ "type": "publish", "channel": "room-42",
  "data": { "text": "hi" } }

// → or message one peer directly
{ "type": "send", "to": "peer-7",
  "data": { "kind": "offer", "sdp": "…" } }

03 — PRESENCE

User presence and online status, built in

The first presence event after you subscribe is the full roster, even when the channel is empty. After that you get joins and leaves. Every peer carries the identity your backend put in its token, such as a user ID, display name and avatar, so an online indicator needs no extra lookup.

  • Identity travels on presence and direct messages, and on channel messages when you opt in
  • Your server can list who is in a channel over REST
Presence and chat guide
online.js
// Your server signed each peer's identity into
// its token: peerMetadata { userId, username }

const online = new Map();

client.on("presence", ({ joined, left }) => {
  // First event: the full roster. Then changes.
  for (const p of joined) {
    online.set(p.peerId, p.metadata);
  }
  for (const p of left) online.delete(p.peerId);

  renderOnlineList(
    [...online.values()].map((m) => m?.username),
  );
});

await client.subscribe("team-lobby");

04 — USE CASES

One real-time messaging platform, many jobs

The same protocol carries every one of these.

WebRTC signalling

Exchange offers, answers and ICE candidates, with Metered TURN credentials delivered on connect.

How it works with TURN

AI agent communication

A planner publishes subtasks to a workflow channel and agents reply directly. Long tool calls stream back as a sequence of direct messages. A Python voice agent can also join a call and talk with your users.

Presence and chat

One channel per room, author identity on messages when you opt in, and moderation from your server: kick a peer or post a system message.

Presence and chat guide

IoT telemetry and device control

Each device gets a narrowly scoped token, publishes telemetry on its own channel and listens for commands your backend sends over REST.

Live cursors and collaboration

High-frequency cursor and state updates on a per-document channel, with identity cached from presence so each message stays small. For peer-to-peer traffic, open a WebRTC data channel.

Peer-to-peer data channel guide

Realtime notifications

Publish to a user’s or team’s channel straight from your backend with one REST call. No WebSocket on the server side.

REST control plane

05 — WEBRTC SIGNALLING

WebRTC signalling that delivers your TURN credentials

When your app has an active Metered TURN service, on any tier including free, each connection’s welcome message carries ready-to-use TURN credentials by default. Pass them straight to RTCPeerConnection: no separate credential fetch, no backend call.

  • MeteredPeer handles perfect negotiation, reconnects and recovers failed peer links, and lets you label camera and screen-share streams
  • Runs on the browser’s WebRTC, react-native-webrtc, flutter_webrtc and aiortc
  • No backend at all for prototypes: a browser-safe publishable key works on a static site, as long as the key has Send ticked
  • Bring your own TURN servers instead by putting them in the token
call.js
import { MeteredPeer } from "@metered-ca/realtime";

// Metered TURN credentials arrive with the connection
const peer = new MeteredPeer({
  tokenProvider: () => fetchJwt(),
});

peer.on("peer-joined", ({ peer: remote }) => {
  remote.on("stream-added", ({ stream, metadata }) => {
    if (metadata?.role === "screen") showScreen(stream);
    else showCamera(stream);
  });
});
peer.on("peer-left", ({ peer: remote }) => {
  removeTiles(remote.id);
});

// Reconnects are automatic; show a banner meanwhile
peer.on("state-change", ({ to }) => {
  setBanner(to === "reconnecting");
});

const cam = await navigator.mediaDevices
  .getUserMedia({ video: true, audio: true });
peer.addStream(cam, { role: "camera" });
await peer.join("room-42");

06 — METERED TURN

Pair it with Metered TURN

Realtime Messaging establishes the session. Metered TURN carries the media, relaying WebRTC traffic through NATs and firewalls. Use them together, or each on its own.

Explore Metered TURN
  • Global TURN network with automatic routing to the nearest server
  • TURN on ports 80 and 443, with TLS on 443 for locked-down networks
  • Set credentials to expire automatically, or revoke them anytime via API

07 — SDKS

SDKs for JavaScript, Python and Flutter

All three speak the same wire protocol, so a browser, a Python agent and a Flutter app can share one channel. Each has the same two classes, MeteredPeer for calls and SignallingClient for pub/sub, reconnects for you, and is MIT-licensed.

JAVASCRIPT · TYPESCRIPT

Browser, Node.js and React Native. A small bundle with WebRTC included and no runtime dependencies, in ESM, CommonJS and UMD builds with TypeScript types.

npm install @metered-ca/realtime

PYTHON

Async Python 3.10+, with WebRTC on aiortc. Built for AI voice agents, IoT bridges, recording bots and server-side pub/sub.

pip install "metered-realtime[webrtc]"

FLUTTER · DART

Android, iOS, web and desktop, with WebRTC from flutter_webrtc and events as Dart streams.

flutter pub add metered_realtime

RAW WEBSOCKET

Any other language, from C and C++ on devices to Go, Rust, Swift and Kotlin: connect to the same WebSocket protocol directly. It’s single JSON messages, and those clients share channels with SDK peers.

08 — SECURITY AND CONTROL

WebSocket authentication with scoped tokens

Secret key Server-side only. Signs JWTs and authenticates REST calls
Publishable key Browser-safe, with a fixed scope set in the dashboard
Scope Channel patterns plus actions: publish, subscribe, presence, send
Token lifetime 24 hours at most
Revocation Live connections on a revoked key close within seconds
Kick a peer One REST call closes every connection for that peer
Authentication guide
token.js
// Server: mint a token for one user
const url = "https://rms.metered.ca/v1/tokens";
const res = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${REALTIME_SK}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    peerId: userId,
    channels: [`call-${callId}`],
    permissions: [
      "publish", "subscribe", "presence", "send",
    ],
    expiresInSec: 3600,
    peerMetadata: { userId, username },
  }),
});
const { token } = await res.json();

// Browser: connect as that user
const client = new SignallingClient({
  tokenProvider: fetchToken,
});

09 — PRICING

Realtime Messaging pricing

Every account can use Realtime Messaging on the free tier, with no TURN plan needed. Paid Metered TURN plans include a larger allowance in their price, with no separate subscription.

REALTIME MESSAGING · INCLUDED WITH EVERY TURN PLAN

Realtime Messaging allowance by plan

Concurrent connections and messages per month, included in the plan price.

PLAN CONCURRENT CONNECTIONS MESSAGES / MONTH OVER THE ALLOWANCE
FreeEvery account 100 100k Hard capNo overage charges
GrowthTURN 150 GB · $99/mo 1,000 5M $2.50 / million messages$0.01 / extra peak connection
BusinessTURN 500 GB · $199/mo 5,000 25M $2.50 / million messages$0.01 / extra peak connection
EnterpriseTURN 2 TB and up 20,000 100M $2.50 / million messages$0.01 / extra peak connection

Paid-plan overage is drawn from your account balance and is on by default. Connection overage is charged once per billing period for each connection your peak concurrency reaches above the allowance. The allowance becomes a hard cap if you switch overage off in the dashboard, or if your balance runs out with auto-recharge off. The free tier is hard-capped. Limits and quotas

Need higher limits, or Realtime Messaging without a TURN plan?

10 — FAQ

FAQ

Is Realtime Messaging a chat API?

No. It is the messaging layer underneath: channels, direct messages and presence over managed WebSockets. You build the interface. A chat room is one channel, and the roster comes from presence events.

How is it different from a backend pub/sub queue?

Realtime Messaging is built for clients — browsers, mobile apps, devices and agents — connecting directly over WebSockets with per-client scoped tokens and presence. It does not store messages: there is no history or replay, so catch-up state stays in your backend.

Do I need a Metered TURN plan?

No. Every account gets the free tier: 100 concurrent connections and 100,000 messages a month. Paid TURN plans include a larger allowance. For WebRTC, automatic TURN credential delivery needs an active TURN service on the app, on any tier including free.

Can I use my own TURN servers?

Yes. Put your own iceServers in the token's metadata and they are used instead of the automatically injected Metered TURN credentials.

How does authentication work?

A secret key pair stays on your server and signs short-lived JWTs (up to 24 hours) that scope each client to specific channels and actions. For static sites and prototypes, a browser-safe publishable key carries a fixed scope set in the dashboard.

What happens when a connection drops?

Every SDK reconnects automatically with exponential backoff and jitter, and treats close codes such as an invalid token or an administrative disconnect as final. WebRTC sessions also recover their peer connections and reconcile channels, so remote peers survive a signalling reconnect.

Which languages have an SDK?

JavaScript and TypeScript (browser, Node.js and React Native), Python and Flutter. Any other language, from C and C++ on devices to Go, Rust, Swift and Kotlin, connects to the same WebSocket protocol directly, and those clients share channels with SDK peers.

Does it work with React Native?

Yes. The JavaScript SDK runs on React Native with react-native-webrtc supplying WebRTC. Expo needs a development build rather than Expo Go.

Is it an MQTT broker?

No. It speaks its own JSON protocol over WebSockets. Devices connect with the same protocol as browsers, and your backend can publish commands over REST without holding a WebSocket open.

Can I self-host it?

No. Realtime Messaging is a managed service and the server is not open source. The SDKs are MIT-licensed: @metered-ca/realtime on npm, metered-realtime on PyPI and metered_realtime on pub.dev.

Build on Realtime Messaging

100 concurrent connections and 100k messages a month, free on every account.

Start free