Published May 18, 2026. Last verified May 18, 2026.

Table of contents

Key takeaway

Real-time Twitter monitoring works by polling a REST endpoint at a short interval (typically 5 to 30 seconds), comparing returned tweet IDs against the last seen ID, and dispatching new tweets to your alert pipeline. For multi-account tracking, batch up to 5,000 accounts into a single X List and poll the list endpoint, cutting request volume by orders of magnitude.

Why real-time Twitter data got harder after 2023

Real-time Twitter monitoring used to mean one thing: open a persistent connection to the filtered stream, define a few rules, and let tweets flow into your application as they were posted. After the 2023 API pricing overhaul, that path costs $5,000 per month and requires a corporate-grade integration. For most teams (indie developers, brand monitoring squads, trading bots, news desks, lead-gen agencies), that price tag killed the project.

This guide walks through the alternative: a pull-based real-time monitoring pattern built on top of the Sorsa API, an alternative Twitter/X REST API that delivers fresh tweet data on every request, supports 20 requests per second per key, and lets you batch up to 5,000 accounts into a single List call. With response times around 300ms and the right endpoint choice, you can detect a new tweet within seconds of it being posted without managing OAuth flows, persistent sockets, or reconnection logic.

A quick note on terminology: this article uses "polling" deliberately. It is not a euphemism for "delayed." Done right, a polling loop returns a tweet within the interval you choose plus the API response time. If your loop runs every 5 seconds against a 300ms endpoint, your worst-case latency is roughly 5.3 seconds. That matches or beats most consumer-tier streaming products and crucially, it works on a flat-rate REST budget. For background on how this approach compares to the official API, see our Twitter API alternative overview.

Polling vs streaming: which approach fits your case?

Two ways to get data from a social platform: push-based (streaming, webhooks) or pull-based (polling). Most engineers default to streaming because it sounds faster. In practice, the choice depends on three things: how many things you are monitoring, your latency budget, and your appetite for connection state.

FactorPolling (REST)Streaming (WebSocket / filtered stream)
Latency to first tweetInterval + API response timeConnection latency, usually 1 to 3 seconds
Setup complexityOne API call in a loopPersistent connection, reconnect logic, backpressure handling
AuthenticationAPI key in a headerOAuth or token rotation
Recovery on crashResume from saved cursorReconnect, replay buffer, dedup window
Cost modelPer requestPer stream tier (often flat, expensive)
Best for1 to 5,000 monitored targets, alerts with 1 to 30s toleranceSub-second latency, full-firehose ingestion, trading at millisecond scale

Streaming wins when you need millisecond-level alerts on a large keyword space and you have engineering capacity to handle reconnects, gap recovery, and rule management. For everything else (brand listening, news monitoring, lead generation, compliance alerts, mid-frequency trading signals, content moderation), polling is simpler, cheaper, and good enough.

The other underrated advantage of polling: if your script crashes, it picks up where it left off on the next cycle. Streaming pipelines need a separate replay buffer to handle outages without data loss. Sorsa does not ship a managed webhook product on purpose; the pull pattern keeps the control plane in your hands, and the 20-requests-per-second rate limit gives enough headroom for production-scale loops.

Pick the right endpoint for what you're monitoring

The first design decision is choosing the endpoint that matches the shape of your target. Four endpoints cover virtually every real-time monitoring need.

Monitoring targetEndpointMethodWhy
A single account/user-tweetsPOSTReturns the latest tweets from one user's timeline
Up to 5,000 accounts at once/list-tweetsGETOne request covers every member of an X List
A keyword, hashtag, or search query/search-tweetsPOSTFull operator syntax, supports order: latest for chronological results
@mentions of a specific handle/mentionsPOSTPurpose-built for mention tracking with engagement filters

The endpoint that surprises most teams is /list-tweets. Putting 50 accounts into a single List and polling the list endpoint is a 50x cost reduction compared to polling each account individually. The same pattern scales to 500 or 5,000 accounts with no change in request volume. We cover the Lists and Communities endpoints in the docs, and the X Lists product page on x.com is where you actually create the list.

Level 1: Track a single account

The simplest case. You want to know the moment one account posts: a competitor, a CEO, a regulator, an influencer. Useful for low-volume monitoring or for testing your pipeline before scaling up.

Python

python
import requests
import time

API_KEY = "YOUR_API_KEY"
USERNAME = "elonmusk"
POLL_INTERVAL = 5  # seconds

