> ## 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.

# Análise de concorrentes

# Como analisar concorrentes no X com a API

Este fluxo reúne comparação de perfis, estratégia de conteúdo, composição da audiência, sentimento público e participação nas menções (share of voice). Cada etapa usa endpoints específicos e pode compor um relatório semanal automatizado.

Use Python 3.8+ com `requests` e substitua `YOUR_API_KEY` em todos os exemplos. O script consolidado, com as funções auxiliares, está ao final.

> **Comece grátis:** as primeiras 100 requisições, sem cartão e sem validade, permitem testar o fluxo com poucas páginas.

> Veja estratégia e exemplos no [guia de concorrentes do blog](https://api.sorsa.io/blog/twitter-competitor-analysis).

> **Sem código:** o [Profile Comparison Tool](https://api.sorsa.io/playground/compare-users) compara seguidores, engajamento, médias de curtidas e repostagens, frequência e idade de duas contas. O [Engagement Calculator](https://api.sorsa.io/playground/engagement-calculator) calcula engajamento por publicação de uma conta.

## Configuração

```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"}
```

O cabeçalho `ApiKey` autentica cada chamada. Veja [autenticação](https://docs.sorsa.io/pt-BR/authentication).

## Etapa 1: comparar perfis

**Endpoints:** `GET /v3/info` e `GET /v3/info-batch`.

Estabeleça uma referência de seguidores, publicações, idade, bio e verificação. [`/info-batch`](https://docs.sorsa.io/pt-br/api-reference/usu%C3%A1rios/perfis-de-usu%C3%A1rios-em-lote) retorna até 100 perfis em uma chamada.

### Capturar um retrato dos perfis

```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}"
    )
```

### Acompanhar crescimento

Crescimento exige pelo menos duas observações datadas. Registre diariamente ou semanalmente com cron, GitHub Actions ou ferramenta equivalente:

```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):
    with open(csv_file, encoding="utf-8") as f:
        rows = [r for r in csv.DictReader(f) if r["username"].lower() == username.lower()]
    if len(rows) < 2:
        return None
    rows.sort(key=lambda row: row["date"])
    latest_row = rows[-1]
    cutoff = date.fromisoformat(latest_row["date"]) - timedelta(days=days)
    earlier_rows = [r for r in rows if date.fromisoformat(r["date"]) <= cutoff]
    if not earlier_rows:
        return None
    latest = int(latest_row["followers"])
    earlier = int(earlier_rows[-1]["followers"])
    return ((latest - earlier) / earlier) * 100 if earlier else None


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")
```

A função compara o último registro com a observação mais recente na data de corte ou antes dela. Com registros irregulares, o intervalo real pode exceder `days`. Grave diariamente para maior precisão e armazene IDs junto aos nomes para que renomeações não dividam o histórico.

Fórmula de crescimento:

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

### Mudanças na bio e posicionamento

Compare `description`, `location`, `bio_urls` e `created_at` entre registros para detectar mudanças sem chamadas adicionais.

## Etapa 2: estratégia de conteúdo

**Endpoints:** `POST /v3/user-tweets` e `POST /v3/search-tweets`.

Analise originais, respostas, citações, repostagens, médias de engajamento e melhores posts.

### Obter publicações recentes

[`/user-tweets`](https://docs.sorsa.io/pt-br/api-reference/publica%C3%A7%C3%B5es/publica%C3%A7%C3%B5es-do-usu%C3%A1rio) retorna até 20 por página, sem o teto de 3.200. Para períodos antigos específicos, prefira busca com `since:` e `until:`.

```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
```

### Analisar a composição

```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']}")
```

As categorias se sobrepõem: um post original pode conter mídia. As porcentagens são independentes e não precisam somar 100%.

### Comparar períodos históricos

Use [`/search-tweets`](https://docs.sorsa.io/pt-BR/search-tweets) com datas para comparar trimestres ou outros períodos. Veja [operadores](https://docs.sorsa.io/pt-BR/search-operators) e [dados históricos](https://docs.sorsa.io/pt-BR/historical-data).

```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", [])  # First page only; paginate for a full period.


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")
```

## Etapa 3: composição da audiência

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

### Seguidores verificados: menor custo

[`/verified-followers`](https://docs.sorsa.io/pt-br/api-reference/usu%C3%A1rios/seguidores-verificados) seleciona apenas contas verificadas, reduzindo o volume em comparação com a lista completa.

```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")
```

Compare listas ao longo do tempo para detectar novas conexões de destaque. No acompanhamento de mídia, jornalistas podem começar a seguir uma conta semanas antes de uma publicação sobre ela.

### Lista completa: maior custo

[`/followers`](https://docs.sorsa.io/pt-br/api-reference/usu%C3%A1rios/seguidores) retorna até 200 perfis por página. Um milhão de seguidores exige aproximadamente 5.000 chamadas. Consulte [preços](https://api.sorsa.io/pricing) e [otimização](https://docs.sorsa.io/pt-BR/optimizing-api-usage).

```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
```

### Sobreposição de audiência

Calcule a interseção dos conjuntos de 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):,}")

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

Veja [seguidores e contas seguidas](https://docs.sorsa.io/pt-BR/followers-and-following).

### Categorias de seguidores cripto e Web3

[`/followers-stats`](https://docs.sorsa.io/pt-br/api-reference/sorsa-e-cripto/estat%C3%ADsticas-por-categoria-de-seguidores) retorna influenciadores, projetos e fundos para contas da base cripto. Veja [Sorsa Score](https://docs.sorsa.io/pt-BR/sorsa-score-and-crypto-analytics).

```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']}")
```

As contagens incluem apenas contas já acompanhadas na base cripto da Sorsa.

## Etapa 4: sentimento e menções

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

Filtre por curtidas, repostagens, respostas e datas. `min_likes` ajuda a reduzir ruído; veja [menções](https://docs.sorsa.io/pt-BR/search-mentions).

### Menções com engajamento

```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
```

### Classificar sentimento com VADER

VADER é uma biblioteca aberta voltada a texto de redes sociais. Executa localmente, sem cobrança por chamada, e lida com negação, intensificadores e emojis.

```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}...")
```

Para sarcasmo, reclamações técnicas ou sentimento misto, considere enviar `full_text` a um LLM, como OpenAI ou Anthropic. Use VADER para triagem e LLM apenas em casos selecionados para controlar custos.

## Etapa 5: share of voice

SOV compara menções de uma marca com o total da categoria.

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

Implementação:

```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 if total else 0):.1f}%)")
for h, n in sorted(competitor_mentions.items(), key=lambda x: -x[1]):
    print(f"  @{h:<20} {n:>5}  ({(n/total*100 if total else 0):.1f}%)")
```

* Um filtro como `min_likes=5` reduz ruído de spam, mas não comprova autenticidade.
* Acompanhe variações semanais; eventos da categoria podem distorcer números absolutos.
* `max_pages` limita a amostra. Use datas e filtros iguais e confirme o fim da paginação para todas as marcas; caso contrário, identifique o relatório como amostral.
* Para SOV por tema, substitua `/mentions` por `/search-tweets` e use a consulta da categoria como denominador.

## Relatório semanal consolidado

Execute com cron, GitHub Actions ou outro agendador.

```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, encoding="utf-8") as f:
        rows = [r for r in csv.DictReader(f) if r["username"].lower() == username.lower()]
    if len(rows) < 2:
        return None
    rows.sort(key=lambda row: row["date"])
    latest_row = rows[-1]
    cutoff = date.fromisoformat(latest_row["date"]) - timedelta(days=days)
    earlier_rows = [r for r in rows if date.fromisoformat(r["date"]) <= cutoff]
    if not earlier_rows:
        return None
    latest = int(latest_row["followers"])
    earlier = int(earlier_rows[-1]["followers"])
    return ((latest - earlier) / earlier) * 100 if earlier else None


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 if total else 0):.1f}%)")


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

Com a profundidade padrão e três concorrentes, uma execução usa cerca de 100 chamadas, variando pela paginação. Semanalmente, são algumas centenas por mês, dentro do Starter de 10.000. Teste primeiro com um ou dois concorrentes e poucas páginas usando as 100 gratuitas. Veja [preços](https://api.sorsa.io/pricing).

## Próximos passos

* [Busca](https://docs.sorsa.io/pt-BR/search-tweets): palavras-chave e operadores.
* [Menções](https://docs.sorsa.io/pt-BR/search-mentions): combinações de filtros.
* [Seguidores](https://docs.sorsa.io/pt-BR/followers-and-following): extração, paginação e CSV.
* [Histórico](https://docs.sorsa.io/pt-BR/historical-data): períodos longos.
* [Monitoramento](https://docs.sorsa.io/pt-BR/real-time-monitoring): novas publicações.
* [Público-alvo](https://docs.sorsa.io/pt-BR/target-audiences-Discovery): segmentação para leads.
* [Otimização](https://docs.sorsa.io/pt-BR/optimizing-api-usage): lotes e orçamento.
* [Referência](https://docs.sorsa.io/pt-BR/api-reference-guide): especificações.
