Rate limits and app status
To keep the service stable, each app's OpenAPI calls have a rate limit (quota), and exceeding it returns 429 rate_limited. OpenAPI also rejects some or all requests when an app or tenant is disabled, suspended or set to read-only. This page explains these rules and what to do when you get the corresponding errors. For count limits such as friends and group members, see Limits; app-level quotas such as registered users and concurrent connections are set by the app's plan, see Plan quotas.
OpenAPI call quota
All of an app's OpenAPI requests share one quota: rest_qps_limit per second, 1200 by default, with the same burst size.
- What counts: all of the app's OpenAPI requests, including queries, plus Get an App Token requests whose credentials pass validation, all in the same counter. Requests from multiple servers and multiple App Tokens are counted together.
- What doesn't count: requests failing the authentication checks, that is with an invalid App Token, one not belonging to the app, a source IP outside the allowlist, or an unavailable tenant or app. APIs users call from clients have a separate quota, see Other app quotas.
- Batch APIs count per item: APIs handling several items count per item; for example creating 100 users in one batch counts as 100, batch sending counts per recipient, and batch adding group members counts per person. One request counts at most the app's per-second quota, so apps with small quotas can still use batch APIs. If the quota isn't enough, the whole request returns
429 rate_limitedand none of its items run. - Getting App Tokens is also limited per source IP: at most 120 a minute per IP, including requests with wrong parameters or credentials. App Tokens are valid for 7 days by default; cache and reuse them instead of getting one before every call.
How rate limiting is calculated
Rate limits here "refill steadily and allow bursts". Think of the quota as a bucket holding at most N: each request takes 1, the bucket refills steadily at N per period, and requests are rejected when it's empty. So:
- After being idle for a while, you can send N requests back to back (a burst);
- Once used up, 1 is restored every "period ÷ N"; for example a quota of 1200 per second restores 1 about every 0.83 milliseconds;
- Over the long run, the average rate doesn't exceed N per period;
- Within any window one period long, up to about 2N may get through: the N stored in the bucket, plus N restored during the same window.
For example, calling continuously at high concurrency under the default quota, about 2400 succeed in the first second, then it settles at about 1200 a second, with the rest returning 429.
Limits phrased as "N a minute" or "N a day" elsewhere in these docs work the same way unless stated otherwise: you can use all N at once, then 1 is restored every "period ÷ N". For example getting App Tokens is limited to 120 a minute per IP, and once used up 1 is restored every 0.5 seconds.
A rejected request returns 429 rate_limited, with the Retry-After header giving the seconds to wait until enough quota is restored, rounded up and at least 1. The OpenAPI quota is per second and restores quickly, so Retry-After is usually 1:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 1
X-Request-Id: GJJ3W5Z3NWMWF56NNGGWDZWWYO
{
"error": {
"code": "rate_limited",
"message": "请求过于频繁,请稍后重试",
"request_id": "GJJ3W5Z3NWMWF56NNGGWDZWWYO"
}
}When the app's OpenAPI quota is used up there's no details; limits related to messages, chat rooms and so on carry details.reason, see Error codes.
To avoid hitting rate limits:
- Cache the App Token instead of getting one before every call;
- Control your call rate on the server with queues, queue background jobs such as bulk imports and broadcast notifications separately from online requests, and run them off-peak so they don't eat into the quota for online requests;
- After a
429, wait forRetry-Afterinstead of retrying immediately; multiple worker threads should honor the wait together rather than each retrying on its own.
Other app quotas
Besides the OpenAPI call quota, each app has these quotas, set by the platform:
| Quota | Default | Description | When exceeded |
|---|---|---|---|
Messages per minute msg_per_min_limit | 30000 | Messages sent by clients and the server in the app combined; batch sends count per recipient, online-only messages count, system messages such as group notices don't | 429 rate_limited with details.reason app_message_rate |
Client requests per second client_qps_limit | 1200 | APIs users call from clients plus requests over the persistent connection, counted separately from OpenAPI; call requests and call heartbeats over the persistent connection count, while entering and leaving chat rooms and sending chat room messages over it don't (they have chat room limits). Sign-in and renewal have a separate quota of the same size; new persistent connections are also limited to this many per second, counted separately | 429 rate_limited |
Storage quota storage_limit_gb | 1024 GB | When the storage quota limit is enabled, the total space of available files in the app (attachments, avatars, chat room covers, group files, including image thumbnails), with 1 GB as 230 bytes; deleted and blocked files and unfinished uploads don't count. The effective quota is the smaller of this and the plan's storage capacity. With the limit turned off, storage quota isn't checked and file storage isn't billed. Concurrent uploads may push usage slightly over. Check current usage and quota with Storage usage | With the limit enabled and exceeded, 409 limit_exceeded with details.reason storage_quota |
Chat rooms chatroom_limit | 10000 | Undismissed chat rooms in the app; the effective limit is the smaller of this and the plan's chat room count; 0 means no more chat rooms can be created | 409 limit_exceeded with details.reason chatroom_limit |
Chat room messages per second chatroom_msg_per_sec_limit | 2000 | Messages accepted per second across all the app's chat rooms, from clients and the server, at all three priorities (high, normal, low); not counted toward msg_per_min_limit | 429 rate_limited with details.reason room_send_rate; low-priority messages are dropped without an error |
Pushes per minute push_per_min_limit | 60000 | Offline pushes the app sends per minute, counted per device: messages and friend and group notifications count, while incoming calls, post-recall updates and test pushes don't; 0 stops pushing | No error: devices beyond the limit aren't pushed this time, recorded as rate_limited in Push statistics |
Callback requests per second callback_qps_limit | 500 | Event callback requests per second across all the app's callback endpoints; each endpoint's per-second limit can't exceed it, see Callback endpoints | No error: events beyond the limit wait in the queue and go out a little later, never dropped |
These OpenAPI-related rate limits are fixed:
| Limit | Value | When exceeded |
|---|---|---|
| Messages per group | 40 a second, from clients and the server combined | 429 rate_limited with details.reason group_send_rate |
| Password hashing (creating users with passwords, setting passwords) | 100 a second per app, OpenAPI and console combined | 429 rate_limited; in batch user creation, users beyond the limit appear among the failures |
| Getting App Tokens | 120 a minute per source IP | 429 rate_limited |
| Image processing when completing uploads | Waits up to 5 seconds when busy, then rejects; the file is still uploading, so just complete it again later | 429 rate_limited with details.reason image_busy |
| High-priority messages per chat room | 20 a second, from clients and the server combined. Normal and low priority are limited by the runtime policy chatroom_msg_per_second, see Chat room messages | 429 rate_limited with details.reason room_send_rate |
| Attribute writes per chat room | 20 a second, from clients and the server combined | 429 rate_limited with details.reason room_attribute_rate |
| Reporting on users' behalf | 600 a minute per app | 429 rate_limited with details.reason request_rate |
Per-user limits (such as messages a user sends per second, friend requests per day, files and data uploaded per day) only apply to users' actions in clients; your server acting on users' behalf isn't subject to them. For values see Limits. Server file uploads and getting download addresses only count toward the OpenAPI call quota, and getting several download addresses at once counts once.
Plan quotas
The quotas above are limits the platform sets to protect service stability. Separately, an app's registered users, concurrent connections, total groups, members per group, groups per user, message retention days, concurrent calls, chat rooms, and whether audio and video are available, are set by the app's plan; whether file storage capacity is limited by the plan depends on the platform's billing settings. See them under "Plan and usage" in the app's details in the console.
- When used up: the corresponding operation is refused, usually with
409 limit_exceededanddetails.reasonnaming which, such asapp_group_limit,storage_quota,chatroom_limit; there's nodetailswhen registered users are full. When concurrent connections are full new persistent connections are closed, and when the plan doesn't include audio and video users can't start or answer calls. See Count limits. - Relation to quotas and runtime policies: with the storage quota limit enabled, storage capacity and chat rooms are also bound by the platform quotas in the table above, whichever is smaller applies; members per group, groups per user, message retention days and the audio/video switch can also be set stricter in runtime policies, and the stricter one applies.
- Lowering a quota: data already over isn't affected but can't grow; except message retention, where messages beyond a shortened retention can no longer be fetched and are gradually deleted.
- Raising a quota: change plans, see Change plan.
Get the current plan
Query the app's current plan, the quotas in effect and the latest day's usage from your server, for example to alert your team before users approach a limit. AI coding assistants use it with the server key they were authorized with to check whether a quota is enough.
curl "$IM_API/$ORG/$APP/billing/plan" \
-H "Authorization: Bearer $APP_TOKEN"{
"app_name": "demo",
"plan": { "code": "std", "name": "标准版", "kind": "im" },
"current_since": "2026-11-01",
"period_last_day": null,
"upcoming": [],
"quotas": {
"max_users": 100000, "max_connections": 5000, "max_groups": 0, "max_group_members": 0,
"max_groups_per_user": 0, "max_message_retention_days": 7, "max_storage_gb": 100,
"max_concurrent_calls": 20, "max_chatrooms": 0, "max_group_call_participants": 0,
"rtc_enabled": true, "max_game_online": 0, "max_game_room_players": 0
},
"entitlements": { "chatroom": true, "game_room": false, "ai_bot": true },
"storage_quota_enabled": true,
"latest_usage": {
"date": "2026-11-15", "registered_users": 1234, "peak_connections": 4812, "dau": 980,
"messages": 56120, "chatroom_peak_connections": 0, "game_player_minutes": null,
"storage_gb": 3, "storage_bytes": 2899102924
}
}| Field | Description |
|---|---|
plan | The plan's code, name and kind (im instant messaging, game game) |
current_since, period_last_day | When the current plan took effect; plans bought for a term have a last day, otherwise null |
upcoming | Plans already scheduled to take effect later (plan_code, plan_name, effective_from) |
quotas | Quotas in effect, including any adjustments the platform made for this app. 0 means unlimited; max_game_room_players of 0 means the protocol limit of 10; max_storage_gb is 0 when the storage quota limit isn't enabled |
entitlements | Whether each feature is available, such as game_room, chatroom, ai_bot |
latest_usage | Usage on the latest stats day (yesterday at the latest), null when there are no stats yet, and null for items not collected. These are daily stats, not live values: the previous day is collected early each morning |
- Only returns data for the token's own app, without prices, balance or bills; change plans and view bills in the console.
- Counts toward the OpenAPI call quota.
App and tenant status
Tenants can disable apps in the console; the platform can suspend apps or tenants, or set apps read-only, when it finds violations or risks; when a tenant is in arrears its apps become read-only and later the tenant is suspended, see Arrears. How each status affects OpenAPI:
| Status | Getting App Tokens | Issued App Tokens | Calling OpenAPI | After recovery |
|---|---|---|---|---|
| Normal | Works | Valid | Works | — |
| Rate-limited (platform flag) | Works | Valid | Same as normal, with all quotas enforced as usual; the platform may also lower the app's quotas | — |
| Read-only (set by the platform or due to arrears) | Works | Valid | Queries and enforcement actions work; writes that add data or change profiles return 403 app_unavailable, with details.reason arrears when it's only due to arrears | Nothing to do; writes work again |
| App disabled (in the console) | 403 app_unavailable | Stop working immediately | 401 unauthenticated | Get a new App Token after enabling |
| App suspended by the platform | 403 app_unavailable | Stop working immediately | 401 unauthenticated | Get a new App Token after the platform restores it |
| Tenant suspended (by the platform or for arrears) | 403 tenant_unavailable, with details.reason arrears when it's only due to arrears | Stop working immediately | 401 unauthenticated | Get a new App Token after recovery |
| Tenant in the account-deletion cooling-off period | 403 tenant_unavailable | Stop working immediately | 401 unauthenticated | Get a new App Token after canceling the deletion |
| App deleted | 401 invalid_client | Permanently invalid | 401 unauthenticated with details.reason app_deleted | Can't be recovered |
| Tenant closed | 401 invalid_client | Permanently invalid | 401 unauthenticated with details.reason tenant_closed | Can't be recovered |
- When status changes take effect: usually immediately, at most about 30 seconds.
- What you see after disabling or suspension: all issued App Tokens stop working, so you first get
401 unauthenticated, then403 app_unavailableor403 tenant_unavailablewhen getting a new token. Don't keep retrying; raise an alert, try again at long intervals (such as a few minutes), and check the tenant and app status in the console. - After recovery: previously issued App Tokens don't become valid again; get a new one, with the same Client ID and Client Secret.
- Read-only: refused writes include creating users and changing user profiles; adding friends and changing friend remarks and custom attributes; creating groups, adding members, changing group profiles and member attributes; sending, editing and pinning messages; creating uploads (uploads already created can still complete); creating chat rooms, changing chat room profiles, setting chat room attributes, adding to chat room allowlists and sending chat room messages. Queries still work, as do banning, muting, signing users out, changing passwords, deleting users, deleting friends, blocking, removing group members, dismissing groups, recalling and deleting messages, marking as read, getting download addresses and deleting files, chat room bans, dismissal and member enforcement, and ending calls and removing call members: they don't add data, or are needed to deal with violations. Each API's documentation is authoritative.
- Effect on users: while an app is disabled or suspended, or the tenant is suspended or in the cooling-off period, users' User Tokens also stop working, persistent connections are closed, users can't sign in or send and receive messages, and can't get file download addresses (addresses already obtained work until they expire, and public avatar addresses stay accessible), and ongoing calls end; after recovery clients just renew, without signing in again. In read-only state users stay connected and can view messages, enter chat rooms, and answer and join calls, but can't send messages (including chat room messages), start calls or invite others in group calls. After an app is deleted or the tenant closed, users' tokens are permanently invalid.
- Offline push and event callbacks: while an app is disabled or suspended, or the tenant is suspended or in the cooling-off period, no offline pushes are sent (post-recall update notifications still are) and no new event callbacks are generated; events missed during that time aren't sent after recovery, see Effects of app status and When callbacks aren't sent. In read-only state pushes and callbacks work as usual.
Arrears
An issued invoice not fully paid by its due date is overdue. By default, counted from the due date of the earliest overdue invoice, all the tenant's apps become read-only after 15 days and the tenant's service is suspended after 30 days, unless your contract says otherwise; the billing contact is notified 3 days before each takes effect. In both states details.reason of app_unavailable and tenant_unavailable is arrears.
Once all overdue invoices are paid, read-only and suspension lift automatically, usually within seconds of the payment being recorded: writes work again once read-only lifts, and after suspension lifts, just get a new App Token. Billing continues during restrictions. See Overdue invoices and arrears.
Requesting higher quotas
There are two kinds of limits, raised in different ways:
- Count quotas: registered users, concurrent connections, total groups, concurrent calls, chat rooms and other plan quotas; change plans if you need more, see Change plan. With the storage quota limit enabled, storage capacity and chat rooms are also bound by platform quotas (1024 GB and 10,000 by default); mention it when contacting us if you need more.
- Protective quotas: the OpenAPI call quota and other app quotas, such as requests per second, messages per minute and pushes per minute, aren't tied to price and are adjusted by the platform according to business needs.
The default protective quotas suit most apps. If your business needs more, for example for large data imports, broadcast notifications at peak times, or many users coming online at once, contact us with:
- the app's AppKey;
- the quotas to adjust and the values you want;
- your business scenario and expected peak call volume.
Quota and plan changes take effect quickly (quotas within about 30 seconds, plans usually within seconds), without getting a new App Token.
RTC channel admission
- Channels and calls share two limits: a user can only be in one call or channel at a time, and the app's concurrent call quota counts both together.
- For channel counts, participants and request rates, see Limits.
- New joins are controlled by the platform's channel switch and the app's channel switch, which is on by default. Turning either off doesn't immediately end existing sessions.
- When the app is read-only, the audio/video policy is off, or the plan doesn't allow audio and video, sessions in existing channels can't renew media credentials; cleanup such as leaving still works.