URL = "https://api.sorsa.io/v3/user-tweets"
HEADERS = {"ApiKey": API_KEY, "Content-Type": "application/json"}

last_seen_id = None

print(f"Monitoring @{USERNAME}...")

while True:
    try:
        resp = requests.post(URL, headers=HEADERS, json={"username": USERNAME})
        resp.raise_for_status()
        tweets = resp.json().get("tweets", [])

        if tweets:
            # Tweet IDs are Snowflake strings. Cast to int for safe comparison
            # since lexicographic order can break across ID length boundaries.
            top_id = int(tweets[0]["id"])

            if last_seen_id is None:
                last_seen_id = top_id
                print(f"Baseline set: {last_seen_id}")
            else:
                new_tweets = [t for t in tweets if int(t["id"]) > last_seen_id]
                # Print in chronological order (oldest first).
                for tweet in reversed(new_tweets):
                    print(f"[NEW] @{USERNAME}: {tweet['full_text'][:140]}")
                if new_tweets:
                    last_seen_id = top_id

    except requests.exceptions.RequestException as e:
        print(f"Request error: {e}")
        time.sleep(POLL_INTERVAL * 2)
        continue

    time.sleep(POLL_INTERVAL)

JavaScript

javascript
const API_KEY = "YOUR_API_KEY";
const USERNAME = "elonmusk";
const POLL_INTERVAL = 5000;

let lastSeenId = null;
console.log(`Monitoring @${USERNAME}...`);

while (true) {
  try {
    const resp = await fetch("https://api.sorsa.io/v3/user-tweets", {
      method: "POST",
      headers: { "ApiKey": API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ username: USERNAME }),
    });
    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);

    const tweets = (await resp.json()).tweets || [];

    if (tweets.length > 0) {
      // BigInt comparison avoids precision loss on 64-bit Snowflake IDs.
      const topId = BigInt(tweets[0].id);

      if (lastSeenId === null) {
        lastSeenId = topId;
        console.log(`Baseline set: ${lastSeenId}`);
      } else {
        const newTweets = tweets.filter((t) => BigInt(t.id) > lastSeenId);
        for (const t of [...newTweets].reverse()) {
          console.log(`[NEW] @${USERNAME}: ${t.full_text.slice(0, 140)}`);
        }
        if (newTweets.length) lastSeenId = topId;
      }
    }
  } catch (err) {
    console.error(`Error: ${err.message}`);
    await new Promise((r) => setTimeout(r, POLL_INTERVAL * 2));
    continue;
  }
  await new Promise((r) => setTimeout(r, POLL_INTERVAL));
}

Two things worth noting in the code above. First, tweet IDs are Snowflake values and arrive as strings. Comparing them as strings works inside a single time window but is fragile across ID length boundaries; cast to int in Python or BigInt in JavaScript. Second, the loop establishes a baseline on the first successful call rather than dumping the entire timeline. That avoids a spam burst on startup.

This works, but it scales poorly. Monitoring 50 accounts means 50 separate polling loops and 50x the API requests. That is where X Lists come in.

Level 2: Track up to 5,000 accounts in one request

X Lists are the most useful and most underused tool for multi-account monitoring. A List is a public group of accounts (up to 5,000), and /list-tweets returns the merged latest tweets across all members in a single request. Build a List once, point your poller at it, and you have effectively built your own custom firehose without paying for the official one.

Step 1: create a public X List

  1. Go to X Lists and create a new list.
  2. Add the accounts you want to monitor (up to 5,000).
  3. Set the list to Public. Private lists are not accessible via the API.
  4. Copy the List ID from the URL. For https://x.com/i/lists/1234567890 the ID is 1234567890.

Step 2: poll the list

python
import requests
import time

API_KEY = "YOUR_API_KEY"
LIST_ID = "YOUR_LIST_ID"
POLL_INTERVAL = 5

URL = f"https://api.sorsa.io/v3/list-tweets?list_id={LIST_ID}"
HEADERS = {"ApiKey": API_KEY, "Accept": "application/json"}


