Realtime limits and errors
Look up Realtime limits, error codes, and retry behavior.
Realtime limits apply at several levels: the HTTP request, product, channel and individual socket. Use the tables below to find the limit or error and the action it requires.
For request and response fields, see public HTTP routes. Namespace, grant, webhook and archive management are covered by the Realtime API reference.
Names and identifiers
Thing | Limit |
|---|---|
Channel name | namespace:rest, 3–128 characters of [a-zA-Z0-9._:-]. A name with no namespace segment is invalid. |
Namespace name | 1–64 characters of [a-zA-Z0-9._-]. No colon. |
Token subject | 1–128 characters of [a-zA-Z0-9._-]. |
Grant pattern | A concrete namespace segment, then up to 7 more segments; a segment may be *, which matches exactly one segment. |
Tokens
Thing | Limit |
|---|---|
Channels per token | 1–32 |
Operations per channel | 1–4, from subscribe, publish, presence, history |
Token lifetime | 60–3600 seconds; 3600 when you omit it |
Request and message size
Thing | Limit |
|---|---|
Request body, all routes except POST /messages | 64 KiB (65 536 bytes) |
Request body, POST /messages | 256 KiB (262 144 bytes) |
Client event published from a browser | 32 KiB (32 768 bytes) of serialized data |
The inline payload limit is 256 KiB. Realtime delivers serialized data at or below that size directly. Larger payloads use a storage reference: ref contains bucket_key, size and optional content_type. History entries likewise contain either data or ref. The public HTTP request-body limits above still apply.
Storing larger payloads requires storage provisioned for the product. Without it, the publish returns
503. Payloads within the inline limit are unaffected.
Publish quota
The product publish quota uses fixed 10-second windows:
Value | Default |
|---|---|
Sustained rate | 50 publishes per second |
Window | 10 seconds |
Burst allowance | 100 publishes per window |
Hard limit | 1 100 publishes per window (burst + twice the sustained allowance) |
Deployments can change these defaults. Check the limits that apply to your deployment when planning capacity.
The hard limit determines rejection; exceeding the sustained rate alone does not. Rejected publishes return 429 with Retry-After in seconds until the current window ends.
Public HTTP calls also have a separate limit of 100 requests per minute per IP address. Both limits apply to a public publish.
Client events
Browser publishing requires client events to be enabled on the namespace. Each connection allows 5 publishes per second with a burst of 10. Exceeding that budget returns a rate_limited error frame without closing the connection.
Client events are live-only: they have no history offset or epoch and cannot be replayed. The sending connection receives an acknowledgement but not its own message.
Presence
Thing | Limit |
|---|---|
Members per channel | 100 |
Member info | 10 KiB (10 240 bytes) serialized |
Entry lifetime | 1–3600 seconds; 60 when not given |
The roster counts distinct identities. Multiple tabs for one person increase that member’s connections count. Joining a full roster returns 429.
History
Thing | Limit |
|---|---|
last_n | 1–1000 entries |
last_ms | Any positive number of milliseconds |
Retention window | Up to 30 days, set per namespace |
Archive retention | Up to 3650 days, set per namespace |
Reading history from a namespace with retention disabled succeeds with an empty result.
Request either a window or a cursor containing both offset and epoch. Supplying both modes, neither mode or half a cursor is invalid. A recovered: false response means the epoch changed or required entries expired; reload current application state as described in history.
HTTP status codes
Code | What it means | What to do |
|---|---|---|
400 | The request failed validation — a malformed channel name, a half cursor, a cursor and a window together, a missing required field. The body names the failing fields. | Fix the request. Retrying it unchanged will fail the same way. |
401 | public_key_required: no public key was presented. | Send the key as Authorization: Bearer <key>. |
401 | public_key_invalid: the key matches no active key of the product in the path — unknown, mistyped, revoked (once the revocation has propagated), belonging to another product, or created on the other environment. | Unknown, revoked and foreign are deliberately not told apart, and the response does not name the environment that answered. Check the edge origin before you suspect the key, and give a fresh key its propagation window. |
403 | public_key_scope: the key is active but its scope does not cover the route you called. | Widen the key with Edit scope on Product → Settings → Public Access, or use a key that already covers Realtime. Retrying unchanged will not help. |
403 | Presence is not enabled for the channel’s namespace. | Enable presence on the namespace, or stop reading presence there. |
404 | The namespace is not registered for your product. | Register the namespace. |
413 | The request body is over the cap for the route. | Publish a smaller payload; see message size. |
422 | Every capability you declared on POST /tokens/issue was stripped by namespace policy, leaving nothing to mint. | Check the namespaces are registered and their policy allows the operations you asked for. |
429 | A publish beyond the hard quota, or presence on a full roster. Publishes carry Retry-After. | Wait out Retry-After and retry. If it recurs at a steady rate, you are over the sustained quota, not merely bursting. |
503 | public_config_unavailable: no key snapshot is loaded yet and the edge fails closed. | Retry with backoff. Your key is not the problem. |
503 | Fan-out or history is unavailable, or a payload is over the inline cap and no payload storage is provisioned. | Retry with backoff. The oversized-payload case will not clear on its own — provision storage or publish smaller payloads. |
The edge returns public_key_* and public_config_unavailable before a request reaches Realtime, with an error object containing code and message. The three key errors also include step: public_key_check. Allow roughly 30 seconds for key creation or revocation to propagate through the edge snapshot. See authentication.
WebSocket close codes
RealtimeSocket handles reconnection. These codes explain its behavior in logs or in a custom client:
Code | What it means | What the socket does |
|---|---|---|
4001 | The connection carries no valid token. | Reconnect immediately with a freshly minted token. |
4100 | The realtime service is draining, on shutdown or redeploy. | Reconnect immediately; another node takes the connection. |
4200 | Slow consumer: the connection’s per-channel buffer overflowed and messages were dropped. | Reconnect with backoff and come back through cursor recovery — the gap is real and must be replayed, not ignored. |
Codes 4001 and 4100 reconnect immediately. All other closes use backoff; repeated authentication failures still require checking the token provider.
WebSocket error frames
Error frames reject individual actions while leaving the connection open:
Code | What it means |
|---|---|
invalid | The frame was malformed, or the action was not allowed in this state — an unparseable frame, a missing channel, a client publish over 32 KiB, presence info too large, or a presence update from a connection that is not in the roster. |
forbidden | The token does not carry the operation for that channel. |
unavailable | The realtime service is up and can verify your token, but cannot serve subscribe, publish or presence work right now. Retry with backoff. |
rate_limited | The connection exceeded its client-event budget. |
Next steps
- Authenticate your backend and your users: mint a token that carries the operations you need.
- Connect a browser tab: how the socket reacts to each close code.
- Replay what a client missed: what to do when a recovery is refused.
- Call the public HTTP routes: the base path, the allowed origins, and each route’s request and response body.