# Realtime on SupaHost

> How Supabase Realtime works on SupaHost. Broadcast, Presence and Postgres Changes over WebSockets, per-project limits, capacity per plan, and no per-message or per-connection fees.

Source: https://supahost.app/docs/realtime/overview · Updated 2026-11-11

Supabase Realtime lets clients exchange messages and receive database changes over a WebSocket. Every SupaHost project has Realtime, with all three features of the open-source server: **Broadcast**, **Presence** and **Postgres Changes**. You use it through `supabase.channel()` in supabase-js (or the equivalent in other client libraries), with no changes to your code.

Realtime on SupaHost is included in your instance's flat monthly price. There are no fees per message, per concurrent connection or per channel. What limits you is the instance's capacity and a set of per-project rate limits, covered in [Realtime capacity and limits](/docs/realtime/limits).

## The three features

| Feature | What it does | Good for |
|---|---|---|
| [Broadcast](/docs/realtime/broadcast) | Sends low-latency messages from one client to every other client on a channel. Can also be sent from your server or the database | Chat, cursors, live reactions, "someone is typing", game moves |
| [Presence](/docs/realtime/presence) | Keeps a shared list of who is on a channel, with a small state object per client | Online indicators, "3 people viewing", avatars on a shared document |
| [Postgres Changes](/docs/realtime/postgres-changes) | Streams inserts, updates and deletes from your tables to subscribed clients, filtered by RLS | Live dashboards, order status, small collaborative lists |

All three run over the same connection. A client opens one WebSocket and joins as many channels as it needs (up to 100 per client).

## How it's set up on SupaHost

- Each instance runs one Realtime server, and each project is a separate tenant on it with its own limits. Projects can't see each other's channels.
- Clients connect through your project URL. supabase-js derives the WebSocket address from the URL you pass to `createClient`:

```
wss://app-one.acme.supahost.space/realtime/v1/websocket
```

- The REST endpoint for sending Broadcast messages without a WebSocket is under `https://app-one.acme.supahost.space/realtime/v1/api/`.
- Like every other API on your project, Realtime needs your project's `anon` key (or a user's access token). supabase-js sends it for you.
- Postgres Changes reads from each project's own database, so a busy table in one project doesn't feed changes into another.

## A first channel

This connects to a channel for one board in a project management app, listens for "card-moved" messages, and sends one.

```ts
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.SUPABASE_URL!, // https://app-one.acme.supahost.space
  process.env.SUPABASE_ANON_KEY!
)

const board = supabase.channel('board:42')

board
  .on('broadcast', { event: 'card-moved' }, ({ payload }) => {
    console.log(`Card ${payload.cardId} moved to ${payload.column}`)
  })
  .subscribe(async (status) => {
    if (status !== 'SUBSCRIBED') return
    await board.send({
      type: 'broadcast',
      event: 'card-moved',
      payload: { cardId: 'c-7', column: 'done' },
    })
  })

// Later, when the user leaves the board
await supabase.removeChannel(board)
```

The `subscribe` callback receives the channel status: `SUBSCRIBED`, `TIMED_OUT`, `CHANNEL_ERROR` or `CLOSED`. Handle at least the first three. If you see `TIMED_OUT` or `CHANNEL_ERROR`, see [Realtime TIMED_OUT and CHANNEL_ERROR](/kb/realtime-timed-out).

## Public and private channels

By default a channel is public: anyone with your `anon` key who knows the channel name can join it and send messages. That's fine for things like a public live counter, but not for private data.

A channel created with `{ config: { private: true } }` is checked against RLS policies on the `realtime.messages` table, so you decide which users can receive and send on which topics. Use private channels for anything user-specific. See [Realtime authorization](/docs/realtime/authorization).

Postgres Changes has its own check: clients only receive rows they could read through your table's RLS policies.

## Picking the right feature

- **To show other users what someone is doing right now** (typing, cursor position, a game move), use Broadcast. Messages aren't stored; they go to whoever is connected.
- **To show who is online**, use Presence. Realtime keeps the list in sync as clients join and leave.
- **To keep a view in sync with a table**, start with Postgres Changes for low to moderate traffic. When many clients watch the same busy table, send changes with Broadcast from the database instead. It scales better because each change is sent once to a topic rather than checked against every subscriber. See [Broadcasting from the database](/kb/realtime-broadcast-from-database).

Realtime is not a durable message queue. If a client is offline when a message is sent, it misses it. Store anything that must not be lost in a table, and use Realtime to tell clients to refresh. For background work, see [Queues with pgmq](/docs/cron-queues/queues).

## Capacity at a glance

These are capacity guides from SupaHost's load tests, for a whole instance (shared by its projects):

| Plan | Concurrent Realtime connections (guide) |
|---|---|
| Builder | about 5,000 |
| Pro | about 15,000 |
| Business | about 30,000 |

Each project also has fixed rate limits, such as 5,000 events per second and 500 channel joins per second. Details and what happens when you reach them are in [Realtime capacity and limits](/docs/realtime/limits).

Realtime traffic goes through your instance's gateway, so messages sent to clients count toward your monthly egress.

## Debugging

- Open your browser's DevTools, go to the Network tab, filter by WS, and select the `websocket` connection to see every frame.
- Log channel status changes in your `subscribe` callback.
- SupaHost doesn't have a searchable log explorer in the dashboard, and Studio's Logs pages aren't available.

See [Debugging Realtime with logs and DevTools](/kb/realtime-debugging) and [Realtime messages not arriving](/kb/realtime-messages-not-arriving).

## Next steps

- [Broadcast](/docs/realtime/broadcast)
- [Presence](/docs/realtime/presence)
- [Postgres Changes](/docs/realtime/postgres-changes)
- Full client reference: [supabase-js `channel()`](https://supabase.com/docs/reference/javascript/subscribe)
