// Example 1 — single catch + switch on the numeric error code.
try
{
await channel.SendNewMessageAsync("Hello");
}
catch (StreamApiException ex)
{
// ex.Code / ex.StatusCode / ex.ErrorMessage / ex.ExceptionFields / ex.MoreInfo
// are all available for inspection. See the API Error Codes docs page
// for the full list.
switch (ex.Code)
{
case StreamApiException.RateLimitErrorStreamCode:
// HTTP 429 — back off with exponential delay before retrying.
await Task.Delay(TimeSpan.FromSeconds(1));
break;
case StreamApiException.CooldownErrorStreamCode:
// HTTP 403 — slow-mode cooldown. Gate the send UI for `channel.Cooldown` seconds.
break;
case StreamApiException.PermissionDeniedErrorStreamCode:
// HTTP 403 — user lacks permission. Hide / disable the control.
break;
default:
Debug.LogError($"Stream API error {ex.Code} (HTTP {ex.StatusCode}): {ex.ErrorMessage}");
break;
}
}
// Alternatively, use the `when` syntax combined with our dedicated
// error-checking extensions.
try
{
await channel.SendNewMessageAsync("Hello");
}
catch (StreamApiException ex) when (ex.IsRateLimitExceededError())
{
// HTTP 429 / Stream code 9 — back off before retrying.
await Task.Delay(TimeSpan.FromSeconds(1));
}
// Most common error cases have a dedicated extension method on StreamApiException.
// Pick the helper that matches your branch instead of comparing `ex.Code` by hand:
// IsRateLimitExceededError (429 / 9) — back off and retry
// IsCooldownError (403 / 60) — gate UI for `channel.Cooldown` seconds
// IsPermissionDeniedError (403 / 17) — hide the UI control
// IsNoAccessToChannelsError (403 / 70) — drop the channel locally
// IsAppSuspendedError (403 / 99) — show service-unavailable UI
// IsAuthenticationError (401 / 5) — send the user back to sign-in
// IsTokenExpiredError (401 / 40) — refresh the token
// IsTokenError (401 / 40-43) — any token-related failure
// IsDoesNotExistError (404 / 16) — refresh the local view
// IsInputError (400 / 4) — inspect `ex.ExceptionFields`
// IsMessageTooLongError (400 / 20) — show character-limit error
// IsMessageModerationFailedError (400 / 73) — show "filtered" UI
// IsPayloadTooBigError (413 / 22) — ask for a smaller file
// IsInternalSystemError (500 / -1) — retry, then surface a transient-failure UI
//
// The general guidance: wrap a call in try/catch where you want to react
// specifically (rate-limit back-off, cooldown UI, validation feedback,
// permission gating). For everything else, let the exception propagate to
// your global error handler — every SDK call already throws StreamApiException
// on server-rejected requests, so a single boundary catch is enough.Rate Limits
Rate limit mechanics are shared by every Stream product and documented on Rate limits: how limits are counted, the X-RateLimit-* headers, inspecting your quotas, catching 429s, and enabling automatic retries in the server SDKs. This page lists the chat endpoint limits and chat-specific practices.
Rate Limits by Endpoint
Default rate limits for self-serve plans (per minute, per platform).
| API Request | Limit/min |
|---|---|
| Connect | 10,000 |
| Get or Create Channel | 10,000 |
| Get App | 10,000 |
| Mark All Read | 10,000 |
| Mark Read | 10,000 |
| Query Channels | 10,000 |
| Send Event | 10,000 |
| Create Guest | 1,000 |
| Delete Message | 1,000 |
| Delete Reaction | 1,000 |
| Get Message | 1,000 |
| Get Reactions | 1,000 |
| Get Replies | 1,000 |
| Query Users | 1,000 |
| Run Message Action | 1,000 |
| Send Message | 1,000 |
| Send Reaction | 1,000 |
| Stop Watching Channel | 1,000 |
| Update Message | 1,000 |
| Upload File | 1,000 |
| Upload Image | 1,000 |
| Ban | 300 |
| Create Device | 300 |
| Edit Users | 300 |
| Flag | 300 |
| Hide Channel | 300 |
| Mute | 300 |
| Query Members | 300 |
| Search | 300 |
| Show Channel | 300 |
| Unban | 300 |
| Unflag | 300 |
| Unmute | 300 |
| Update Channel | 300 |
| Update Users | 300 |
| Update Users (Partial) | 300 |
| Activate User | 60 |
| Check Push | 60 |
| Create Channel Type | 60 |
| Deactivate User | 60 |
| Delete Channel | 60 |
| Delete Channel Type | 60 |
| Delete Device | 60 |
| Delete File | 60 |
| Delete User | 60 |
| Export Channel | 60 |
| Export User | 60 |
| Get Channel Type | 60 |
| List Channel Types | 60 |
| List Devices | 60 |
| Truncate Channel | 60 |
| Update App | 60 |
| Update Channel Type | 60 |
All endpoints also enforce the user rate limit of 60 requests per minute per user. Rate limits can be adjusted based on your use case and plan.
Avoiding Rate Limits in Chat
In addition to the general practices, chat integrations should:
-
Avoid redundant queries - Channel creation is an upsert operation. Do not call
QueryChannelsto check if a channel exists before creating it. See Query Channels. -
Use one WebSocket per user - Multiple WebSocket connections per user cause performance issues, billing problems, and unexpected behavior. See Initialization & Users.
-
Follow livestream best practices - High-volume messaging scenarios require additional optimization. See Livestream Best Practices.
Handling 429s in the Unity SDK
The Unity SDK surfaces rate limits and other API errors through StreamApiException. The exception classes are documented in Error handling, and the full list of codes in API error codes: