FetchLayer fetchlayer.dev Sign in

Tutorial

How to Monitor Replies to a Tweet (Automated)

Build an automated pipeline to track replies on any X/Twitter thread. Get notified via webhooks, Slack, or Discord when new replies appear.

Written by Alex P.

  • twitter monitoring
  • tweet replies
  • X alerts
  • twitter automation
  • reply tracking

You launched a product, published an announcement, or someone is discussing your brand in a thread. You want to know who’s replying and what they’re saying — automatically.

Here’s how to build a reply monitor that checks any tweet on a schedule and alerts you when new replies come in.


The Architecture

Cron (every 2 min) → Fetch tweet replies → Filter new replies → Send alert

Step 1: Get Tweet Replies

The FetchLayer tweet-replies endpoint returns replies for any tweet by ID:

async function getReplies(tweetId) {
  const res = await fetch('https://api.fetchlayer.dev/twitter/tweet-replies', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FETCHLAYER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ tweetId })
  });
  const data = await res.json();
  return data.replies || [];
}

Why not use X’s official API? The official API charges $100/month minimum, rate-limits aggressively, and doesn’t provide reply threading on the Basic tier. FetchLayer gives you clean JSON with one API key and no OAuth ceremony.


Step 2: Track Seen Replies

import { readFileSync, writeFileSync, existsSync } from 'fs';

const SEEN_FILE = './seen-replies.json';

function loadSeen() {
  if (!existsSync(SEEN_FILE)) return new Set();
  return new Set(JSON.parse(readFileSync(SEEN_FILE, 'utf-8')));
}

function saveSeen(seen) {
  writeFileSync(SEEN_FILE, JSON.stringify([...seen].slice(-2000)));
}

Step 3: Filter and Alert

// Only alert on replies from verified accounts or with high engagement
function isNotable(reply) {
  return reply.author.isVerified || (reply.likeCount || 0) > 5;
}

async function sendAlert(reply, contextTweetId) {
  const tweetUrl = `https://x.com/i/status/${contextTweetId}`;

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `💬 New reply on tweet`,
      blocks: [
        {
          type: 'section',
          text: {
            type: 'mrkdwn',
            text: `*@${reply.author.handle}* ${reply.author.isVerified ? '✓' : ''} replied:\n>${reply.text.slice(0, 280)}\n<${tweetUrl}|View thread> · ${reply.likeCount} likes`
          }
        }
      ]
    })
  });
}

Step 4: Full Script

// monitor-replies.mjs
const TWEET_ID = process.argv[2]; // Pass tweet ID as argument

async function run() {
  if (!TWEET_ID) {
    console.error('Usage: node monitor-replies.mjs <tweetId>');
    process.exit(1);
  }

  const seen = loadSeen();
  const replies = await getReplies(TWEET_ID);
  const newReplies = replies.filter(r => !seen.has(r.id) && isNotable(r));

  for (const reply of newReplies) {
    await sendAlert(reply, TWEET_ID);
    seen.add(reply.id);
    console.log(`  → Alerted: @${reply.author.handle}`);
  }

  saveSeen(seen);
  console.log(`Checked: ${replies.length} replies, ${newReplies.length} new alerts`);
}

run().catch(console.error);

Step 5: Deploy

# Every 2 minutes, monitor replies to your launch tweet
*/2 * * * * cd /app && node monitor-replies.mjs "1942939879222220800" >> replies.log 2>&1

Advanced: Monitor Multiple Tweets

const WATCH_LIST = [
  { id: '1942939879222220800', label: 'Launch announcement' },
  { id: '1942939879222220801', label: 'Feature poll' },
];

async function runAll() {
  const seen = loadSeen();

  for (const { id, label } of WATCH_LIST) {
    const replies = await getReplies(id);
    const newReplies = replies.filter(r => !seen.has(r.id));

    for (const reply of newReplies) {
      await sendAlert(reply, id);
      seen.add(reply.id);
      console.log(`[${label}] Alerted: @${reply.author.handle}`);
    }
  }

  saveSeen(seen);
}

What’s Next