> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sorsa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Competitor Analysis

# How to Analyze Competitors on Twitter Using the API

This guide describes a complete workflow for analyzing competitors on X (formerly Twitter) using Sorsa API: profile benchmarking, content strategy decomposition, audience composition, public sentiment, and share of voice. Each phase maps to a specific endpoint, and the phases compose into a single weekly report you can run on a schedule.

All examples use Python 3.8+ with `requests`. Replace `YOUR_API_KEY` with your actual key in every snippet. The complete consolidated script with all helper functions is at the bottom of this page.

> **Free to start:** Every endpoint in this guide works on your first 100 requests: one-time, no credit card, no expiry. That is enough to trial the full workflow at shallow depth before choosing a plan.

> **Note:** For a narrative walkthrough with strategy context and worked examples, see [Twitter Competitor Analysis: A Developer's Guide](https://api.sorsa.io/blog/twitter-competitor-analysis) on the blog.

> **No-code option:** For ad-hoc side-by-side comparison without writing code, use the [Profile Comparison Tool](https://api.sorsa.io/playground/compare-users). It returns followers, engagement rate, average likes and retweets per tweet, posting frequency, and account age for any two handles. The [Engagement Calculator](https://api.sorsa.io/playground/engagement-calculator) covers per-tweet engagement-rate math for a single account.

***

## Setup

```python theme={null}
import requests
import time
import csv
from pathlib import Path
from datetime import date, datetime, timedelta, timezone

API_KEY = "YOUR_API_KEY"
BASE = "https://api.sorsa.io/v3"
HEADERS = {"ApiKey": API_KEY}
JSON_HEADERS = {**HEADERS, "Content-Type": "application/json"}
```

The `ApiKey` header authenticates every request. See [Authentication](https://docs.sorsa.io/authentication) for details.

***

## Phase 1: Profile Benchmarking

**Endpoints:** `GET /v3/info`, `GET /v3/info-batch`

Establish the baseline: follower count, tweet volume, account age, bio, verified status. Use [`/info-batch`](https://docs.sorsa.io/api-reference/users-data/user-profile-batch) to fetch up to 100 profiles in a single request, which counts as one request against your quota.

### Snapshot script

```python theme={null}
def get_profiles(usernames):
    """Fetch profiles for up to 100 accounts in a single API call."""
    resp = requests.get(
        f"{BASE}/info-batch",
        headers=HEADERS,
        params=[("usernames", u) for u in usernames],
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json().get("users", [])


competitors = ["stripe", "wise", "revolutapp"]
profiles = get_profiles(competitors)

print(f"{'Handle':<18} {'Followers':>12} {'Tweets':>10} {'Following':>10} {'Verified':>10}")
print("-" * 64)
for p in profiles:
    print(
        f"@{p['username']:<17} "
        f"{p['followers_count']:>12,} "
        f"{p['tweets_count']:>10,} "
        f"{p['followings_count']:>10,} "
        f"{str(p.get('verified', False)):>10}"
    )
```

### Tracking growth over time

A single snapshot has no analytical value. Log snapshots on a daily or weekly schedule (cron, GitHub Actions, etc.) and compute deltas:

```python theme={null}
def log_snapshot(profiles, output_file="snapshots.csv"):
    """Append today's snapshot to a running CSV log."""
    file_exists = Path(output_file).exists()
    today = date.today().isoformat()
    with open(output_file, "a", newline="") as f:
        writer = csv.writer(f)
        if not file_exists:
            writer.writerow(["date", "username", "followers", "tweets", "following"])
        for p in profiles:
            writer.writerow([
                today,
                p["username"],
                p["followers_count"],
                p["tweets_count"],
                p["followings_count"],
            ])


def compute_growth(csv_file, username, days=7):
    """Compute follower growth rate over the last N days."""
    with open(csv_file) as f:
        rows = [r for r in csv.DictReader(f) if r["username"] == username]
    if len(rows) < 2:
        return None
    latest = int(rows[-1]["followers"])
    earlier = int(rows[max(0, len(rows) - days - 1)]["followers"])
    if earlier == 0:
        return None
    return ((latest - earlier) / earlier) * 100


log_snapshot(profiles)
for handle in competitors:
    g = compute_growth("snapshots.csv", handle, days=7)
    if g is not None:
        print(f"@{handle}: {g:+.2f}% weekly follower growth")
```

The growth formula:

```text theme={null}
Growth Rate % = ((Followers Today - Followers N Days Ago) / Followers N Days Ago) * 100
```

### Bio and positioning changes

`/info` returns `description`, `location`, `bio_urls`, and `created_at`. Diff these across snapshots to detect positioning shifts (bio changes, link destination changes) at zero additional cost.

***

## Phase 2: Content Strategy

**Endpoints:** `POST /v3/user-tweets`, `POST /v3/search-tweets`

Pull a competitor's recent tweets and decompose their content mix: original posts vs. replies vs. quotes vs. retweets, average engagement, top-performing posts.

### Fetching recent tweets

[`/user-tweets`](https://docs.sorsa.io/api-reference/tweets/user-tweets) returns up to 20 tweets per page. Unlike the official X API's timeline endpoints, it is not hard-capped at 3,200 tweets, so you can paginate deeper into an account's history. For retrieval far back in time, `/search-tweets` with `since:`/`until:` operators (see below) is the more reliable approach.

```python theme={null}
def fetch_user_tweets(username, max_pages=10):
    """Pull a competitor's recent tweets via pagination."""
    all_tweets = []
    cursor = None

    for _ in range(max_pages):
        body = {"username": username}
        if cursor:
            body["next_cursor"] = cursor

        resp = requests.post(
            f"{BASE}/user-tweets",
            headers=JSON_HEADERS,
            json=body,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()

        all_tweets.extend(data.get("tweets", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)

    return all_tweets
```

### Decomposing the content mix

```python theme={null}
def analyze_content(tweets, username):
    if not tweets:
        return None

    total = len(tweets)
    likes = [t.get("likes_count", 0) for t in tweets]
    retweets = [t.get("retweet_count", 0) for t in tweets]
    replies = [t.get("reply_count", 0) for t in tweets]

    original = sum(1 for t in tweets if not t.get("is_reply") and not t.get("retweeted_status"))
    reply_count = sum(1 for t in tweets if t.get("is_reply"))
    quote_count = sum(1 for t in tweets if t.get("is_quote_status"))
    with_media = sum(1 for t in tweets if t.get("entities"))

    top_tweet = max(tweets, key=lambda t: t.get("likes_count", 0))

    return {
        "username": username,
        "sample_size": total,
        "avg_likes": sum(likes) / total,
        "avg_retweets": sum(retweets) / total,
        "avg_replies": sum(replies) / total,
        "original_pct": original / total * 100,
        "reply_pct": reply_count / total * 100,
        "quote_pct": quote_count / total * 100,
        "media_pct": with_media / total * 100,
        "top_tweet_likes": top_tweet.get("likes_count", 0),
        "top_tweet_text": top_tweet.get("full_text", "")[:200],
    }


for handle in competitors:
    tweets = fetch_user_tweets(handle, max_pages=10)
    result = analyze_content(tweets, handle)
    if result:
        print(f"\n@{result['username']} (n={result['sample_size']})")
        print(f"  Avg likes/tweet:     {result['avg_likes']:.1f}")
        print(f"  Avg retweets/tweet:  {result['avg_retweets']:.1f}")
        print(f"  Content mix: {result['original_pct']:.0f}% original / "
              f"{result['reply_pct']:.0f}% replies / {result['quote_pct']:.0f}% quotes / "
              f"{result['media_pct']:.0f}% with media")
        print(f"  Top tweet: ({result['top_tweet_likes']} likes) {result['top_tweet_text']}")
```

Note that these categories overlap (a tweet with media is also an original post), so the percentages are independent shares rather than a partition that sums to 100.

### Historical comparison

To compare two time windows for the same account (e.g., Q1 vs Q4), switch from `/user-tweets` to [`/search-tweets`](https://docs.sorsa.io/search-tweets) and use the `since:` and `until:` operators. See [Search Operators](https://docs.sorsa.io/search-operators) for the full syntax and [Historical Data](https://docs.sorsa.io/historical-data) for backfill patterns.

```python theme={null}
def fetch_tweets_in_range(username, since_date, until_date):
    query = f"from:{username} since:{since_date} until:{until_date}"
    resp = requests.post(
        f"{BASE}/search-tweets",
        headers=JSON_HEADERS,
        json={"query": query, "order": "latest"},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json().get("tweets", [])


q1_tweets = fetch_tweets_in_range("stripe", "2026-01-01", "2026-04-01")
q4_tweets = fetch_tweets_in_range("stripe", "2025-10-01", "2026-01-01")
```

***

## Phase 3: Audience Composition

**Endpoints:** `GET /v3/followers`, `GET /v3/verified-followers`, `GET /v3/followers-stats`

A competitor's follower list reveals who their audience is. There are two approaches with different cost profiles.

### Verified followers (low cost)

[`/verified-followers`](https://docs.sorsa.io/api-reference/users-data/verified-followers) returns only verified accounts following a handle. This is the highest-signal slice of any audience and is dramatically cheaper than pulling the full follower graph.

```python theme={null}
def fetch_verified_followers(username, max_pages=10):
    all_users = []
    cursor = None
    for _ in range(max_pages):
        params = {"username": username}
        if cursor:
            params["next_cursor"] = cursor
        resp = requests.get(
            f"{BASE}/verified-followers",
            headers=HEADERS,
            params=params,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()
        all_users.extend(data.get("users", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return all_users


for handle in competitors:
    verified = fetch_verified_followers(handle, max_pages=5)
    top = sorted(verified, key=lambda u: u.get("followers_count", 0), reverse=True)[:10]
    print(f"\n@{handle}: {len(verified)} verified followers fetched")
    for u in top:
        print(f"  @{u['username']:<25} {u['followers_count']:>10,} followers")
```

Diff verified-follower lists across snapshots to detect new high-authority followers per competitor. Journalist follow events often precede coverage by 2-4 weeks.

### Full follower extraction (high cost)

[`/followers`](https://docs.sorsa.io/api-reference/users-data/followers) returns up to 200 profiles per page. For an account with 1M followers, full extraction is roughly 5,000 requests. Plan accordingly: see [Pricing](https://api.sorsa.io/pricing) for plan limits and [Optimizing API Usage](https://docs.sorsa.io/optimizing-api-usage) for batch and budget patterns.

```python theme={null}
def fetch_all_followers(username, max_pages=200):
    """Pull all followers via pagination. Cost scales with account size."""
    all_users = []
    cursor = None
    for _ in range(max_pages):
        params = {"username": username}
        if cursor:
            params["next_cursor"] = cursor
        resp = requests.get(
            f"{BASE}/followers",
            headers=HEADERS,
            params=params,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()
        all_users.extend(data.get("users", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return all_users
```

### Audience overlap

Once you have follower lists for two accounts, compute overlap with set intersection on user IDs:

```python theme={null}
followers_a = {u["id"] for u in fetch_all_followers("competitor_a")}
followers_b = {u["id"] for u in fetch_all_followers("competitor_b")}

overlap = followers_a & followers_b
only_a = followers_a - followers_b
only_b = followers_b - followers_a

print(f"Shared audience: {len(overlap):,}")
print(f"Unique to @competitor_a: {len(only_a):,}")
print(f"Unique to @competitor_b: {len(only_b):,}")

overlap_ratio = len(overlap) / min(len(followers_a), len(followers_b))
print(f"Overlap ratio: {overlap_ratio:.1%}")
```

For a deeper dive on follower extraction patterns, see [Followers and Following](https://docs.sorsa.io/followers-and-following).

### Crypto and Web3 follower breakdown

For accounts in Sorsa's crypto database, [`/followers-stats`](https://docs.sorsa.io/api-reference/sorsa-info-crypto-related/follower-category-stats) returns a categorical breakdown: influencers, projects, VCs. See [Sorsa Score and Crypto Analytics](https://docs.sorsa.io/sorsa-score-and-crypto-analytics) for context.

```python theme={null}
def get_follower_breakdown(username):
    resp = requests.get(
        f"{BASE}/followers-stats",
        headers=HEADERS,
        params={"username": username},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()


for handle in ["VitalikButerin", "saylor"]:
    stats = get_follower_breakdown(handle)
    print(f"\n@{handle}:")
    print(f"  Tracked followers: {stats['followers_count']}")
    print(f"  Influencers:       {stats['influencers_count']}")
    print(f"  Projects:          {stats['projects_count']}")
    print(f"  VCs:               {stats['venture_capitals_count']}")
```

Counts only include accounts already tracked in the Sorsa crypto database.

***

## Phase 4: Public Sentiment and Mentions

**Endpoint:** `POST /v3/mentions`

[`/mentions`](https://docs.sorsa.io/api-reference/search/search-mentions) supports filtering by minimum likes, retweets, replies, and date ranges. Filtering by `min_likes` cuts low-signal noise (bot replies, auto-tags) from the mention stream. See [Track Mentions](https://docs.sorsa.io/search-mentions) for the full filter set.

### Pulling high-engagement mentions

```python theme={null}
def fetch_mentions(handle, min_likes=10, since_date=None, until_date=None, max_pages=5):
    all_mentions = []
    cursor = None
    for _ in range(max_pages):
        body = {"query": handle, "order": "popular", "min_likes": min_likes}
        if since_date:
            body["since_date"] = since_date
        if until_date:
            body["until_date"] = until_date
        if cursor:
            body["next_cursor"] = cursor

        resp = requests.post(
            f"{BASE}/mentions",
            headers=JSON_HEADERS,
            json=body,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()
        all_mentions.extend(data.get("tweets", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return all_mentions
```

### Sentiment classification with VADER

VADER is an open-source sentiment library tuned for social media text. It runs locally with no per-call cost and handles negation, intensifiers, and emoji reasonably well.

```python theme={null}
# pip install vaderSentiment
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

analyzer = SentimentIntensityAnalyzer()

def classify_sentiment(mentions):
    results = {"positive": [], "negative": [], "neutral": []}
    for m in mentions:
        score = analyzer.polarity_scores(m["full_text"])["compound"]
        if score >= 0.05:
            results["positive"].append((score, m))
        elif score <= -0.05:
            results["negative"].append((score, m))
        else:
            results["neutral"].append((score, m))
    return results


for handle in competitors:
    mentions = fetch_mentions(handle, min_likes=10, max_pages=5)
    s = classify_sentiment(mentions)
    print(f"\n@{handle}: {len(mentions)} mentions analyzed")
    print(f"  Positive: {len(s['positive'])}  Negative: {len(s['negative'])}  Neutral: {len(s['neutral'])}")
    if s["negative"]:
        worst = min(s["negative"], key=lambda x: x[0])
        text = worst[1]["full_text"][:150].replace("\n", " ")
        print(f"  Sharpest negative: {text}...")
```

For higher accuracy on sarcasm, technical complaints, or mixed sentiment, pipe `full_text` into an LLM API (OpenAI, Anthropic). A hybrid approach (VADER for filtering, LLM only on flagged or high-engagement mentions) keeps cost predictable.

***

## Phase 5: Share of Voice

Share of voice (SOV) measures one brand's mention volume against the category total. The formula:

```text theme={null}
SOV = (your mentions in period) / (your mentions + sum of competitor mentions in period)
```

Implementation:

```python theme={null}
def count_mentions(handle, days=7, min_likes=0):
    until = datetime.now(timezone.utc).date().isoformat()
    since = (datetime.now(timezone.utc) - timedelta(days=days)).date().isoformat()
    mentions = fetch_mentions(
        handle,
        min_likes=min_likes,
        since_date=since,
        until_date=until,
        max_pages=20,
    )
    return len(mentions)


brand = "your_handle"
your_mentions = count_mentions(brand, days=7, min_likes=5)
competitor_mentions = {h: count_mentions(h, days=7, min_likes=5) for h in competitors}

total = your_mentions + sum(competitor_mentions.values())
print(f"\nShare of voice, last 7 days (min 5 likes):")
print(f"  @{brand:<20} {your_mentions:>5}  ({your_mentions/total*100:.1f}%)")
for h, n in sorted(competitor_mentions.items(), key=lambda x: -x[1]):
    print(f"  @{h:<20} {n:>5}  ({n/total*100:.1f}%)")
```

Notes:

* Filter by minimum engagement (`min_likes=5` is a reasonable floor) to exclude bot and spam noise.
* Track week-over-week deltas, not absolute snapshots. Category-level events skew absolute numbers in ways that obscure your own movement.
* To compute category-keyword SOV (e.g., "embedded finance" rather than brand mentions), replace `/mentions` with `/search-tweets` and use the keyword as the denominator query.

***

## Consolidated Weekly Report Script

This script combines all five phases. Drop it into a cron job, GitHub Actions schedule, or any task runner.

```python theme={null}
import requests
import csv
import time
from datetime import date, datetime, timedelta, timezone
from pathlib import Path
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

API_KEY = "YOUR_API_KEY"
BASE = "https://api.sorsa.io/v3"
HEADERS = {"ApiKey": API_KEY}
JSON_HEADERS = {**HEADERS, "Content-Type": "application/json"}

BRAND = "your_handle"
COMPETITORS = ["competitor1", "competitor2", "competitor3"]
SNAPSHOT_FILE = "snapshots.csv"

analyzer = SentimentIntensityAnalyzer()


def get_profiles(usernames):
    resp = requests.get(
        f"{BASE}/info-batch",
        headers=HEADERS,
        params=[("usernames", u) for u in usernames],
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json().get("users", [])


def log_snapshot(profiles, output_file=SNAPSHOT_FILE):
    file_exists = Path(output_file).exists()
    today = date.today().isoformat()
    with open(output_file, "a", newline="") as f:
        writer = csv.writer(f)
        if not file_exists:
            writer.writerow(["date", "username", "followers", "tweets", "following"])
        for p in profiles:
            writer.writerow([
                today, p["username"], p["followers_count"],
                p["tweets_count"], p["followings_count"],
            ])


def compute_growth(csv_file, username, days=7):
    with open(csv_file) as f:
        rows = [r for r in csv.DictReader(f) if r["username"] == username]
    if len(rows) < 2:
        return None
    latest = int(rows[-1]["followers"])
    earlier = int(rows[max(0, len(rows) - days - 1)]["followers"])
    if earlier == 0:
        return None
    return ((latest - earlier) / earlier) * 100


def fetch_user_tweets(username, max_pages=5):
    all_tweets = []
    cursor = None
    for _ in range(max_pages):
        body = {"username": username}
        if cursor:
            body["next_cursor"] = cursor
        resp = requests.post(f"{BASE}/user-tweets", headers=JSON_HEADERS, json=body, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        all_tweets.extend(data.get("tweets", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return all_tweets


def analyze_content(tweets, username):
    if not tweets:
        return None
    total = len(tweets)
    likes = [t.get("likes_count", 0) for t in tweets]
    original = sum(1 for t in tweets if not t.get("is_reply") and not t.get("retweeted_status"))
    with_media = sum(1 for t in tweets if t.get("entities"))
    return {
        "username": username,
        "sample_size": total,
        "avg_likes": sum(likes) / total,
        "original_pct": original / total * 100,
        "media_pct": with_media / total * 100,
    }


def fetch_verified_followers(username, max_pages=3):
    all_users = []
    cursor = None
    for _ in range(max_pages):
        params = {"username": username}
        if cursor:
            params["next_cursor"] = cursor
        resp = requests.get(f"{BASE}/verified-followers", headers=HEADERS, params=params, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        all_users.extend(data.get("users", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return all_users


def fetch_mentions(handle, min_likes=10, since_date=None, until_date=None, max_pages=5):
    all_mentions = []
    cursor = None
    for _ in range(max_pages):
        body = {"query": handle, "order": "popular", "min_likes": min_likes}
        if since_date:
            body["since_date"] = since_date
        if until_date:
            body["until_date"] = until_date
        if cursor:
            body["next_cursor"] = cursor
        resp = requests.post(f"{BASE}/mentions", headers=JSON_HEADERS, json=body, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        all_mentions.extend(data.get("tweets", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return all_mentions


def classify_sentiment(mentions):
    results = {"positive": [], "negative": [], "neutral": []}
    for m in mentions:
        score = analyzer.polarity_scores(m["full_text"])["compound"]
        if score >= 0.05:
            results["positive"].append((score, m))
        elif score <= -0.05:
            results["negative"].append((score, m))
        else:
            results["neutral"].append((score, m))
    return results


def count_mentions(handle, days=7, min_likes=0):
    until = datetime.now(timezone.utc).date().isoformat()
    since = (datetime.now(timezone.utc) - timedelta(days=days)).date().isoformat()
    return len(fetch_mentions(handle, min_likes=min_likes, since_date=since, until_date=until, max_pages=20))


def header(text):
    line = "=" * 64
    print(f"\n{line}\n{text}\n{line}")


def run_weekly_report():
    header("PHASE 1: PROFILE BENCHMARKS")
    profiles = get_profiles(COMPETITORS + [BRAND])
    print(f"{'Handle':<18} {'Followers':>12} {'Tweets':>10} {'Verified':>10}")
    for p in profiles:
        print(f"@{p['username']:<17} {p['followers_count']:>12,} "
              f"{p['tweets_count']:>10,} {str(p.get('verified', False)):>10}")
    log_snapshot(profiles)
    for h in COMPETITORS + [BRAND]:
        g = compute_growth(SNAPSHOT_FILE, h, days=7)
        if g is not None:
            print(f"  @{h}: {g:+.2f}% weekly follower growth")

    header("PHASE 2: CONTENT STRATEGY")
    for handle in COMPETITORS:
        tweets = fetch_user_tweets(handle, max_pages=5)
        result = analyze_content(tweets, handle)
        if result:
            print(f"@{result['username']}: avg {result['avg_likes']:.0f} likes/tweet, "
                  f"{result['original_pct']:.0f}% original, "
                  f"{result['media_pct']:.0f}% with media")

    header("PHASE 3: VERIFIED FOLLOWERS")
    for handle in COMPETITORS:
        verified = fetch_verified_followers(handle, max_pages=3)
        print(f"@{handle}: {len(verified)} verified followers in top pages")

    header("PHASE 4: SENTIMENT")
    for handle in COMPETITORS:
        mentions = fetch_mentions(handle, min_likes=10, max_pages=3)
        s = classify_sentiment(mentions)
        print(f"@{handle}: {len(s['positive'])} pos / {len(s['negative'])} neg "
              f"/ {len(s['neutral'])} neutral (n={len(mentions)})")

    header("PHASE 5: SHARE OF VOICE (7d)")
    your_n = count_mentions(BRAND, days=7, min_likes=5)
    comp_n = {h: count_mentions(h, days=7, min_likes=5) for h in COMPETITORS}
    total = your_n + sum(comp_n.values())
    if total:
        print(f"  @{BRAND}: {your_n} ({your_n/total*100:.1f}%)")
        for h, n in sorted(comp_n.items(), key=lambda x: -x[1]):
            print(f"  @{h}: {n} ({n/total*100:.1f}%)")


if __name__ == "__main__":
    run_weekly_report()
```

At the default page depths in this script, a full run for three competitors is on the order of 100 requests (a little more for very active brands where mention pagination runs deeper, a little less otherwise). Run weekly, that is a few hundred requests a month, well inside the Starter plan (10,000 requests per month). The free 100 requests are enough to try the workflow first at shallow depth, on one or two competitors, before choosing a plan. See [Pricing](https://api.sorsa.io/pricing) for plan details.

***

## Next Steps

* [Search Tweets](https://docs.sorsa.io/search-tweets): keyword and operator search across all of X.
* [Track Mentions](https://docs.sorsa.io/search-mentions): deeper coverage of the `/mentions` endpoint with all filter combinations.
* [Followers and Following](https://docs.sorsa.io/followers-and-following): full patterns for follower extraction, pagination, and CSV export.
* [Historical Data](https://docs.sorsa.io/historical-data): backfill strategies for long time-window analysis.
* [Real-Time Monitoring](https://docs.sorsa.io/real-time-monitoring): polling patterns for detecting new tweets within seconds.
* [Target Audience Discovery](https://docs.sorsa.io/target-audiences-Discovery): extract and segment competitor follower lists for lead generation.
* [Optimizing API Usage](https://docs.sorsa.io/optimizing-api-usage): batch endpoints, cursor handling, and request budgeting.
* [API Reference](https://docs.sorsa.io/api-reference-guide): full specification for every endpoint used in this guide.
