For live events or concerts, you can sometimes have so many users, that the sheer volume of messages overloads the browser or mobile device. This can cause the UI to freeze, high CPU usage, and degraded user experience. Stream offers 3 features to help with this:
Slow mode helps reduce noise on a channel by limiting users to a maximum of 1 message per cooldown interval.
The cooldown interval is configurable and can be anything between 1 and 120 seconds. For instance, if you enable slow mode and set the cooldown interval to 30 seconds a user will be able to post at most 1 message every 30 seconds.
Moderators, admins and server-side API calls are not restricted by the cooldown period and can post messages as usual.
Slow mode is disabled by default and can be enabled/disabled via the Dashboard, using the Chat Explorer:
It can also be enabled/disabled by admins and moderators via SDK.
val channelClient = client.channel("messaging", "general")// Enable slow mode and set cooldown to 1schannelClient.enableSlowMode(cooldownTimeInSeconds = 1).enqueue { /* Result handling */ }// Increase cooldown to 30schannelClient.enableSlowMode(cooldownTimeInSeconds = 30).enqueue { /* Result handling */ }// Disable slow modechannelClient.disableSlowMode().enqueue { /* Result handling */ }
// enable slow mode and set cooldown to 1sawait channel.enableSlowMode(1);// increase cooldown to 30sawait channel.enableSlowMode(30);// disable slow modeawait channel.disableSlowMode();
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))channelController.enableSlowMode(cooldownDuration: 10)channelController.disableSlowMode()
// Enable slow mode and set cooldown to 1sChannel->EnableSlowMode(FTimespan::FromSeconds(1.));// Increase cooldown to 30sChannel->EnableSlowMode(FTimespan::FromSeconds(30.));// Disable slow modeChannel->DisableSlowMode();
using GetStream;using GetStream.Models;var client = new StreamClient("{{ api_key }}", "{{ api_secret }}");var chat = new ChatClient(client);var channelId = "channel-id";// Enable slow mode (e.g. 1 message per 30 seconds)await chat.UpdateChannelPartialAsync("messaging", channelId, new UpdateChannelPartialRequest { Set = new Dictionary<string, object> { ["cooldown"] = 30 } });// Disable slow modeawait chat.UpdateChannelPartialAsync("messaging", channelId, new UpdateChannelPartialRequest { Set = new Dictionary<string, object> { ["cooldown"] = 0 } });
// This feature is not yet available in the Unity SDK.// Please let us know if you'd like this feature implemented: https://github.com/GetStream/stream-chat-unity/issues
// Android SDKfinal ChannelClient channelClient = client.channel("messaging", "general");// Enable slow mode and set cooldown to 1schannelClient.enableSlowMode(1).enqueue(result -> { /* Result handling */ });// Increase cooldown to 30schannelClient.enableSlowMode(30).enqueue(result -> { /* Result handling */ });// Disable slow modechannelClient.disableSlowMode().enqueue(result -> { /* Result handling */ });// Backend SDK// Enable slow mode and set cooldown to 1schat.channel("messaging", "general") .update(UpdateChannelRequest.builder().cooldown(1).build());// Increase cooldown to 30schat.channel("messaging", "general") .update(UpdateChannelRequest.builder().cooldown(30).build());// Disable slow modechat.channel("messaging", "general") .update(UpdateChannelRequest.builder().cooldown(0).build());
When a user posts a message during the cooldown period, the API returns an error message. You can avoid hitting the APIs and instead show such limitation on the send message UI directly. When slow mode is enabled, channels include a cooldown field containing the current cooldown period in seconds.
val channelClient = client.channel("messaging", "general")// Get the cooldown valuechannelClient.query(QueryChannelRequest()).enqueue { result -> if (result is Result.Success) { val channel = result.value val cooldown = channel.cooldown val message = Message(text = "Hello") channelClient.sendMessage(message).enqueue { // After sending a message, block the UI temporarily // The disable/enable UI methods have to be implemented by you disableMessageSendingUi() Handler(Looper.getMainLooper()) .postDelayed(::enableMessageSendingUi, cooldown.toLong()) } }}
const p = channel.sendMessage(msg);if (channel.data.cooldown != null && channel.data.cooldown > 0) { p.then(() => { // first lock the UI so that the user is aware of the cooldown disableSendMessageUI(); // restore the UI after the cooldown is finished setTimeout(enableSendMessageUI, channel.data.cooldown); });}await p;
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))channelController.synchronize { error in if error == nil { let cooldown = channelController.channel?.cooldownDuration }}
Channel->SendMessage( Message, [](const bool& bSuccess) { if (Channel->Properties.Cooldown > 0) { // first lock the UI so that the user is aware of the cooldown DisableSendMessageUI(); // restore the UI after the cooldown is finished GetWorld()->GetTimerManager().SetTimer( TimerHandle, [] { EnableSendMessageUI(); }, Channel->Properties.Cooldown.GetTotalSeconds(), false); } });
// This feature is not yet available in the Unity SDK.// Please let us know if you'd like this feature implemented: https://github.com/GetStream/stream-chat-unity/issues
final ChannelClient channelClient = client.channel("messaging", "general");// Get the cooldown valuechannelClient.query(new QueryChannelRequest()).enqueue(channelResult -> { if (channelResult.isSuccess()) { final Channel channel = channelResult.data(); int cooldown = channel.getCooldown(); Message message = new Message(); message.setText("Hello"); channelClient.sendMessage(message).enqueue((messageResult) -> { // After sending a message, block the UI temporarily // The disable/enable UI methods have to be implemented by you disableMessageSendingUi(); new Handler(Looper.getMainLooper()) .postDelayed(() -> enableMessageSendingUi(), cooldown); }); }});
When a channel has more than 100 active watchers Stream Chat automatically toggles off some features. This is to avoid performance degradation for end-users. Processing large amount of events can potentially increase CPU and memory usage on mobile and web apps.
Read events and typing indicator events are discarded
Watcher start/stop events are only sent once every 5 seconds
Stream applies two control layers to protect clients and keep real-time behaviour predictable under load:
API throttles cap how often a given event type can be produced. Limits are scoped by entity key (per channel, per call, or per user), not globally across your app.
WebSocket queue throttles cap how quickly events are delivered over a single user connection. Each connection has three queues that prioritise traffic and smooth bursts.
Together, these limits keep delivery fair during spikes such as live events, concerts, or mass-notification scenarios, without dropping the events that matter most.
WebSocket queues
Every user connection drains events through three queues, each with its own per-connection rate limit:
Queue
Limit (events/sec)
Purpose
critical
30
Highest priority. System and access events where fast, reliable delivery matters most.
ordered
15
Preserves event order. Used for sequential streams such as messages and reactions.
mergeable
10
Coalesces repeated updates by key (latest wins) to reduce noisy state-change traffic.
If a burst exceeds a queue's limit, events continue to be delivered but at the queue's steady-state rate. Events on the mergeable queue may be collapsed so only the most recent update per key is sent.
API throttles per event
The tables below list the per-entity API throttle for each event type and the WebSocket queue it is delivered through. unlimited means no API-side cap is applied; delivery is still governed by the WebSocket queue.
API throttles are applied per entity key. For example, message.new at 10 events/sec applies per channel, and user.presence.changed at 8 events/sec applies per user. Separate channels or users do not share the same budget.
Event
API throttle (events/sec)
WS queue
WS queue limit (events/sec)
* (custom events)
8
ordered
15
ai_indicator.clear
10
ordered
15
ai_indicator.stop
10
ordered
15
ai_indicator.update
10
ordered
15
channel.created
5
ordered
15
channel.deleted
5
ordered
15
channel.frozen
10
mergeable
10
channel.hidden
10
ordered
15
channel.kicked
30
critical
30
channel.max_streak_changed
10
mergeable
10
channel.truncated
5
ordered
15
channel.unfrozen
10
mergeable
10
channel.updated
8
mergeable
10
channel.visible
10
ordered
15
draft.deleted
10
mergeable
10
draft.updated
10
mergeable
10
member.added
8
ordered
15
member.removed
8
ordered
15
member.updated
8
ordered
15
message.deleted
8
ordered
15
message.delivered
8
ordered
15
message.new
10
ordered
15
message.pending
15
ordered
15
message.read
20
ordered
15
message.undeleted
8
ordered
15
message.updated
10
ordered
15
notification.added_to_channel
8
critical
30
notification.channel_deleted
8
critical
30
notification.channel_mutes_updated
8
mergeable
10
notification.channel_truncated
8
critical
30
notification.invite_accepted
8
critical
30
notification.invite_rejected
8
critical
30
notification.invited
8
critical
30
notification.mark_read
20
critical
30
notification.mark_unread
10
critical
30
notification.message_new
10
critical
30
notification.mutes_updated
8
mergeable
10
notification.reminder_due
unlimited
critical
30
notification.removed_from_channel
8
critical
30
notification.thread_message_new
10
critical
30
poll.closed
15
ordered
15
poll.deleted
15
ordered
15
poll.updated
15
ordered
15
poll.vote_casted
15
ordered
15
poll.vote_changed
15
ordered
15
poll.vote_removed
15
ordered
15
reaction.deleted
10
ordered
15
reaction.new
10
ordered
15
reaction.updated
10
ordered
15
reminder.created
15
ordered
15
reminder.deleted
15
ordered
15
reminder.updated
15
ordered
15
thread.updated
10
mergeable
10
typing.start
10
ordered
15
typing.stop
10
ordered
15
user.messages.deleted
15
critical
30
user.muted
10
mergeable
10
user.watching.start
5
ordered
15
user.watching.stop
5
ordered
15
Event
API throttle (events/sec)
WS queue
WS queue limit (events/sec)
call.accepted
8
critical
30
call.blocked_user
8
critical
30
call.closed_caption
10
critical
30
call.closed_captions_failed
8
critical
30
call.closed_captions_started
8
critical
30
call.closed_captions_stopped
8
critical
30
call.created
8
critical
30
call.deleted
8
critical
30
call.dtmf
30
critical
30
call.ended
8
critical
30
call.frame_recording_failed
8
critical
30
call.frame_recording_ready
10
critical
30
call.frame_recording_started
8
critical
30
call.frame_recording_stopped
8
critical
30
call.hls_broadcasting_failed
5
critical
30
call.hls_broadcasting_started
5
critical
30
call.hls_broadcasting_stopped
5
critical
30
call.kicked_user
8
critical
30
call.live_started
8
critical
30
call.member_added
8
ordered
15
call.member_removed
8
ordered
15
call.member_updated
8
ordered
15
call.member_updated_permission
8
critical
30
call.missed
8
critical
30
call.moderation_blur
8
critical
30
call.moderation_warning
8
critical
30
call.notification
8
critical
30
call.permission_request
8
critical
30
call.permissions_updated
8
critical
30
call.reaction_new
8
ordered
15
call.recording_failed
unlimited
critical
30
call.recording_ready
unlimited
critical
30
call.recording_started
unlimited
critical
30
call.recording_stopped
unlimited
critical
30
call.rejected
8
critical
30
call.ring
8
critical
30
call.rtmp_broadcast_failed
5
critical
30
call.rtmp_broadcast_started
5
critical
30
call.rtmp_broadcast_stopped
5
critical
30
call.session_ended
8
critical
30
call.session_participant_count_updated
0.30
mergeable
10
call.session_participant_joined
10
ordered
15
call.session_participant_left
10
ordered
15
call.session_started
8
critical
30
call.stats_report_ready
5
critical
30
call.transcription_failed
5
critical
30
call.transcription_ready
5
critical
30
call.transcription_started
5
critical
30
call.transcription_stopped
5
critical
30
call.unblocked_user
8
critical
30
call.updated
8
critical
30
call.user_feedback_submitted
8
ordered
15
call.user_muted
8
critical
30
connection.error
10
critical
30
connection.ok
10
critical
30
custom
10
ordered
15
ingress.error
30
critical
30
ingress.started
30
critical
30
ingress.stopped
30
critical
30
Event
API throttle (events/sec)
WS queue
WS queue limit (events/sec)
app.updated
30
critical
30
health.check
unlimited
critical
30
user.banned
30
critical
30
user.deactivated
30
critical
30
user.deleted
30
critical
30
user.presence.changed
8
ordered
15
user.reactivated
30
critical
30
user.unbanned
30
critical
30
user.updated
10
mergeable
10
If you are on an Enterprise Plan, throttling limits can be adjusted for your application by our support team.