Tweet Alerts WebSocket: live tweets from tracked accounts

The WebSocket endpoint provides a persistent, real-time stream of tweet alerts. Connect once, subscribe to any of your tracked accounts, and receive structured JSON messages as tweets happen. This is ideal for trading bots, dashboards, and any application that needs the lowest possible latency.

Connection

Connect with your API key as a query parameter:

WebSocket URL
wss://api.xanguard.tech/v1/ws?api_key=xg_your_key

Client to Server Messages

After connecting, send JSON messages to subscribe or unsubscribe from handles. Only accounts already on your tracked list can be subscribed (add them first with POST /v1/accounts; send handles without @). Other handles are skipped with an error message, and the ack lists what was subscribed.

JSON — Subscribe to handles
{
  "type": "subscribe",
  "handles": ["elonmusk", "VitalikButerin"]
}
JSON — Unsubscribe from handles
{
  "type": "unsubscribe",
  "handles": ["elonmusk"]
}

Server to Client Messages

Tweet Alert

Received when a tracked account posts a tweet:

JSON — Tweet alert
{
  "type": "alert",
  "tweet_id": "1947000000000000000",
  "handle": "elonmusk",
  "text": "Just bought more $BTC",
  "url": "https://x.com/elonmusk/status/1947000000000000000",
  "image_url": null,
  "image_urls": [],
  "is_reply": false,
  "is_quote": false,
  "received_at": 1784646000120,
  "created_at": 1784645999800,
  "original_tweet_id": null,
  "possibly_sensitive": false,
  "mentions": []
}

received_at is when Xanguard detected the tweet; created_at is when Twitter says it was posted — the difference is your true detection latency. Quotes include an embedded quoted_tweet object (tweet_id, text, author_handle, author_name, author_avatar, image_urls); replies include in_reply_to_user / in_reply_to_tweet_id refs plus an embedded quoted_tweet carrying the parent tweet (same fields) — label by is_reply vs is_quote, not by quoted_tweet presence. Reply/quote context is included inline before delivery. All timestamps are epoch milliseconds.

Delivery rules: only tweets posted less than 60 seconds earlier, and detected after you subscribed, are sent; there is no replay after a reconnect. Each connection holds only the newest unsent alert, so a client that reads slowly can miss alerts during a burst. Telegram settings, keywords and mutes do not apply here: every tweet of a subscribed handle is sent.

Acknowledgement

Sent after a successful subscribe or unsubscribe action:

JSON — Acknowledgement
{
  "type": "ack",
  "action": "subscribe",
  "handles": ["elonmusk"],
  "active": 1,
  "max": 25
}

Error

Sent when a request cannot be fulfilled:

JSON — Error
{
  "type": "error",
  "message": "Would exceed handle limit: 10 + 5 new = 15, max 10"
}

Limits

LimitValue
ConnectionsPer account, across all your keys: Starter 2, Growth 3, Pro 5, Business 10, Scale 10, Enterprise 25, Ultra 50 (B2B dt_ keys 5). Excess attempts get a plain-text HTTP 429. A bad key or free plan gets a plain-text HTTP 401 "Invalid or expired API key".
Inbound messages10 messages/sec per connection — the 11th frame in any 1-second window closes the connection (pong frames count)
App-level pingSend {"type":"ping"} to receive {"type":"pong"} (optional liveness check)

Keepalive

The server sends a WebSocket ping frame every 30 seconds. Most WebSocket libraries answer automatically. If pongs go missing for ~3 minutes (6 consecutive pings), the server closes the connection. Your client should implement automatic reconnection with backoff.

Example: JavaScript Client

JavaScript — WebSocket client
const WS_URL = 'wss://api.xanguard.tech/v1/ws?api_key=xg_your_key';
let backoff = 1000; // ms, doubles on each failure, capped at 30s

function connect() {
  const ws = new WebSocket(WS_URL);

  ws.onopen = () => {
    backoff = 1000;
    // Subscribe to handles once connected (re-sent on every reconnect)
    ws.send(JSON.stringify({
      type: 'subscribe',
      handles: ['elonmusk', 'VitalikButerin']
    }));
  };

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    if (msg.type === 'alert') {
      console.log(`Tweet from @${msg.handle}: ${msg.text}`);
      console.log(`Link: ${msg.url}`);
    } else if (msg.type === 'ack') {
      console.log(`Watching ${msg.active}/${msg.max} handles`);
    } else if (msg.type === 'error') {
      console.error(`Error: ${msg.message}`);
    }
  };

  ws.onerror = () => ws.close();

  ws.onclose = () => {
    const delay = backoff + Math.random() * 500;
    console.log(`Disconnected, reconnecting in ${Math.round(delay)} ms`);
    backoff = Math.min(backoff * 2, 30000);
    setTimeout(connect, delay);
  };
}

connect();

Example: Python Client

Python — WebSocket client (asyncio)
import asyncio, json, random
import websockets  # pip install websockets

URI = "wss://api.xanguard.tech/v1/ws?api_key=xg_your_key"
HANDLES = ["elonmusk", "VitalikButerin"]

async def run_once():
    # The websockets library answers the server's ping frames automatically
    async with websockets.connect(URI) as ws:
        await ws.send(json.dumps({"type": "subscribe", "handles": HANDLES}))
        async for message in ws:
            msg = json.loads(message)
            if msg["type"] == "alert":
                print(f"@{msg['handle']}: {msg['text']}")
                print(f"Link: {msg['url']}")
            elif msg["type"] == "error":
                print("Error:", msg.get("message"))

async def main():
    backoff = 1
    while True:
        try:
            await run_once()
            backoff = 1  # clean close: reconnect quickly
        except websockets.exceptions.InvalidHandshake as e:
            status = getattr(getattr(e, "response", None), "status_code", None)
            if status in (401, 403):
                print(f"Rejected ({status}): check your API key and plan.")
                return
            print(f"Handshake failed: {e}")
        except (websockets.ConnectionClosed, OSError) as e:
            print(f"Disconnected: {e}")
        await asyncio.sleep(backoff + random.random())
        backoff = min(backoff * 2, 30)

asyncio.run(main())