twitterapi.io is an independent third-party service. Not affiliated with X Corp.

Blogtwitter alerts for keywords

Twitter (X) Alerts for Keywords — A Developer's Guide

By Alex Chen6 min read

A keyword-alert system for Twitter (X) is one of the most common dev workflows around the API — track a brand mention, a campaign hashtag, a competitor reference, or a market-moving phrase, and route matching tweets to your team in real time. Dashboard tools sell this as a polished UI; the dev path is direct API + your own delivery integration, which costs a few dollars a month at meaningful volumes.

This guide walks the full production pattern: pick the API path, structure the polling loop, dedupe tweet IDs, score each match against alert criteria, and deliver via email or webhook. Runnable Python end-to-end with per-call cost from each provider's published pricing page. Most top SERP results on this query are surface-level walkthroughs; this one is the code-level recipe.

01 — Section

The five-step pattern — keyword → poll → dedupe → score → deliver

Every Twitter keyword-alert system, no matter the UI on top, decomposes into the same five steps:

1. Define the keyword(s) — One term, a phrase, a boolean expression (OR between alternatives, - for negation), with optional engagement / language / date filters baked into the query.

2. Poll the search API — Hit /twitter/tweet/advanced_search (twitterapi.io) or /2/tweets/search/recent (X official) on a cadence. Every 5 minutes is the typical baseline.

3. Dedupe by tweet ID — Every successive poll may return the same tweet again. Maintain a seen set of IDs; skip any ID you've already processed.

4. Score against alert criteria — Each new tweet is evaluated against your filter (minimum engagement, sentiment threshold, author whitelist, regex on the body). Matches advance to step 5.

5. Deliver — Push the match through your delivery channel: email (SES, SendGrid), webhook (Slack, Discord, custom HTTP), or push notification (your mobile app). Track delivery state so failures don't double-fire.

02 — Section

twitterapi.io's advanced_search accepts the full X advanced-search expression as the query parameter. Auth is X-API-Key header. Pricing per twitterapi.io/pricing: $0.00015 per returned tweet.

Pick this for any meaningful polling workload — the per-call cost compounds favorably as you scale the number of tracked keywords.

python
import os, requests, json, time

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"

def poll_keyword(keyword: str, min_faves: int = 10):
    """Single poll for matches on this keyword."""
    r = requests.get(
        f"{BASE}/twitter/tweet/advanced_search",
        headers=HEADERS,
        params={"query": f"{keyword} min_faves:{min_faves}", "queryType": "Latest"},
        timeout=15,
    )
    r.raise_for_status()
    return r.json().get("tweets", [])

seen = set()
KEYWORD = "vector database"

while True:
    matches = poll_keyword(KEYWORD, min_faves=10)
    new = [t for t in matches if t["id"] not in seen]
    for t in new:
        seen.add(t["id"])
        # Deliver via your preferred channel (email / webhook / Slack)
        print(f"\u26a0 alert: {t.get('text', '')[:140]}")
    time.sleep(300)  # 5 min
03 — Section

Path 2 — X official `/2/tweets/search/recent`

X's official search endpoint accepts the same operator set. Auth is a bearer token from the X Developer Console. Pricing per docs.x.com/x-api/getting-started/pricing: $0.005 per post read, with 24h UTC dedup window.

Pick this when you're already on the X bill (you also write or read other surfaces); marginal alert-poll cost rides on the same auth.

python
# pip install tweepy
import tweepy

client = tweepy.Client(bearer_token="YOUR_X_BEARER")
seen = set()
KEYWORD = "vector database"

import time
while True:
    resp = client.search_recent_tweets(
        query=f"{KEYWORD} min_faves:10",
        max_results=100,
        tweet_fields=["created_at", "public_metrics", "author_id"],
    )
    for t in resp.data or []:
        if t.id in seen:
            continue
        seen.add(t.id)
        # Deliver via your channel
        print(f"\u26a0 alert: {t.text[:140]}")
    time.sleep(300)  # 5 min
04 — Section

Scoring — what 'matches your criteria' actually means