def monitor_list(callback, interval=POLL_INTERVAL):
    """Poll an X List and call `callback` for each new tweet detected."""
    last_seen_id = None
    print(f"Monitoring List {LIST_ID} (interval: {interval}s)")

    while True:
        try:
            resp = requests.get(URL, headers=HEADERS, timeout=10)
            resp.raise_for_status()
            tweets = resp.json().get("tweets", [])

            if not tweets:
                time.sleep(interval)
                continue

            top_id = int(tweets[0]["id"])

            if last_seen_id is None:
                last_seen_id = top_id
                print(f"Baseline set: {last_seen_id}")
            else:
                new_tweets = [t for t in tweets if int(t["id"]) > last_seen_id]
                if new_tweets:
                    for tweet in reversed(new_tweets):
                        callback(tweet)
                    last_seen_id = top_id

        except requests.exceptions.RequestException as e:
            print(f"Request error: {e}. Retrying in {interval * 2}s")
            time.sleep(interval * 2)
            continue

        time.sleep(interval)


def on_new_tweet(tweet):
    user = tweet["user"]
    print(f"[NEW] @{user['username']}: {tweet['full_text'][:120]}")
    print(
        f"       Likes: {tweet.get('likes_count', 0)} | "
        f"RTs: {tweet.get('retweet_count', 0)} | "
        f"Views: {tweet.get('view_count', 'N/A')}\n"
    )


if __name__ == "__main__":
    monitor_list(on_new_tweet)

The efficiency gain is dramatic. Monitoring 50 accounts individually at a 10-second interval costs 50 * 8,640 = 432,000 requests per day. Putting those same 50 accounts into one X List and polling /list-tweets costs 8,640 requests per day. That is a 50x reduction with no loss of coverage. For more patterns like this, see the optimizing API usage docs.

One gotcha: /list-tweets returns up to 20 tweets per page. If your List members tweet so frequently that more than 20 new tweets arrive within one poll interval, you can miss some. Two fixes: drop the interval to 2 to 3 seconds, or paginate via next_cursor until you reach a previously seen ID. For most use cases (brand monitoring, news desks, audience research), 20 tweets per 5 to 10 seconds is more than enough headroom.

Level 3: Track keywords, hashtags, and search queries

Account-based monitoring catches what known sources say. Keyword-based monitoring catches what anyone says about your topic. Use /search-tweets with order: "latest" to get chronological results.

python
import requests
import time

API_KEY = "YOUR_API_KEY"
QUERY = '"your brand" OR @yourbrand lang:en'
POLL_INTERVAL = 10

URL = "https://api.sorsa.io/v3/search-tweets"
HEADERS = {"ApiKey": API_KEY, "Content-Type": "application/json"}


def monitor_keyword(query, callback, interval=10):
    last_seen_id = None
    print(f"Monitoring: {query} (interval: {interval}s)")

    while True:
        try:
            resp = requests.post(
                URL,
                headers=HEADERS,
                json={"query": query, "order": "latest"},
                timeout=10,
            )
            resp.raise_for_status()
            tweets = resp.json().get("tweets", [])

            if tweets:
                top_id = int(tweets[0]["id"])
                if last_seen_id is None:
                    last_seen_id = top_id
                    print(f"Baseline set: {last_seen_id}")
                else:
                    new_tweets = [t for t in tweets if int(t["id"]) > last_seen_id]
                    for tweet in reversed(new_tweets):
                        callback(tweet)
                    if new_tweets:
                        last_seen_id = top_id

        except requests.exceptions.RequestException as e:
            print(f"Error: {e}")
            time.sleep(interval * 2)
            continue

        time.sleep(interval)

The real power lives in the query string. The API supports the full Twitter Advanced Search operator set (an unofficial reference is on igorbrigadir/twitter-advanced-search). For example, to track high-engagement English-language mentions of your brand and skip retweets:

python
monitor_keyword('"your brand" min_faves:10 lang:en -filter:retweets', on_new_tweet)

A few operators that earn their keep in real-time monitoring:

  • min_faves:N, min_retweets:N to filter for already-trending content.
  • -filter:retweets, -filter:replies to drop noise.
  • from:user1 OR from:user2 to monitor a handful of accounts without a List.
  • (keyword1 OR keyword2) (problem OR issue OR broken) to catch sentiment-loaded mentions.
  • near:"san francisco" within:25mi for geo-bounded monitoring.

If your query string starts to feel unwieldy, the search builder playground lets you construct one visually. Full operator coverage lives in the search operators reference.

Push new tweets to Slack, Discord, or any HTTP endpoint

The polling loop is the producer. The callback is where you decide what happens to each new tweet. Because the callback is just a function, the same monitor can route to anything that speaks HTTP. Slack first because it is the most common destination, then a few quick variants.

Slack via Incoming Webhook

python
import requests

SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"


