Skip to main content

Supabase Logo

Supabase Realtime Client

Send ephemeral messages with Broadcast, track and synchronize state with Presence, and listen to database changes with Postgres Change Data Capture (CDC).

Guides · Reference Docs · Multiplayer Demo

Overview

This client enables you to use the following Supabase Realtime's features:

  • Broadcast: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users.
  • Presence: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage.
  • Postgres Change Data Capture (CDC): listen for changes in your PostgreSQL database and send them to clients.

Usage

Installing the Package

pip3 install realtime

or using uv

uv add realtime

Creating a Channel

import asyncio
from typing import Optional

from realtime import AsyncRealtimeClient, RealtimeSubscribeStates


async def main():
    REALTIME_URL = "ws://localhost:4000/websocket"
    API_KEY = "1234567890"

    client = AsyncRealtimeClient(REALTIME_URL, API_KEY)
    channel = client.channel("test-channel")

    def _on_subscribe(status: RealtimeSubscribeStates, err: Optional[Exception]):
        if status == RealtimeSubscribeStates.SUBSCRIBED:
            print("Connected!")
        elif status == RealtimeSubscribeStates.CHANNEL_ERROR:
            print(f"There was an error subscribing to channel: {err.args}")
        elif status == RealtimeSubscribeStates.TIMED_OUT:
            print("Realtime server did not respond in time.")
        elif status == RealtimeSubscribeStates.CLOSED:
            print("Realtime channel was unexpectedly closed.")

    await channel.subscribe(_on_subscribe)

Notes:

  • REALTIME_URL is ws://localhost:4000/socket when developing locally and wss://<project_ref>.supabase.co/realtime/v1 when connecting to your Supabase project.
  • API_KEY is a JWT whose claims must contain exp and role (existing database role).
  • Channel name can be any string.

Broadcast

Your client can send and receive messages based on the event.

# Setup...

channel = client.channel(
    "broadcast-test", {"config": {"broadcast": {"ack": False, "self": False}}}
)

await channel.on_broadcast("some-event", lambda payload: print(payload)).subscribe()
await channel.send_broadcast("some-event", {"hello": "world"})

Notes:

  • Setting ack to true means that the channel.send promise will resolve once server replies with acknowledgement that it received the broadcast message request.
  • Setting self to true means that the client will receive the broadcast message it sent out.
  • Setting private to true means that the client will use RLS to determine if the user can connect or not to a given channel.
  • Setting replication_ready to true instructs the server to emit a system event once the Postgres replication connection backing the channel is established and ready to stream changes. Listen for it with on_system; the payload's status is "ok" (message: "Replication connection established") on success or "error" if the connection is not ready in time.
channel = client.channel(
    "db-changes", {"config": {"broadcast": {"replication_ready": True}}}
)

channel.on_postgres_changes(
    "*", schema="public", table="messages",
    callback=lambda payload: print("Change received!", payload),
).on_system(
    lambda payload: payload.status == "ok"
    and print("Replication connection is ready:", payload.message)
)

await channel.subscribe()

Presence

Your client can track and sync state that's stored in the channel.

# Setup...

channel = client.channel(
    "presence-test",
    {
        "config": {
            "presence": {
                "key": ""
            }
        }
    }
)

channel.on_presence_sync(lambda: print("Online users: ", channel.presence_state()))
channel.on_presence_join(lambda new_presences: print("New users have joined: ", new_presences))
channel.on_presence_leave(lambda left_presences: print("Users have left: ", left_presences))

await channel.track({ 'user_id': 1 })

Postgres CDC

Receive database changes on the client.

# Setup...

channel = client.channel("db-changes")

channel.on_postgres_changes(
    "*",
    schema="public",
    callback=lambda payload: print("All changes in public schema: ", payload),
)

channel.on_postgres_changes(
    "INSERT",
    schema="public",
    table="messages",
    callback=lambda payload: print("All inserts in messages table: ", payload),
)

channel.on_postgres_changes(
    "UPDATE",
    schema="public",
    table="users",
    filter="username=eq.Realtime",
    callback=lambda payload: print(
        "All updates on users table when username is Realtime: ", payload
    ),
)

