Skip to main content

Connect

Open a WebSocket to the gateway. Nothing is required at the handshake — no headers, no query parameters, no subprotocol.
Once open, the socket is immediately usable for market data. Authentication is only needed for your own account’s activity.
Market data needs no credential. If all you want is trades, order books, tickers or implied values, skip this page and go straight to Subscriptions.

Authenticate

To receive your own fills, orders and positions, send an authenticate frame carrying an API key.
On success the server replies with a success frame, and you are subscribed to your own event stream. You do not subscribe to it explicitly — authenticating is what enrolls you.
Your private stream arrives as prediction-market-update frames. These are independent of any subscribe you have sent; authenticating does not disturb your market-data subscriptions, and subscribing does not disturb your private stream.
A failed authenticate leaves the socket open for anonymous market data. You receive an error frame and stay connected, unauthenticated — there is no need to reconnect before retrying with a working credential.

API keys

Issue an API key from your SOAR account settings. Keys are prefixed soar_sk_ and are shown once at creation — store the value then, because it is not retrievable afterwards. Scopes do not nest. A trade-only key is rejected on the WebSocket with forbidden_scope, exactly as it is on the REST portfolio endpoints. A key needs read specifically.
An API key does not grant your account’s notification feed. Notifications are first-party application traffic and read has never covered them.

Replacing a credential

Re-authenticating resolves the new credential first, then replaces the old one — so your private stream stays up for the length of the lookup. If the new credential fails, you are left unauthenticated rather than silently kept on the old one. Treat a failed re-authentication as “log in again”, not as “still logged in as before”. Only one authenticate may be in flight at a time. A second frame sent while one is still resolving is rejected with authenticate_in_progress; wait for the pending response and retry.

Revocation

Revoking a key stops the private stream within 60 seconds, matching the REST promise. The server sends an error frame and then closes the socket: Because the socket closes, your market-data subscriptions end along with the private stream. Reconnect and resubscribe. Market data itself still needs no credential, so an anonymous socket is enough to keep prices flowing while you sort out the key.

Connection limits

Concurrent authenticated connections are capped at 5 per API key, counted across all servers. Excess authenticate attempts are refused with an error frame carrying code connection_limit. The socket stays open and market data keeps flowing, so you do not need to reconnect — close one of your other authenticated sockets and try again.
The limit is enforced best-effort rather than transactionally, so sockets opening simultaneously on one key may briefly exceed it. Treat it as back-pressure, not a guarantee, and do not design around the exact number.

Replacing REST polling

The private stream is a drop-in replacement for polling GET /v1/portfolio/fills and GET /v1/orders/{order_id}. Start buffering incoming socket events, authenticate, and wait for the success frame before loading history and current state over REST. Reconcile that response with the buffered events, deduplicating fills by tradeId, then keep applying live updates. There is no atomic boundary between the REST response and the stream, and missed events are not replayed. Loading REST history before authenticating can leave a gap. Reconcile against REST after every reconnect.