No summary available
Project Links
Meta
Author: Joel Lee
Maintainer: Leonardo Santiago
Requires Python: >=3.9
Classifiers
Programming Language
- Python :: 3
- Python :: 3.9
- Python :: 3.10
- Python :: 3.11
- Python :: 3.12
- Python :: 3.13
- Python :: 3.14
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"
socket = AsyncRealtimeClient(REALTIME_URL, API_KEY)
channel = socket.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_URLisws://localhost:4000/socketwhen developing locally andwss://<project_ref>.supabase.co/realtime/v1when connecting to your Supabase project.API_KEYis a JWT whose claims must containexpandrole(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
acktotruemeans that thechannel.sendpromise will resolve once server replies with acknowledgement that it received the broadcast message request. - Setting
selftotruemeans that the client will receive the broadcast message it sent out. - Setting
privatetotruemeans that the client will use RLS to determine if the user can connect or not to a given channel.
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!")
)
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.
2.28.3
Mar 20, 2026
2.28.2
Mar 13, 2026
2.28.1
Mar 13, 2026
2.28.0
Feb 10, 2026
2.27.3
Feb 03, 2026
2.27.2
Jan 14, 2026
2.27.1
Jan 06, 2026
2.27.0
Dec 16, 2025
2.26.0
Dec 15, 2025
2.25.1
Dec 10, 2025
2.25.0
Dec 03, 2025
2.24.0
Nov 07, 2025
2.23.3
Nov 06, 2025
2.23.2
Nov 03, 2025
2.23.1
Nov 03, 2025
2.23.0
Oct 31, 2025
2.22.4
Oct 30, 2025
2.22.3
Oct 28, 2025
2.22.2
Oct 24, 2025
2.22.1
Oct 21, 2025
2.22.0
Oct 08, 2025
2.21.1
Oct 03, 2025
2.21.0
Oct 03, 2025
2.20.0
Sep 22, 2025
2.19.0
Sep 17, 2025
2.7.0
Jul 28, 2025
2.6.0
Jul 10, 2025
2.5.3
Jun 26, 2025
2.5.2
Jun 24, 2025
2.5.1
Jun 19, 2025
2.5.0
May 15, 2025
2.4.3
Apr 28, 2025
2.4.2
Mar 26, 2025
2.4.1
Feb 27, 2025
2.4.0
Feb 19, 2025
2.3.0
Jan 30, 2025
2.2.0
Jan 24, 2025
2.1.0
Dec 30, 2024
2.0.6
Oct 18, 2024
2.0.5
Sep 28, 2024
2.0.4
Sep 27, 2024
2.0.2
Aug 20, 2024
2.0.1
Aug 17, 2024
2.0.0
Aug 16, 2024
1.0.6
Jun 15, 2024
1.0.5
May 29, 2024
1.0.4
Apr 13, 2024
1.0.3
Mar 26, 2024
1.0.2
Dec 02, 2023
1.0.1
Dec 02, 2023
1.0.0
Feb 05, 2023
0.0.5
Sep 14, 2022
0.0.4
Dec 31, 2021
0.0.3
Dec 17, 2021
0.0.2
Dec 17, 2021
Wheel compatibility matrix
Files in release
Extras:
None
Dependencies:
pydantic
(<3.0.0,>=2.11.7)
typing-extensions
(>=4.14.0)
websockets
(<16,>=11)