def send_to_slack(tweet):
    user = tweet["user"]
    text = (
        f"*New tweet from @{user['username']}*\n"
        f"{tweet['full_text']}\n"
        f"Likes: {tweet.get('likes_count', 0)} | "
        f"RTs: {tweet.get('retweet_count', 0)} | "
        f"Views: {tweet.get('view_count', 'N/A')}\n"
        f"https://x.com/{user['username']}/status/{tweet['id']}"
    )
    requests.post(SLACK_WEBHOOK_URL, json={"text": text})


# Plug into any monitor:
monitor_list(send_to_slack)
# or: monitor_keyword("bitcoin lang:en min_faves:50", send_to_slack)

Set up the Slack Incoming Webhook URL in your Slack app settings (official Slack docs). Same pattern for Discord, Telegram, or any internal endpoint.

Discord

python
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR/WEBHOOK"


def send_to_discord(tweet):
    user = tweet["user"]
    content = (
        f"**@{user['username']}** just tweeted:\n"
        f"{tweet['full_text']}\n"
        f"https://x.com/{user['username']}/status/{tweet['id']}"
    )
    requests.post(DISCORD_WEBHOOK_URL, json={"content": content})

Discord webhook setup is documented at discord.com/developers/docs/resources/webhook.

Telegram

python
TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID = "YOUR_CHAT_ID"


def send_to_telegram(tweet):
    user = tweet["user"]
    text = (
        f"New tweet from @{user['username']}\n\n"
        f"{tweet['full_text']}\n\n"
        f"https://x.com/{user['username']}/status/{tweet['id']}"
    )
    requests.post(
        f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage",
        json={"chat_id": TELEGRAM_CHAT_ID, "text": text},
    )

Any custom HTTP endpoint

python
def send_to_internal_api(tweet):
    requests.post(
        "https://internal.example.com/events/twitter",
        json={
            "tweet_id": tweet["id"],
            "username": tweet["user"]["username"],
            "text": tweet["full_text"],
            "metrics": {
                "likes": tweet.get("likes_count", 0),
                "retweets": tweet.get("retweet_count", 0),
                "views": tweet.get("view_count", 0),
            },
            "url": f"https://x.com/{tweet['user']['username']}/status/{tweet['id']}",
        },
        headers={"Authorization": "Bearer YOUR_INTERNAL_TOKEN"},
        timeout=5,
    )

What you have built is, in effect, your own webhook relay. Sorsa provides the data; your callback decides who hears about each new tweet. The advantage of owning that relay yourself is that you keep all routing rules in your code (filtering, rate limiting, fan-out to multiple channels, retry policy) rather than living inside a vendor dashboard.

How often should you poll, and what does it cost?

Every cycle of your loop costs one request. The interval you choose directly drives your monthly usage.

IntervalRequests / hourRequests / dayRequests / 30 days
1 second3,60086,4002,592,000
5 seconds72017,280518,400
10 seconds3608,640259,200
30 seconds1202,88086,400
1 minute601,44043,200

A few practical guidelines from running these loops in production:

  • Brand listening, news monitoring, lead gen: 10 to 30 seconds is plenty. You catch any new tweet within half a minute of posting, and the monthly footprint is small.
  • Financial signal detection, breaking news bots, trading workflows: 1 to 5 seconds. You will burn through more requests but the latency budget justifies it.
  • Compliance, audit, slow-moving research: 1 to 5 minutes. Real-time is overkill for use cases where the action window is measured in hours.

For a full breakdown of how requests translate to dollars across plans, see our Twitter API pricing article. The short version: a 10-second interval on a single list endpoint fits comfortably inside the Pro tier, and a 1-second loop on a single high-priority target still costs well under what the official API charges for any kind of streaming access.

Production hardening: five things to fix before going live

The examples above are deliberately minimal. Before pointing one of these at production traffic, address these five concerns.

1. Persist last_seen_id across restarts

If your script crashes and restarts without remembering its checkpoint, two things go wrong: it either reprocesses old tweets (duplicate alerts to your Slack channel) or sets a fresh baseline and silently misses the gap. Store the checkpoint to a file, Redis, or your database.

python
import json
import os

STATE_FILE = "monitor_state.json"


def load_state():
    if os.path.exists(STATE_FILE):
        with open(STATE_FILE) as f:
            return json.load(f).get("last_seen_id")
    return None


def save_state(last_seen_id):
    with open(STATE_FILE, "w") as f:
        json.dump({"last_seen_id": last_seen_id}, f)

Load on startup, save after every successful poll that updates the cursor.

2. Exponential backoff on errors