Raw matches from the API are a stream of tweets containing the keyword. Most production alert systems add a scoring layer before delivery to keep the signal-to-noise ratio high:

Engagement thresholdmin_faves:N in the query handles most cases. For finer control, post-fetch filter on public_metrics.like_count / retweet_count / reply_count against your own threshold.

Author whitelist or blacklist — Restrict to accounts you trust (from:userA OR from:userB) or exclude accounts you don't (-from:botC). Build the operator into the query if the list is small; client-side filter if it's larger.

Sentiment — Pass the tweet text to an LLM (OpenAI, Anthropic) with a sentiment-classifier prompt. Cost depends on the classifier; typical cents per 100 tweets via gpt-4-class models. Skip negatively-scored matches if your use case is opportunity-detection.

Body regex / structured match — If you need to extract a number (price, URL, ticker symbol) from the tweet, regex-match before delivering. Forward only structured-match successes.

05 — Section

Delivery channels — email, webhook, Slack, push

Three common patterns for routing matches to your audience:

Email — Amazon SES is cost-efficient ($0.10 per 1,000 emails per docs.aws.amazon.com/ses/), SendGrid is friendlier for templating. Batch matches into one email per N minutes for non-urgent alerts; one email per match for urgent.

Webhook — POST the tweet record (or your summary) to a Slack incoming webhook, a Discord webhook, or your own HTTP endpoint. Slack's per-channel rate limit is documented at api.slack.com — bursting too fast triggers throttling.

Push notification — Your mobile app's push provider (Apple APNs, Google FCM, OneSignal). Useful for in-app product features rather than bulk alerts.

Pick by your audience's preference — many production systems run all three in parallel for redundancy.

06 — Section

Side-by-side comparison — 3 alert architectures

Same pattern (alert when keyword matches) framed across three architectural choices. Per-call costs derived from each provider's published pricing.

Dimensiontwitterapi.ioX officialDashboard tool (Brand24 / Mention)
Per-tweet cost$0.00015 (twitterapi.io/pricing)$0.005 (docs.x.com)bundled in monthly tier
Setup time~30 min (sign up + key + Python loop)~1 hour (X Dev account + bearer)UI walkthrough in 10 min
Custom scoringfull (your code)full (your code)tier-bundled limited options
Custom deliveryfull (any channel)full (any channel)provider-bundled webhooks + email
Best fordev-product embedding, cost-sensitivealready-on-X-bill workloadsnon-dev stakeholders, plug-and-play

Three patterns: (a) per-call cost compounds — at any non-trivial volume the twitterapi.io path is materially cheaper, derivable from cited rates; (b) dashboard tools win on plug-and-play but lose on custom scoring + delivery flexibility; (c) most production teams run a self-built alert system because the scoring rules are domain-specific and not in any dashboard's UI.

07 — Section

Picking a path — the decision rule

Building keyword alerts as a product feature inside your own app? → twitterapi.io + your own scoring + your own delivery. The cost structure makes per-customer alerts economically viable.

Already on the X bill, alerts are a side-feature? → X official; marginal cost rides on the same auth.

Need plug-and-play UI for a non-technical team? → dashboard tool (Brand24, Mention, etc.). Pay the seat fee, get a polished UI; lose the custom-scoring + custom-delivery flexibility.

Most dev-built systems are a hybrid: twitterapi.io for the polling layer + custom code for scoring + your existing email/webhook infrastructure. Total monthly bill for moderate-volume tracking ($0.00015/tweet × ~10K tracked tweets per month) is under $2 in API credits.

python
# Production-ready alert system: poll, dedupe, score, deliver.
import os, requests, json, time, hashlib
from datetime import datetime, timezone

HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"

