Authentication
After the WebSocket quote push connection is established, a login authentication frame must be sent first. Do not send subscription requests before authentication succeeds. If authentication fails, the connection is closed, or no authentication result is returned within the timeout, close the current connection, correct the credentials, then reconnect and re-authenticate.
INFO
This document describes the authentication frame form supported by the current WebSocket access layer. The backend quote subscription service does not directly verify public access credentials; instead, it receives the session context established by the access layer after authentication. Therefore, developers only need to focus on the first-frame authentication after the WebSocket connection, the authentication result handling, and the subsequent subscription flow. You do not need to, and should not, pass in internal session fields.
Prerequisites
Before starting, confirm the following preparations are complete:
- You have obtained valid access credentials according to Getting Started.
- You have connected to the WebSocket Quote endpoint:
wss://webapi-quote.moomoo.com- The current account has the corresponding market, category, and quote permission level. Permissions affect the subscribable data types, order book depth, and actual push content.
Authentication Methods
WebSocket quote push supports two access methods:
| Method | Recommendation | Use Case | Description |
|---|---|---|---|
| OAuth 2.1 + PKCE | Recommended | Third-party applications, desktop applications, mobile applications, web applications, and other scenarios that require user authorization | Use access_token as a Bearer Token to complete WebSocket login authentication. |
| API Key | Compatible | Server-side systems owned by the developer, backend tasks, internal tools, and other scenarios where the developer manages the key | Use AppKey, timestamp, nonce, and signature to complete WebSocket login authentication. |
Recommended: use OAuth
The OAuth method does not require storing an API private key on the client, nor does it require manually managing signature material for each access. Unless you already have a server-side API Key integration or have explicit compatibility requirements, OAuth 2.1 + PKCE is preferred.
Authentication Frame Format
After the connection is established, the client should send the authentication frame as the first business message:
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Fixed as auth. |
data.auth_type | string | Yes | Authentication type. OAuth uses oauth2; API Key uses appkey. |
data.authorization | string | Yes | For OAuth, pass Bearer {access_token}; for API Key, pass the signature result. |
OAuth Authentication Example
First obtain an access_token per Getting Started: OAuth 2.1 + PKCE, then send the following JSON after the WebSocket connection opens:
{
"action": "auth",
"data": {
"auth_type": "oauth2",
"authorization": "Bearer {access_token}"
}
}Notes:
authorizationmust include theBearerprefix.- If
access_tokenis expired or invalid, authentication will fail. Refresh it withrefresh_token, then re-establish the WebSocket connection and re-authenticate. - Confirm that the user's authorization scope covers quote read access; if permissions are insufficient, subsequent subscriptions may fail or be unable to receive the corresponding types of data.
Token Security
Do not write access_token or refresh_token to frontend publicly accessible static configs, logs, or error reports. Server-side applications should use secure storage; desktop or mobile applications should use system keychains, encrypted storage, or similar mechanisms.
API Key Authentication Example
The API Key method is suitable for scenarios where the server holds the private key. First create an AppKey, upload the public key, and securely store the private key per Getting Started: Legacy API Key.
The high-level structure of the WebSocket authentication frame is as follows:
{
"action": "auth",
"data": {
"auth_type": "appkey",
"credential_id": "{app_key}",
"authorization": "{signature_base64}",
"timestamp_ms": 1782357937000,
"nonce": "{nonce}"
}
}Field description:
| Field | Type | Required | Description |
|---|---|---|---|
credential_id | string | Yes | AppKey ID. |
authorization | string | Yes | Signature result generated with the AppKey private key, usually a Base64-encoded signature string. |
timestamp_ms | int64 | Yes | Client's current millisecond timestamp. |
nonce | string | Yes | Client-generated random string used to prevent replay. |
API Key Signature Rules
WebSocket API Key authentication and REST API Key calls share the same AppKey / public key / private key identity system, but the signature payload for WebSocket authentication may include WebSocket-specific methods and paths. Follow the official integration guide or the WebSocket signature rules provided by platform tools; do not directly reuse the signature payload of a REST request.
Private Key Security
The API Key private key should only be stored in the developer's own secure server or controlled runtime environment. Do not distribute the private key to browsers, mobile installation packages, client config files, or code repositories.
Authentication Result Handling
After sending the authentication frame, the client should wait for the server to return the authentication result before sending subscription requests.
Recommended handling:
| Scenario | Recommendation |
|---|---|
| Authentication succeeds | Record that the connection is in an authenticated state, then send subscription requests. For the subscription format, see Subscribe and Unsubscribe. |
| Error returned | Do not continue subscribing. Check Token, AppKey, signature, timestamp, permissions, and other config based on the error message. |
| Server closes the connection | Treat the current connection as unavailable. Correct the credentials or wait for the backoff duration, then reconnect and re-authenticate. |
| Wait timeout | Actively close the connection and retry. Avoid sending subscription requests while the authentication state is unknown. |
Authentication Success Response
When authentication succeeds, the server returns the following JSON:
{
"id": "req-001",
"session_id": "123456789",
"server_time": 1782357937000000
}| Field | Type | Description |
|---|---|---|
id | string | Echo of the request ID; omitted if not provided in the request. |
session_id | string | Session ID assigned by the server. |
server_time | int64 | Server time, microsecond timestamp. |
After authentication succeeds, the current access layer establishes the session context required for subsequent subscriptions. The client does not need to parse or pass internal routing fields, such as uid, session_id, conn_id, the quote permission profile, or quota fields; these fields are handled internally by the access layer and backend service.
Channel Refresh
After authentication succeeds, a refresh frame can be sent on the established connection to update the session ticket. After a successful refresh, existing subscriptions are not interrupted and continue to push normally.
Refresh Frequency Requirement
The client must send a refresh frame at least every 10 minutes. If no active refresh is sent for more than 10 minutes, the server will actively close the connection. It is recommended to send a refresh frame on a 5-minute cycle to allow for error recovery.
OAuth Refresh
After using refresh_token to obtain a new access_token in the background, send the following refresh frame:
{
"action": "refresh",
"data": {
"authorization": "Bearer {new_access_token}"
}
}Notes:
- It is recommended to refresh proactively before the Token expires to avoid session interruption due to Token expiry.
authorizationmust include theBearerprefix.
API Key Refresh
{
"action": "refresh",
"data": {
"credential_id": "{app_key}",
"authorization": "{new_signature_base64}",
"timestamp_ms": 1782357938000,
"nonce": "{new_nonce}"
}
}Notes:
timestamp_msandnoncemust use new values different from the previous authentication; they cannot be reused.- The signing payload format is the same as for initial authentication; do not reuse previous signing material.
Refresh Success Response
When the refresh succeeds, the server returns a response in the same format as the initial authentication:
{
"id": "req-002",
"session_id": "123456789",
"server_time": 1782357938000000
}session_id remains unchanged; server_time is updated to the current server time.
If the refresh fails, check whether the new credentials are valid. If refreshes fail consecutively, close the connection and re-establish it with new credentials and re-authenticate.
Relationship with Subscription Permissions
Login authentication only confirms "who the current connection is" and "whether the credentials are valid". Whether a symbol or data type can actually be subscribed is also affected by:
- Whether the account has quote permissions for the corresponding market and category.
- Whether the quote permission level supports the corresponding data type or depth, e.g. the effect of LV1 / LV2 / LV3 on order book depth.
- Whether the subscription quota for the current account or permission level is sufficient.
- Whether the symbol code is valid and supports the corresponding push type.
Therefore, successful authentication does not mean all subscriptions will succeed. Subscription requests still need to handle success, failure, insufficient permissions, and insufficient quota based on the response.
Connection Lifecycle Recommendations
- Authenticate before subscribing: After each new WebSocket connection, send the authentication frame first and wait for success before subscribing.
- Re-authenticate after a disconnect: Do not assume the authentication state of the original connection is still valid after a disconnect. Reconnect, re-authenticate, and re-subscribe.
- Send refresh frames periodically: After authentication succeeds, the client must send a refresh frame at least every 10 minutes; a cycle of 5 minutes is recommended. If no refresh is sent for more than 10 minutes, the server will actively close the connection. OAuth scenarios should also refresh before Token expiry; API Key scenarios equally require periodic refresh to maintain the session. See Channel Refresh.
- Do not reuse internal session fields: Session identifiers that appear in server responses or logs are only used for access layer and backend routing and should not be persisted or reused as public subscription parameters.
- Use backoff retries: On consecutive authentication failures, check the credential config and use exponential backoff to avoid high-frequency reconnections.
FAQ
Can I resend an authentication frame on the same connection to switch users?
Not recommended. A WebSocket connection should correspond to a single, explicit login authentication context. To switch users, accounts, or credentials, close the old connection, reconnect with the new credentials, and re-authenticate.
Can OAuth and API Key be mixed?
Choose one authentication method per connection. The OAuth method uses auth_type=oauth2 with a Bearer Token; the API Key method uses auth_type=appkey with signature material. Do not pass both sets of credentials in the same authentication frame.
Do I still need to pass a Token in subscription requests after authentication succeeds?
No. After authentication succeeds, subsequent subscription requests use the established connection session context. Subscription requests only need to express the action, symbols, and data types; do not repeatedly pass Token, AppKey, or internal session fields.
Why do subscriptions still fail after authentication succeeds?
Common reasons include: invalid symbol code, the account lacks permissions for the corresponding market or category, the current quote permission level does not support that data type, insufficient subscription quota, or an invalid K-line period / adjustment method in the request. Troubleshoot with Subscribe and Unsubscribe, Error Codes, and Rate Limit and Quota.
Next Steps
- Quote Push Overview — Learn about WebSocket quote push capabilities and the onboarding flow.
- Subscribe and Unsubscribe — Subscribe to the required quote types after authentication succeeds.
- Keep-alive — Handle ping/pong, timeout, reconnection, and re-subscription.
- Error Codes — Troubleshoot authentication, subscription, permission, and quota issues.
- Rate Limit and Quota — Learn about subscription quotas and retry recommendations.