B2B Realtime API

WebSocket-based realtime stream of tweets, follow/unfollow events, profile changes, and new follower digests for the handles you monitor. Sub-second detection. TweetCatcher-compatible opcode framing.

OpenAPI spec · Postman collection

Quickstart

  1. Subscribe via @B2B_Xanguard_bot on Telegram. Pay in SOL.
  2. The bot's confirmation message contains your raw API key (visible once) — save it now.
  3. Add handles via POST /v1/dt/targets or the bot's /add command.
  4. Connect to wss://api.xanguard.tech/v1/dt/realtime/ws and follow the opcode flow below.

Opcode protocol

Frames are JSON objects with an op integer field. Some carry a d data field, events also carry a t type field.

CodeNameDirectionPurpose
10HELLOserver → clientFirst frame after upgrade. d.heartbeat_interval is ms.
2LOGINclient → serverReply to HELLO with {"op":2,"d":"YOUR_API_KEY"} within 15s.
4READYserver → clientAuth succeeded. Streaming begins.
0EVENTserver → clientCarries t + d. See event types below.
1HEARTBEATclient → serverSend every heartbeat_interval ms.
11HEARTBEAT_ACKserver → clientServer's response to your heartbeat.
3DISCONNECTserver → clientConnection closes after this frame. d is the reason string.

Limits: 5 concurrent connections per API key. Stale events (>30s old) are dropped. Heartbeat ACK timeout is 90s.

Event types

tweet

New tweet, reply, quote, or retweet from a tracked handle.

{
  "op": 0, "t": "tweet",
  "d": {
    "tweet_id": "1789...",
    "author": {"handle": "elonmusk", "twitter_user_id": "44196397", "display_name": "Elon Musk", "avatar_url": "..."},
    "text": "...",
    "url": "https://x.com/elonmusk/status/1789...",
    "is_reply": false, "is_quote": false, "is_retweet": false,
    "image_urls": [], "mentions": [],
    "created_at": "2026-05-09T11:23:45Z"
  }
}

follow_change

A tracked handle followed or unfollowed someone.

{
  "op": 0, "t": "follow_change",
  "d": {
    "actor_handle": "elonmusk",
    "target_handle": "naval",
    "kind": "follow",
    "detected_at": "2026-05-09T11:23:45Z"
  }
}

profile_change

A tracked handle changed bio, name, avatar, banner, pinned tweet, location, or website.

{
  "op": 0, "t": "profile_change",
  "d": {
    "handle": "elonmusk",
    "field": "bio",
    "old_value": "...",
    "new_value": "...",
    "detected_at": "2026-05-09T11:23:45Z"
  }
}

new_followers

Digest of new followers for a tracked handle, emitted approximately every 30 minutes.

{
  "op": 0, "t": "new_followers",
  "d": {
    "handle": "elonmusk",
    "new_count": 42,
    "new_followers": [
      {"handle": "...", "twitter_user_id": "...", "display_name": "...", "follower_count": 12345}
    ],
    "detected_at": "2026-05-09T11:23:45Z"
  }
}

Code samples

JavaScript (browser / Node)

const ws = new WebSocket('wss://api.xanguard.tech/v1/dt/realtime/ws');
let heartbeat;

ws.onmessage = (raw) => {
  const frame = JSON.parse(raw.data);
  switch (frame.op) {
    case 10: // HELLO
      ws.send(JSON.stringify({ op: 2, d: 'dt_YOUR_API_KEY' })); // LOGIN
      heartbeat = setInterval(
        () => ws.send(JSON.stringify({ op: 1 })),
        frame.d.heartbeat_interval
      );
      break;
    case 4:  console.log('streaming'); break;             // READY
    case 0:  console.log(frame.t, frame.d); break;        // EVENT
    case 11: break;                                       // HEARTBEAT_ACK
    case 3:  console.error('disconnected:', frame.d); break;
  }
};
ws.onclose = () => clearInterval(heartbeat);

Python (asyncio)

import asyncio, json
import websockets

async def stream():
    async with websockets.connect('wss://api.xanguard.tech/v1/dt/realtime/ws') as ws:
        hello = json.loads(await ws.recv())                     # OP_HELLO
        await ws.send(json.dumps({'op': 2, 'd': 'dt_YOUR_API_KEY'}))
        heartbeat_ms = hello['d']['heartbeat_interval']

        async def hb():
            while True:
                await asyncio.sleep(heartbeat_ms / 1000)
                await ws.send(json.dumps({'op': 1}))
        asyncio.create_task(hb())

        async for raw in ws:
            frame = json.loads(raw)
            if frame['op'] == 0:                                # EVENT
                print(frame['t'], frame['d'])

asyncio.run(stream())

REST API (handle management)

MethodPathPurpose
GET/v1/dt/targetsList tracked handles.
POST/v1/dt/targetsAdd handle: {"handle":"elonmusk"}
DELETE/v1/dt/targets/{handle}Stop tracking.
GET/v1/dt/statusSubscription tier, expiry, slot usage.
PUT/v1/dt/webhookSet webhook URL (alternative delivery).

All authenticate via ?api_key=dt_... query parameter. Full schemas in the OpenAPI spec.

Webhooks (alternative delivery)

If you'd rather receive HTTP POSTs than maintain a WS connection, set webhook_url:

curl -X PUT 'https://api.xanguard.tech/v1/dt/webhook?api_key=dt_...' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://your-server.example.com/xanguard"}'

Each delivery includes header X-Signature: <hex> = HMAC-SHA256(secret, raw_body). Verify on receive. Body shape:

{
  "event": "tweet",
  "timestamp": 1715253825000,
  "data": { ... }   // identical to the WS event "d" field
}

Retry: 3 attempts with 1s, 2s, 4s backoff. Auto-disable: 10 consecutive failures pause the URL until you re-set it.

Error reference

CodeCauseAction
401Missing/wrong/expired API keyUse /apikey in the bot to regenerate.
403Free tier; account_limit exceededUpgrade plan.
429Rate-limit exceeded (REST) or 5-conn cap (WS)Back off; close idle connections.
OP_DISCONNECTWS-level error; d is the reasonReconnect with backoff.

Support

Questions: @0xDeep on Telegram. Bug reports preferred with payment_id + timestamp.