class KeywordAlertSystem:
    def __init__(self, keyword: str, min_faves: int = 10):
        self.keyword = keyword
        self.min_faves = min_faves
        self.seen = set()
        # Persist seen IDs to disk so a restart doesn't re-alert
        self.state_file = f"alert_state_{hashlib.md5(keyword.encode()).hexdigest()[:8]}.json"
        self._load_state()

    def _load_state(self):
        try:
            with open(self.state_file) as f:
                self.seen = set(json.load(f))
        except FileNotFoundError:
            pass

    def _save_state(self):
        with open(self.state_file, "w") as f:
            json.dump(list(self.seen)[-10000:], f)  # cap memory

    def poll(self):
        r = requests.get(
            f"{BASE}/twitter/tweet/advanced_search",
            headers=HEADERS,
            params={"query": f"{self.keyword} min_faves:{self.min_faves}"},
            timeout=15,
        )
        r.raise_for_status()
        return r.json().get("tweets", [])

    def score(self, tweet) -> bool:
        """Replace with your real scoring rule."""
        pm = tweet.get("public_metrics", {})
        return pm.get("like_count", 0) >= self.min_faves * 5

    def deliver(self, tweet):
        """Replace with your channel (SES, Slack webhook, etc.)."""
        ts = datetime.now(timezone.utc).isoformat()
        print(f"[{ts}] \u26a0 {self.keyword} alert: {tweet.get('text', '')[:140]}")

    def run(self, cadence_sec: int = 300):
        while True:
            try:
                matches = self.poll()
                for t in matches:
                    if t["id"] in self.seen:
                        continue
                    self.seen.add(t["id"])
                    if self.score(t):
                        self.deliver(t)
                self._save_state()
            except Exception as e:
                print(f"poll error: {e}")
            time.sleep(cadence_sec)

if __name__ == "__main__":
    KeywordAlertSystem("vector database", min_faves=20).run()

# Cost framing (math from cited pricing pages):
#   5-min poll, 24h/day = 288 calls/day per tracked keyword
#   Assume ~5 tweets returned per call = 1,440 tweets/day per keyword
#   twitterapi.io: 1,440 × $0.00015 = $0.216/day = ~$6.50/mo per keyword
#   X official:    1,440 × $0.005   = $7.20/day  = ~$216/mo per keyword
# Track 100 keywords → $650/mo via twitterapi.io vs $21,600/mo via X.
08 — Questions

Questions readers ask

How often should I poll the search API?

5 minutes is the practical baseline. X tweets surface within seconds, but the search API returns the same recent set if you poll faster — so polling every minute mostly returns duplicates. For urgent events (market-moving keywords), poll every 1-2 min; for routine monitoring, every 5-15 min.

Do I need a separate poller per keyword, or can I batch?

Boolean OR in the query batches keywords: "vector database" OR "vector search" OR "embedding model". Each unique returned tweet is one billable unit. Group by which keyword matched client-side. For tracking many keywords this is materially cheaper than separate polls.

How do I avoid duplicate alerts on restart?

Persist the seen set of tweet IDs to disk (or a small database). The example in the code section saves to a JSON file keyed by keyword hash; in production use Postgres / Redis / SQLite. Cap the size to avoid unbounded growth — IDs older than the search window can be evicted.

What's a reasonable scoring rule for high-signal alerts?

It depends on your domain. Common starters: like_count >= 100 for genuinely-engaged matches, verified=true for high-trust authors, or LLM sentiment-classify with a threshold. For market-moving topics use velocity (tweet appears at engagement rate > X likes/min). Tune against your own captured data — calibrate against past examples where the alert would have been useful.

Can I deliver to Slack and email simultaneously?

Yes. The deliver() step can call multiple downstream channels in sequence. Common pattern: write the match to a single source-of-truth table, then run a separate worker that picks up unprocessed rows and dispatches to each subscribed channel. Decoupling channels from polling reduces blast radius if one delivery system is down.

What happens at scale — 1,000 keywords, 24h/day?

Math from cited pricing. twitterapi.io: 1,000 × $6.50/mo = $6,500/mo (and you'd batch many keywords per poll via OR queries to reduce this). X official: $216,000/mo at same volume — typically infeasible. At this scale your bill is dominated by the polling layer, not delivery; pick the API path on cost-per-call alone.

09 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key
    Twitter (X) Alerts for Keywords — Dev Guide | TwitterAPI.io