Forward TweetStream tweets to Discord via webhooks
Forward filtered tweets into any Discord channel via webhooks — no bot user, no OAuth.
Why Discord webhooks
Discord channel webhooks take a single HTTP POST and render the body as a native embed. No Discord bot user, no OAuth flow, no gateway connection on your side.
Pair that with TweetStream's filtered stream and you get a zero-maintenance alerting channel — the worker you run is a thin translator between our WebSocket and Discord's webhook endpoint.
1. Get a Discord webhook URL
In the target channel, open Edit Channel → Integrations → Webhooks → New Webhook. Copy the webhook URL — it encodes the channel ID and a secret token. Treat it as a credential.
2. Install worker dependencies
The examples below assume a fresh project. Install the package imports first so the snippets run without module errors.
Node.js:
npm install wsPython:
pip install websockets aiohttp3. Forward tweets (Node.js)
A minimal Node.js worker that posts every tweet from the stream as a Discord embed.
import WebSocket from "ws";
const API_KEY = process.env.TWEETSTREAM_API_KEY!;
const DISCORD_WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL!;
const WS_URL = "wss://ws.tweetstream.io/ws";
const PROTOCOLS = ["tweetstream.v1", `tweetstream.auth.token.${API_KEY}`];
function tweetUrl(handle: string, id: string) {
return `https://x.com/${handle}/status/${id}`;
}
async function forwardToDiscord(embed: Record<string, unknown>) {
const res = await fetch(DISCORD_WEBHOOK_URL, {
body: JSON.stringify({ embeds: [embed] }),
headers: { "content-type": "application/json" },
method: "POST",
});
if (!res.ok) {
console.error("discord webhook failed", res.status, await res.text());
}
}
function connect() {
const ws = new WebSocket(WS_URL, PROTOCOLS);
ws.on("message", async (raw) => {
const env = JSON.parse(raw.toString());
if (env.t !== "tweet" || env.op !== "content") return;
const tweet = env.d;
const handle = tweet.author?.handle ?? "unknown";
await forwardToDiscord({
author: { name: `@${handle}` },
description: tweet.text,
title: "New tweet",
timestamp: new Date(tweet.createdAt ?? Date.now()).toISOString(),
url: tweetUrl(handle, tweet.tweetId),
});
});
ws.on("close", () => {
console.warn("socket closed, reconnecting in 5s");
setTimeout(connect, 5_000);
});
ws.on("error", (err) => {
console.error("socket error", err);
ws.close();
});
}
connect();3b. Forward tweets (Python)
Same worker in Python using websockets and aiohttp.
import asyncio
import json
import os
import aiohttp
import websockets
API_KEY = os.environ["TWEETSTREAM_API_KEY"]
DISCORD_WEBHOOK_URL = os.environ["DISCORD_WEBHOOK_URL"]
WS_URL = "wss://ws.tweetstream.io/ws"
SUBPROTOCOLS = ["tweetstream.v1", f"tweetstream.auth.token.{API_KEY}"]
async def forward(session, tweet):
handle = tweet["author"].get("handle", "unknown")
embed = {
"title": "New tweet",
"description": tweet["text"],
"url": f"https://x.com/{handle}/status/{tweet['tweetId']}",
"author": {"name": f"@{handle}"},
}
async with session.post(DISCORD_WEBHOOK_URL, json={"embeds": [embed]}) as resp:
if resp.status >= 300:
print("discord webhook failed", resp.status, await resp.text())
async def run():
async with aiohttp.ClientSession() as session:
async with websockets.connect(WS_URL, subprotocols=SUBPROTOCOLS) as ws:
async for raw in ws:
env = json.loads(raw)
if env["t"] == "tweet" and env["op"] == "content":
await forward(session, env["d"])
asyncio.run(run())4. Filter on detected tokens (optional)
Swap the listener to react on tweet/meta envelopes so you only alert when TweetStream has detected a ticker, contract address, or DEX URL.
// Only forward tweets that mention tracked tickers or contract addresses.
ws.on("message", async (raw) => {
const env = JSON.parse(raw.toString());
if (env.t !== "tweet") return;
// tweet/meta carries the detection payload.
if (env.op === "meta") {
const tokens = env.d.detected?.tokens ?? [];
if (tokens.length === 0) return;
await forwardToDiscord({
title: `${tokens.length} token${tokens.length > 1 ? "s" : ""} detected`,
description: tokens
.map((t: { symbol: string; priceUsd?: number }) =>
t.priceUsd ? `$${t.symbol} — $${t.priceUsd.toFixed(6)}` : `$${t.symbol}`,
)
.join("\n"),
});
}
});Rate-limit and deployment notes
- Discord webhooks allow roughly 30 requests per minute per webhook. Batch or throttle if your stream exceeds that.
- Discord embeds render better than raw text — use
title,description,url, andauthorfields. - Keep the webhook URL in a secret manager. Rotating it means regenerating the integration in Discord.
- For alerts per tracked handle, set
discordWebhookon each tracked account via the dashboard — TweetStream will route matching tweets without a worker process.
立即开启实时 Twitter WebSocket 提醒
内置 WebSocket 交付、OCR 与代币检测的 Twitter API 替代方案。
开始 3 天试用起价 $199/月 · Basic/Elite 含 3 天试用 · OCR + 代币检测