Network issues, transient 5xx responses, and rate-limit hits (HTTP 429) happen. Instead of retrying immediately and making things worse, back off gradually with a cap.

python
retry_delay = POLL_INTERVAL
MAX_DELAY = 60

while True:
    try:
        resp = requests.get(URL, headers=HEADERS, timeout=10)
        if resp.status_code == 429:
            print(f"Rate limited. Backing off {retry_delay}s")
            time.sleep(retry_delay)
            retry_delay = min(retry_delay * 2, MAX_DELAY)
            continue
        resp.raise_for_status()
        retry_delay = POLL_INTERVAL  # reset on success
        # process tweets
    except requests.exceptions.RequestException as e:
        print(f"Error: {e}")
        time.sleep(retry_delay)
        retry_delay = min(retry_delay * 2, MAX_DELAY)
        continue

    time.sleep(POLL_INTERVAL)

For a deeper dive on rate-limit handling specifically, see Twitter API rate limits in 2026.

3. Decouple polling from processing

Do not run expensive operations (sentiment scoring, database writes, external API calls, AI classification) synchronously inside the polling loop. If a downstream system slows down, your loop falls behind schedule and latency spikes. Push new tweets into a queue and process them in a separate worker.

python
from collections import deque
import threading

tweet_queue = deque()


def polling_loop():
    """Fast loop: poll and enqueue. No heavy work here."""
    # standard polling code, but instead of calling the callback directly:
    # tweet_queue.append(tweet)
    pass


def processing_worker():
    """Separate thread: dequeue and dispatch."""
    while True:
        if tweet_queue:
            tweet = tweet_queue.popleft()
            send_to_slack(tweet)
            save_to_database(tweet)
        else:
            time.sleep(0.1)


threading.Thread(target=processing_worker, daemon=True).start()
polling_loop()

For heavier workloads, swap the in-memory deque for Redis, RabbitMQ, SQS, or whatever message broker your stack already runs.

4. Health checks and monitoring the monitor

Log every poll cycle: timestamp, new tweet count, response time, errors. Alert if the monitor has not completed a successful poll in the last N minutes. Silent failures are the most expensive kind, especially in alerting pipelines where the absence of alerts means "nothing happened" until someone notices the data gap. You can check the API's operational status at the Sorsa status page to rule out platform issues before debugging your own code.

5. Handle edge cases that bite in production

  • Deleted tweets: if a tweet is deleted between when you fetched it and when your callback fires, the URL will 404. Treat this as expected, not an error.
  • Protected accounts: if a tracked user goes private, /user-tweets will return an empty list. Log and continue.
  • Pinned tweets: the first tweet in a /user-tweets response is often the pinned tweet, not the most recent. Sort by created_at if you care about strict chronological order.
  • Retweets vs original tweets: tweet["retweeted_status"] is populated for retweets. Decide whether you want both or only originals.
  • Rate-limited replies: is_replies_limited indicates the author restricted replies. Useful signal for some monitoring use cases.

A real migration story

When I worked with a SaaS company in early 2024 to rebuild their brand-monitoring pipeline, they had been running on the official filtered stream since 2018. Their original setup tracked roughly 200 keyword rules and 60 priority accounts, and it cost $5,000 per month after the new Pro tier kicked in. The internal pitch was: cut the stream, save the money, and hope nothing breaks.

The migration took two weeks. We replaced the keyword rules with two /search-tweets workers running at 15-second intervals (the rules merged into compound boolean queries with OR operators). We replaced the 60-account follow with a single X List and one /list-tweets loop at 10 seconds. Total monthly footprint dropped to about 350,000 requests, which fit on the Pro tier without strain. End-to-end alert latency went from roughly 2 seconds (filtered stream) to roughly 10 seconds at the 90th percentile. For their use case (PR team responding to brand mentions on Slack), the latency change was invisible. The bill change was not.

The migration playbook for similar setups lives in the migration from the official X API docs.

Use cases where this approach earns its keep

Five patterns I see most often.

Brand listening and social CRM. Poll /search-tweets for your brand handle plus product-name keywords, every 15 to 30 seconds. Route to Slack with sentiment hints baked into the message. The PR team responds within minutes.

News and signal detection. Build a List of breaking-news handles (Reuters, AP, Bloomberg, regional outlets, beat reporters) and poll it every 5 seconds. Fan out to a Discord server or trading dashboard. This is the cheapest version of a "news firehose" you can build in 2026.

