Getting Started
Five minutes from npm install to two peers talking. If you can run a static HTML file and a Node script side by side, you can finish this page.
Install
npm install @metered-ca/peer
Or load the UMD bundle from a CDN for a static site:
<script src="https://unpkg.com/@metered-ca/peer/dist/index.umd.js"></script>
<script>
const { MeteredPeer } = window.MeteredPeer;
</script>
Get a key from the dashboard
- Sign up (skip if you already have an account).
- Dashboard → Realtime Messaging → Keys → Create key.
- Pick a key type:
| Key type | When to use | Where it goes |
|---|---|---|
pk_live_… publishable | Static sites, prototypes, no per-user scoping | Directly in browser code |
sk_live_… secret | Production apps with per-user permissions, custom peerId, embedded TURN creds | Server-side only — your backend mints JWTs |
Copy the signing secret when it's shown. It's only displayed once.
For the rest of this page we'll use the pk_live_ path so the example is one file. See Authentication when you're ready to move to JWT minting.
Your first connection
SignallingClient is the smaller surface — pub/sub, no WebRTC. Good for chat, telemetry, anywhere you don't need peer-to-peer media.
import { SignallingClient } from "@metered-ca/peer";
const client = new SignallingClient({ apiKey: "pk_live_…" });
client.on("connected", ({ peerId }) => {
console.log("connected as", peerId);
});
client.on("message", ({ channel, from, data }) => {
console.log(`${from} → ${channel}:`, data);
});
await client.connect();
await client.subscribe("room-42");
await client.publish("room-42", { hello: "world" });
Open this in two browser tabs. Each tab gets a different peerId; one tab's publish shows up as a message event in the other.
That's the whole pub/sub model. Subscribe to channels, publish to channels, receive message events for everything you've subscribed to.
Add peers (WebRTC)
MeteredPeer is the same thing plus channel-driven peer discovery + per-peer RTCPeerConnection lifecycle + MediaStream fan-out. Use this when peers exchange media (video / audio / screen share) or want low-latency P2P data.
import { MeteredPeer } from "@metered-ca/peer";
const peer = new MeteredPeer({ apiKey: "pk_live_…" });
peer.on("peer-joined", ({ peer: remote }) => {
console.log("someone joined:", remote.id);
remote.on("track", ({ streams }) => {
document.querySelector("#remote").srcObject = streams[0];
});
});
peer.on("peer-left", ({ peer: remote }) => {
console.log("someone left:", remote.id);
});
peer.on("data", ({ senderPeerId, data }) => {
console.log(senderPeerId, "said:", data);
});
const localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
peer.addStream(localStream);
await peer.join("my-room");
await peer.send({ hi: "everyone" });
What this does:
join("my-room")connects the WebSocket, subscribes you tomy-room, and asks the server who else is here.- For every peer the server reports,
peer-joinedfires and the SDK opens a peer-to-peerRTCPeerConnectionunder the hood. - The local stream you
addStream'd is sent to every peer; you receive theirs on thetrackevent. peer.send(data)broadcasts to everyone in the channel;peer.sendTo(remoteId, data)targets one peer.
Two browser tabs, same code → working call. No SDP, no ICE, no TURN config (works with embedded TURN — see Authentication).
Messages are server-routed by default
peer.send(data) is server-routed, not P2P over WebRTC DataChannel. It works before ICE completes, but it goes Browser → Metered server → Browser, so:
- Same API call broadcasts to the channel or directs to one peer
- Works even before the peer connection finishes negotiating
- Each message counts against your signalling-message quota
If you need low-latency P2P data (gaming, large transfers), open a real RTCDataChannel via the escape hatch:
peer.on("peer-joined", ({ peer: remote }) => {
remote.on("state-change", ({ to }) => {
if (to !== "connected") return;
const dc = remote.pc.createDataChannel("game-state", { ordered: false });
dc.onmessage = (e) => handleGameTick(JSON.parse(e.data));
});
});
See Data Channels & Low Latency for the full pattern (including backpressure handling and the reconnect gotcha that bites every developer once).
Next step depends on what you're building
| You're building… | Read |
|---|---|
| WebRTC video call (with TURN, with a backend) | Guide: WebRTC Video Call |
| WebRTC video call with no backend (static site) | Guide: WebRTC No Backend |
| Chat / classroom / livestream chat | Guide: Presence & Chat |
| AI agent comms | Guide: AI Agent Communication |
| IoT pub/sub | Guide: IoT Telemetry |
| Anything going to production | Guide: Reconnect Best Practices — required reading |
| Moving from simple-peer / PeerJS | Migration guides |
If you want to switch to per-user JWTs (custom peerId, channel permissions, embedded TURN creds), the Authentication guide walks through both paths.