channel.subscribe(
    lambda status, err: status == RealtimeSubscribeStates.SUBSCRIBED
    and print("Ready to receive database changes!")
)

Filters

filter is a column=operator.value expression evaluated server-side. The following operators are supported:

Operator Example Meaning
eq id=eq.1 equal
neq id=neq.1 not equal
lt lte gt gte age=gte.18 comparison
in status=in.(active,pending) in list
like ilike title=like.%foo% pattern match (case in/sensitive)
is deleted_at=is.null IS null/true/false/unknown
match imatch title=match.^foo POSIX regex match (~ / ~*)
isdistinct value=isdistinct.1 NULL-safe inequality

Any operator can be negated with the not. prefix, e.g. filter="status=not.in.(draft,archived)". Multiple conditions combined with commas are applied as an AND, e.g. filter="amount=gt.100,status=in.(open,pending)".

Note: Realtime evaluates filters server-side over a single table's WAL — there is no resource embedding or or() grouping, and % (not *) is the wildcard for like/ilike.

Selecting columns

Use select to receive only a subset of columns instead of the full row. This reduces payload size (helpful for large bytea/jsonb columns). The selected columns must be selectable by the subscribing role:

channel.on_postgres_changes(
    "*",
    schema="public",
    table="users",
    select=["id", "first_name"],
    # payload record only contains { "id": ..., "first_name": ... }
    callback=lambda payload: print("Selected columns only: ", payload),
)

Get All Channels

You can see all the channels that your client has instantiated.

# Setup...

client.get_channels()

Cleanup

It is highly recommended that you clean up your channels after you're done with them.

  • Remove a single channel
# Setup...

channel = client.channel('some-channel-to-remove')

channel.subscribe()

await client.remove_channel(channel)
  • Remove all channels
# Setup...

channel1 = client.channel('a-channel-to-remove')
channel2 = client.channel('another-channel-to-remove')

await channel1.subscribe()
await channel2.subscribe()

await client.remove_all_channels()

Credits

This repo draws heavily from phoenix-js.

License

MIT.

Metadata

Release files for realtime 2.32.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for realtime 2.32.0
File Size Uploaded
realtime-2.32.0.tar.gz 20.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for realtime 2.32.0
File Interpreter ABI Platform
realtime-2.32.0-py3-none-any.whl Python 3 none any Details

Total release size: 44.8 kB

Release files / realtime-2.32.0.tar.gz

Download URL realtime-2.32.0.tar.gz
Size 20.9 kB
Tags Source
SHA-256 checksum
How to use checksums
42fb260cd39c287497a1c3e9e51733cfbb8ead4259fc2a43106fb83dc5cb04f7
BLAKE2b-256 checksum
How to use checksums
0c705ef463afea434f61666ee110c2a513d2307c81e3086d19719204777c8020
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.8.2

Release files / realtime-2.32.0-py3-none-any.whl

Download URL realtime-2.32.0-py3-none-any.whl
Size 23.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3f26f7c8693eae2553867c3be5bfb476dd886860c3d2d612256639b455cf5930
BLAKE2b-256 checksum
How to use checksums
eb92ab21e7ebfac76cb011f0acf578d2520ddf07f0e043d6bf756c2339607299
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.8.2

Release history Release notifications | RSS feed

This release

2.32.0 This release

2 release files

2.30.1

2 release files

2.29.0

2 release files

2.28.3

2 release files

2.28.2

2 release files

2.28.1

2 release files

2.28.0

2 release files

2.27.2

2 release files

2.27.0

2 release files

2.26.0

2 release files

2.25.1

2 release files

2.23.0

2 release files

2.22.4

2 release files

2.22.3

2 release files

2.22.2

2 release files

2.22.1

2 release files

2.20.0

2 release files

2.19.0

2 release files

2.7.0

2 release files

2.6.0

2 release files

2.5.3

2 release files

2.5.2

2 release files

2.5.1

2 release files

2.5.0

2 release files

2.4.3

2 release files

2.4.2

2 release files

2.4.1

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.6

2 release files

2.0.5

2 release files

2.0.4

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page