Real-Time Stream
A WebSocket for agents. WakeTrades pushes every copy event and every move of your leaders as it happens, and takes commands back (pause, close, emergency stop). Open GET /api/v1/stream with your session token.
The stream is a WebSocket between your agent and its copier. WakeTrades pushes every event as it happens: the same events as the webhook, plus each move of the leaders you follow. Your agent can send commands back on the same connection.
Open the Stream
GET wss://waketrades.com/api/v1/stream with the header Authorization: Bearer <session token>.
- It uses the same session token as the rest of the API (Sign In With a Wallet). When the token expires, the stream closes with code
4001: sign in again and reopen it. - You can keep up to 3 streams open at the same time per account. Beyond that, you get
429 too_many_streams. - A request without the
Upgrade: websocketheader gets426 upgrade_required. - Browsers can't add an
Authorizationheader to a WebSocket: open the stream from a server or an agent runtime.
Python (websockets library)
import asyncio, json, websockets
async def run(token: str):
async with websockets.connect("wss://waketrades.com/api/v1/stream",
additional_headers={"Authorization": f"Bearer {token}"}) as ws:
print(json.loads(await ws.recv())) # hello
await ws.send(json.dumps({"id": "1", "type": "positions"}))
async for raw in ws:
msg = json.loads(raw)
print(msg["type"], msg.get("data"))
JavaScript (Node) (ws library)
import WebSocket from "ws";
const ws = new WebSocket("wss://waketrades.com/api/v1/stream", { headers: { Authorization: `Bearer ${token}` } });
ws.on("open", () => ws.send(JSON.stringify({ id: "1", type: "positions" })));
ws.on("message", (raw) => {
const msg = JSON.parse(raw);
console.log(msg.type, msg.data);
});
First Message
{
"type": "hello",
"protocol": "waketrades.stream.v1",
"account": "0xyour_wallet",
"expiresAt": 1791633600000,
"commands": ["ping", "status", "positions", "orders", "journal", "pause", "resume", "close_position", "close_all", "emergency_stop"]
}
expiresAt is when your session token ends, in ms.
Events
Events have the same shape as webhook events: { "id", "type", "created", "account", "data" }.
- Every event of your copier: the types listed in Webhooks, and also the ones the webhook doesn't push (
skip,error,state,payment,limit_placed…). Askipcarries a stableskipcode (too_small,full,late,not_unified…), likeGET /orders. Muting notifications doesn't change the stream. - Your leaders' moves, as WakeTrades sees them, sent before your copy reacts:
type |
When |
|---|---|
leader_open |
A leader you follow opens a position |
leader_add, leader_cut |
The leader increases or reduces it |
leader_close |
The leader closes it |
leader_flip |
The leader reverses it (long to short, or short to long) |
leader_tpsl |
The leader changes the take profit or stop loss of a position opened since you follow them |
{
"id": "evt_mg8k2l1c_4be01a7d",
"type": "leader_open",
"created": 1791633600000,
"account": "0xyour_wallet",
"data": { "leader": "0xleader…", "coin": "ETH", "side": 1, "sz": 4, "prevSz": 0, "entryPx": 2450.5, "leverage": 10, "leverageType": "cross", "mid": 2451 }
}
side is 1 for long and -1 for short. sz is the leader's size in coins now (0 after a close), and prevSz the size before. tpsl, when known, lists the take profits and stops as [price, share of the position].
The stream doesn't keep events while you're disconnected. After a reconnection, catch up with the journal command or GET /api/v1/journal.
Commands
Send JSON: { "id": "your-id", "type": "<command>" }. Each command gets one answer with the same id:
{ "type": "result", "id": "your-id", "ok": true, "data": { } }
{ "type": "result", "id": "your-id", "ok": false, "error": "alert_channel_required", "message": "…" }
| Command | Fields | What it does |
|---|---|---|
ping |
Answers { "pong": true } |
|
status |
Copying state, exposure level, leaders, open copies, trial and plan dates | |
positions |
Copied positions with stop and take profit, like GET /positions (read again at most every 30 seconds, or right after an order is sent; longer during a Hyperliquid outage or heavy load) |
|
orders |
n (1 to 100, default 20) |
Copy orders, including skipped trades and why, like GET /orders |
journal |
n (1 to 100, default 20) |
Recent events, newest first |
pause |
Stop opening new copies; open ones stay managed | |
resume |
Resume copying (needs Telegram or a webhook) | |
close_position |
coin |
Close one copy at market |
close_all |
Close every copy at market and pause | |
emergency_stop |
The same as POST /emergency-stop: close everything and pause, with what it doesn't do (keyRevoked: false) |
Error codes: bad_request, unknown_command, alert_channel_required, command_failed, geo_blocked (write commands from a restricted jurisdiction, checked when the stream opens) and rate_limited (more than 60 commands per minute on one stream, or more than 180 per minute on all of an account's streams, reconnections included).
Close Codes
| Code | Why |
|---|---|
4001 |
The session token expired |
4003 |
The account was deleted or suspended |