# Slow Mode & Throttling

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:

1. Channel Slow Mode

2. Automatic feature Throttling

3. Message Throttling

Stream scales to 5 million concurrent users on a channel, see [Scaling Chat to 5 Million Concurrent Connections](https://getstream.io/blog/scaling-chat-5-million-concurrent-connections/).

### Channel Slow Mode

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.

<admonition type="info">

Moderators, admins and server-side API calls are not restricted by the cooldown period and can post messages as usual.

</admonition>

Slow mode is disabled by default and can be enabled/disabled via the Dashboard, using the Chat Explorer:

![](https://getstream.imgix.net/docs/Screenshot%202021-08-13%20105802.png?auto=compress&fit=clip&w=800&h=600)

It can also be enabled/disabled by admins and moderators via SDK.

<Tabs>

```kotlin label="Kotlin"
val channelClient = client.channel("messaging", "general")

// Enable slow mode and set cooldown to 1s
channelClient.enableSlowMode(cooldownTimeInSeconds = 1).enqueue { /* Result handling */ }

// Increase cooldown to 30s
channelClient.enableSlowMode(cooldownTimeInSeconds = 30).enqueue { /* Result handling */ }

// Disable slow mode
channelClient.disableSlowMode().enqueue { /* Result handling */ }
```

```js label="JavaScript"
// enable slow mode and set cooldown to 1s
await channel.enableSlowMode(1);

// increase cooldown to 30s
await channel.enableSlowMode(30);

// disable slow mode
await channel.disableSlowMode();
```

```swift label="Swift"
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))

channelController.enableSlowMode(cooldownDuration: 10)

channelController.disableSlowMode()
```

```cpp label="Unreal"
// Enable slow mode and set cooldown to 1s
Channel->EnableSlowMode(FTimespan::FromSeconds(1.));

// Increase cooldown to 30s
Channel->EnableSlowMode(FTimespan::FromSeconds(30.));

// Disable slow mode
Channel->DisableSlowMode();
```

```csharp label="C#"
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 mode
await chat.UpdateChannelPartialAsync("messaging", channelId,
    new UpdateChannelPartialRequest
    {
        Set = new Dictionary<string, object> { ["cooldown"] = 0 }
    });
```

```php label="PHP"
$client->updateChannelPartial("messaging", "general", new Models\UpdateChannelPartialRequest(
    set: (object)["cooldown" => 30], // 30 sec
));
```

```python label="Python"
channel.update_channel_partial(set={"cooldown": 30}) # 30 sec
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Enable slow mode with 30 second cooldown
client.chat.update_channel_partial('messaging', channel_id, Models::UpdateChannelPartialRequest.new(
  set: { 'cooldown' => 30 }
))
```

```go label="Go"
channel := client.Chat().Channel("messaging", "general")

channel.Update(ctx, &getstream.UpdateChannelRequest{
	Cooldown: getstream.PtrTo(30), // 30 sec
})
```

```csharp label="Unity"
// Not yet supported in the Unity SDK
```

```java label="Java"
// Android SDK
final ChannelClient channelClient = client.channel("messaging", "general");

// Enable slow mode and set cooldown to 1s
channelClient.enableSlowMode(1).enqueue(result -> { /* Result handling */ });

// Increase cooldown to 30s
channelClient.enableSlowMode(30).enqueue(result -> { /* Result handling */ });

// Disable slow mode
channelClient.disableSlowMode().enqueue(result -> { /* Result handling */ });

// Backend SDK

// Enable slow mode and set cooldown to 1s
chat.channel("messaging", "general")
    .update(UpdateChannelRequest.builder().cooldown(1).build());

// Increase cooldown to 30s
chat.channel("messaging", "general")
    .update(UpdateChannelRequest.builder().cooldown(30).build());

// Disable slow mode
chat.channel("messaging", "general")
    .update(UpdateChannelRequest.builder().cooldown(0).build());
```

</Tabs>

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.

<Tabs>

```kotlin label="Kotlin"
val channelClient = client.channel("messaging", "general")

// Get the cooldown value
channelClient.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())
    }
  }
}
```

```js label="JavaScript"
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;
```

```swift label="Swift"
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))

channelController.synchronize { error in
  if error == nil {
    let cooldown = channelController.channel?.cooldownDuration
  }
}
```

```cpp label="Unreal"
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);
    }
  });
```

```csharp label="Unity"
// Will be implemented soon, raise a GitHub issue if you need this feature https://github.com/GetStream/stream-chat-unity/issues/
```

```java label="Java"
final ChannelClient channelClient = client.channel("messaging", "general");

// Get the cooldown value
channelClient.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);
    });
  }
});
```

</Tabs>

### Automatic Feature Throttling

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.

1. Read events and typing indicator events are discarded

2. Watcher start/stop events are only sent once every 5 seconds

### Event throttling

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.

<admonition type="note">

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.

</admonition>

<Tabs>

<Tab value="chat" label="Chat">

| 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                          |

</Tab>

<Tab value="video" label="Video">

| 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                          |

</Tab>

<Tab value="shared" label="Chat + Video">

| 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                          |

</Tab>

</Tabs>

<admonition type="info">

If you are on an [Enterprise Plan](https://getstream.io/enterprise/), throttling limits can be adjusted for your application by our support team.

</admonition>


---

This page was last updated at 2026-05-22T16:32:17.619Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/dotnet-csharp/slow-mode/](https://getstream.io/chat/docs/dotnet-csharp/slow-mode/).