Twitter to Discord Webhook Setup

Complete tutorial for routing real-time Twitter/X alerts into Discord channels with webhooks, code examples, and rich embed formatting.

3-day trial, then $199/mo · 5 tracked accounts · 1 WebSocket

Published June 15, 2025 · Updated September 24, 2026 · 5 min read · TweetStream team

No code? Use the Discord bot

Paid plans and trials include the TweetStream Discord bot, which tracks at least 50 X accounts at standard speed, or your plan's limit if higher. Sign up with email or Discord, link your Discord identity, and join the TweetStream community. Then install the bot in your own server. The bot currently supports X only; WebSocket, other sources, and customer webhooks require the applicable paid plan or eligible trial.

See the Twitter Discord bot

Why route Twitter alerts to Discord?

Many crypto teams already coordinate in Discord through trading rooms, research channels, and execution dashboards. Routing Twitter/X alerts there keeps the signal in the same workspace where the team can review it.

Discord webhooks can post formatted Twitter/X alerts to the channels your team already monitors, so no one has to watch another app or inbox.

What is a Discord webhook?

A Discord webhook is a URL that accepts HTTP POST requests and posts messages into a specific channel. You do not need a bot account, OAuth tokens, or gateway connections. Just send JSON to the URL and the message appears in the channel.

Webhook URL format: https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN

Discord webhooks support plain text messages, rich embeds (with titles, colors, fields, images, and footers), file attachments, and up to 10 embeds per message.

Step 1: Create a Discord webhook

  1. Open your Discord server and navigate to the channel where you want alerts
  2. Click the gear icon (Edit Channel) next to the channel name
  3. Go to Integrations → Webhooks → New Webhook
  4. Name the webhook (e.g., 'Twitter Alerts') and optionally set an avatar
  5. Click 'Copy Webhook URL' — save this URL securely
  6. Click Save

Keep your webhook URL private. Anyone with the URL can post messages to your channel.

Step 2: Understand the payload format

Discord webhooks accept a JSON payload with two main fields: content for plain text and embeds for rich formatted messages. For trading alerts, embeds are preferred because they support structured data, color-coding, and inline fields.

Step 3: Send alerts with Node.js

javascript
const WEBHOOK_URL = 'https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN';
 
function normalizeHandle(handle) {
  return String(handle ?? '').replace(/^@/, '');
}
 
function postLabel(tweet) {
  if (tweet.author.platform === 'binance_square') return 'Binance Square post';
  if (tweet.author.platform === 'truth_social') return 'Truth';
  return 'X post';
}
 
function postUrl(tweet, handle) {
  if (tweet.author.platform === 'binance_square') {
    return /^\d+$/.test(tweet.tweetId)
      ? `https://www.binance.com/en/square/post/${tweet.tweetId}`
      : undefined;
  }
  if (tweet.author.platform === 'truth_social') {
    return tweet.link ?? `https://truthsocial.com/@${handle}/posts/${tweet.tweetId}`;
  }
  return tweet.link ?? `https://x.com/${handle}/status/${tweet.tweetId}`;
}
 
async function sendPostAlert(tweet) {
  const handle = normalizeHandle(tweet.author.handle);
  const embed = {
    title: postLabel(tweet),
    description: tweet.text,
    color: 3447003, // Blue
    url: postUrl(tweet, handle),
    author: { name: handle ? `@${handle}` : postLabel(tweet) },
    fields: [],
    footer: { text: 'TweetStream Alert' },
    timestamp: new Date().toISOString(),
  };
 
  // Add detected tokens as fields
  if (tweet.tokens?.length) {
    for (const token of tweet.tokens) {
      embed.fields.push({
        name: token.symbol,
        value: `$${token.price} | ${token.chain}`,
        inline: true,
      });
    }
  }
 
  const response = await fetch(WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ embeds: [embed] }),
  });
 
  if (response.status === 429) {
    const retryAfter = response.headers.get('Retry-After');
    console.log(`Rate limited. Retry after ${retryAfter}s`);
  }
}

Step 4: Send alerts with Python

python
import requests
import time
from typing import Optional
 
WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
 
def normalize_handle(handle: object) -> str:
    return str(handle or "").removeprefix("@")
 
def post_label(tweet: dict) -> str:
    platform = tweet.get("author", {}).get("platform", "twitter")
    if platform == "binance_square":
        return "Binance Square post"
    if platform == "truth_social":
        return "Truth"
    return "X post"
 
def post_url(tweet: dict, handle: str) -> Optional[str]:
    platform = tweet.get("author", {}).get("platform", "twitter")
    if platform == "binance_square":
        post_id = str(tweet["tweetId"])
        return f"https://www.binance.com/en/square/post/{post_id}" if post_id.isascii() and post_id.isdigit() else None
    if platform == "truth_social":
        return tweet.get("link") or f"https://truthsocial.com/@{handle}/posts/{tweet['tweetId']}"
    return tweet.get("link") or f"https://x.com/{handle}/status/{tweet['tweetId']}"
 