Competitive intelligence. A List of competitor accounts plus their CEOs and product leads, polled every 30 seconds. New tweets land in a shared channel; your PMM team gets a free intel feed without anyone tabbing through 40 profiles.

Lead generation. Poll /search-tweets for problem-statement queries: "any recommendations for" (CRM OR analytics OR transcription), "looking for an alternative to", "we just churned from". Route to a Slack channel reviewed by sales. Most teams running this catch 5 to 15 qualified leads per week per query bucket.

Crypto KOL signals. Build a List of crypto influencers and project accounts, poll at 2 to 5 seconds, optionally combine with the Sorsa Score and crypto analytics endpoints to weight signals by audience quality.

Quick reality check vs the official X API

Disclosure: Sorsa is our product. We have tried to keep this comparison honest, but you should test any solution with your own workload before committing.

For full real-time streaming, the official X API Pro tier sits at $5,000 per month. On Sorsa, the same brand-monitoring pattern at a 10-second interval (one List, one keyword query) runs at well under $200 per month on the Pro plan. The trade-off is latency: filtered stream lands tweets within a second or two; polling lands them within your interval plus 300ms of API response time. For most monitoring use cases that gap is invisible. For sub-second trading bots, it is not. For a full pricing breakdown by use case, see Twitter API pricing in 2026.

FAQ

Is REST polling actually "real-time"?

It is near real-time. The latency budget is your polling interval plus the API response time (around 300ms on a fast REST endpoint). At a 5-second interval, your worst case is roughly 5.3 seconds from tweet posted to your callback firing. For 99% of monitoring use cases that meets the working definition of real-time. The 1% where it does not (millisecond trading, live event auctioning) needs a true stream regardless of vendor.

How many accounts can I monitor with one API key?

Practically unlimited via X Lists. A single List holds up to 5,000 accounts and counts as one /list-tweets request per poll. Multiple Lists work in parallel up to the 20 requests-per-second rate limit, which gives you headroom for hundreds of concurrent monitoring jobs.

What happens if I hit the rate limit?

You get an HTTP 429 response. Back off, retry, and the cycle continues; there is no penalty box. Most production pipelines never see 429s because 20 req/s is well above what a reasonable polling cadence demands.

How do I avoid duplicate alerts when my script restarts?

Persist the last seen tweet ID to durable storage (file, Redis, database) after each successful poll. On startup, load it and use it as your baseline. The persistence pattern in the production hardening section above shows the minimal version.

Can I monitor private or protected accounts?

No. Only public Twitter/X data is surfaced. If a tracked account goes private mid-monitoring, the endpoint returns an empty list and your loop continues without error.

Are managed webhooks supported?

Not today. The polling pattern in this guide is the supported real-time path. The upside is that you keep full control over routing logic, filtering, and fan-out; the downside is that you write a few lines of callback code yourself instead of configuring a vendor dashboard. For teams that want a fully-managed webhook product, the official X API offers Account Activity webhooks at enterprise tiers.

How fresh is the returned data?

Fresh on every request. There is no caching layer between you and the platform; if a tweet was posted half a second ago, your next poll picks it up. Combined with around 300ms response times, that is what makes the polling pattern work for monitoring rather than just analytics.

Can I combine real-time monitoring with historical backfill?

Yes, and most production pipelines do exactly that. Use the same endpoints (/user-tweets, /search-tweets, /list-tweets) with cursor-based pagination to walk backwards through history, then switch to the polling loop for new data going forward.

Getting started

To build your own real-time monitoring pipeline:

  1. Grab an API key from the Sorsa dashboard. One key works across all endpoints.
  2. Test interactively in the API playground: hit /user-tweets or /list-tweets with a known username or list ID and confirm you see live results.
  3. Copy one of the loops in this article (single account, List, or keyword) and replace the API key.
  4. Add a callback that routes to wherever you want alerts (Slack, Discord, internal API, queue).
  5. Add the production hardening (state persistence, backoff, decoupled processing) once the basic loop is stable.

Estimate your monthly usage from the interval table, pick a plan, and ship. If you need higher rate limits or volume above the standard plans, contact contacts@sorsa.io or drop into the Discord and we will work out a custom quota.


Daniel Kolbassen is a data engineer and API infrastructure consultant with 12+ years of experience building data pipelines around social media platforms. He has worked with the Twitter/X API since the v1.1 era and has helped over 40 companies restructure their data infrastructure after the 2023 pricing overhaul. He writes about APIs, scraping, and social data engineering. More at the author page.