def send_post_alert(tweet: dict) -> None:
    handle = normalize_handle(tweet.get("author", {}).get("handle"))
    embed = {
        "title": post_label(tweet),
        "description": tweet["text"],
        "color": 3447003,  # Blue
        "url": post_url(tweet, handle),
        "author": {"name": f"@{handle}" if handle else post_label(tweet)},
        "fields": [],
        "footer": {"text": "TweetStream Alert"},
    }
 
    # Add detected tokens
    for token in tweet.get("tokens", []):
        embed["fields"].append({
            "name": token["symbol"],
            "value": f"${token['price']} | {token['chain']}",
            "inline": True,
        })
 
    payload = {"embeds": [embed]}
    resp = requests.post(WEBHOOK_URL, json=payload)
 
    if resp.status_code == 429:
        retry_after = resp.json().get("retry_after", 1)
        time.sleep(retry_after)
        requests.post(WEBHOOK_URL, json=payload)

Rich embed formatting for trading alerts

Discord embeds support extensive formatting. Here are the key fields for trading alerts:

  • title — The alert headline (e.g., account name or token symbol)
  • description — The tweet text or alert body
  • color — Decimal color value (green: 3066993, red: 15158332, blue: 3447003)
  • fields — Name/value pairs for structured data (token, price, chain)
  • footer — Timestamp or source attribution
  • thumbnail — Small image (e.g., token logo or user avatar)
  • url — Link back to the original tweet

Discord webhook rate limits

Discord enforces webhook rate limits and returns HTTP 429 with a Retry-After value when you send too quickly. Exact limits can vary by route and context, so build around the response headers instead of hard-coding one universal number.

For high-volume alert channels, implement these strategies:

  • Queue messages and send in batches rather than one-at-a-time
  • Use multiple webhooks across different channels for different alert types
  • Respect Retry-After when you receive 429 responses
  • Bundle multiple alerts into a single message using multiple embeds (up to 10 per message)
  • Treat response headers as the source of truth for retry timing

Troubleshooting common issues

  • Webhook returns 401 Unauthorized — The webhook URL has been deleted or regenerated. Create a new webhook.
  • Messages not appearing — Check that the webhook is assigned to the correct channel and the bot has Send Messages permission.
  • Embeds not rendering — Verify your JSON structure. The embeds field must be an array, even for a single embed. Color must be a decimal number, not hex.
  • Rate limited (429) — Slow down your request rate. Queue messages and implement backoff.
  • Content too long — Embed descriptions are limited to 4,096 characters. Total embed content across all embeds must not exceed 6,000 characters.

TweetStream Discord delivery

If you do not want to build and maintain a webhook pipeline yourself, TweetStream delivers alerts directly to Discord with zero code. Configure your accounts and webhooks in the dashboard, and TweetStream handles filtering, enrichment (OCR, token detection, live prices), formatting, and delivery. See the Discord crypto alerts page for setup details.

Put this workflow into production faster

This guide covers the workflow. TweetStream handles monitored-post ingestion, token detection, and OCR, then delivers posts, updates, deletes, pins, profile changes, follows, unfollows, and enriched JSON payloads over WebSocket. Supported plans also include history. Your client reconnects the WebSocket as documented and routes received events downstream. Start a 3-day trial with your own accounts.

Start 3-day trial

Frequently asked questions

What is the rate limit for Discord webhooks?
Discord can rate-limit webhook traffic and returns Retry-After guidance when you hit the limit. Parse the response headers instead of relying on a fixed copied number.
Do I need a Discord bot for webhook alerts?
No. Webhooks are simpler than bots: you POST JSON to a URL, with no bot account, OAuth, or gateway connection. Bots add features such as interactive buttons and slash commands.
Can I send images in Discord webhook messages?
Yes. Use the image field in an embed to display a full-width image, or thumbnail for a smaller image. You can also attach files directly using multipart/form-data.
How do I color-code alerts by type?
Set the color field in your embed to a decimal color value. Common choices: green (3066993) for buys, red (15158332) for sells, blue (3447003) for info. Convert hex to decimal with parseInt('0x3498db', 16).
Can I filter which tweets go to which Discord channel?
With a custom setup, you route alerts programmatically based on content. With TweetStream, you can send specific accounts to their own Discord webhook URLs in the dashboard and add keyword filters that skip posts containing terms you do not want.

Test it on your own watchlist

Add up to 5 accounts you trade on, connect one WebSocket, and compare the alerts with what you see today.

3-day trial, then $199/mo · 5 tracked accounts · 1 WebSocket