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

# Brand Mention Sentiment Pipeline

> Search recent brand mentions on X, classify sentiment by topic via Mavera Chat, and surface emerging issues and high-impact mentions

## Scenario

Social listening tools show a sentiment score but not *why* people feel positive or negative. This job searches recent brand mentions, feeds tweets into Mavera Chat with an analyst persona, and produces structured sentiment classification by topic. Run daily to catch emerging issues.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A["X GET /tweets/search/recent?query={brand}"] --> B["Collect tweets"]
    B --> C["Mavera POST /mave/chat"]
    C --> D["Sentiment x topic matrix"]
```

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  X = os.environ["X_BEARER_TOKEN"]; MV = os.environ["MAVERA_API_KEY"]
  X_BASE = "https://api.x.com/2"; MV_BASE = "https://app.mavera.io/api/v1"
  X_H = {"Authorization": f"Bearer {X}"}
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  QUERY = '"Your Brand" OR @yourbrand -is:retweet'
  tweets, next_token = [], None

  # 1. Paginated search
  for _ in range(3):
      params = {"query": QUERY, "max_results": 100,
          "tweet.fields": "created_at,public_metrics,author_id",
          "expansions": "author_id", "user.fields": "name,username,public_metrics"}
      if next_token: params["next_token"] = next_token
      r = requests.get(f"{X_BASE}/tweets/search/recent", headers=X_H, params=params)
      if r.status_code == 429:
          time.sleep(int(r.headers.get("x-rate-limit-reset", time.time()+60)) - int(time.time()))
          r = requests.get(f"{X_BASE}/tweets/search/recent", headers=X_H, params=params)
      r.raise_for_status(); data = r.json()
      users = {u["id"]: u for u in data.get("includes",{}).get("users",[])}
      for t in data.get("data",[]):
          a = users.get(t.get("author_id"),{})
          m = t.get("public_metrics",{})
          tweets.append({"text": t["text"], "username": a.get("username",""),
              "followers": a.get("public_metrics",{}).get("followers_count",0),
              "likes": m.get("like_count",0), "retweets": m.get("retweet_count",0)})
      next_token = data.get("meta",{}).get("next_token")
      if not next_token: break
      time.sleep(1)

  print(f"Collected {len(tweets)} brand mentions")

  # 2. Mavera Chat analysis
  block = "\n\n".join(f"@{t['username']} ({t['followers']:,} fol) | {t['likes']}♥ {t['retweets']}🔁\n{t['text']}"
      for t in sorted(tweets, key=lambda x: -(x["likes"]+x["retweets"]))[:50])

  analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"Brand sentiment analyst. Classify these {len(tweets)} tweets.\n\nTWEETS:\n{block}\n\n"
          "Produce:\n## Sentiment Distribution (count, %, themes)\n## Topic Clusters (ranked, with representative tweets)\n"
          "## High-Impact Mentions (followers >10K or engagement >50)\n## Emerging Issues (3+ negative mentions)\n## Recommended Actions"
  }).json()
  print(analysis.get("content","")[:2000])
  ```

  ```javascript JavaScript theme={"dark"}
  const X = process.env.X_BEARER_TOKEN, MV = process.env.MAVERA_API_KEY;
  const X_BASE = "https://api.x.com/2", MV_BASE = "https://app.mavera.io/api/v1";
  const X_H = { Authorization: `Bearer ${X}` };
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const QUERY = '"Your Brand" OR @yourbrand -is:retweet';
  const tweets = []; let nextToken = null;

  for (let i = 0; i < 3; i++) {
    const params = new URLSearchParams({ query: QUERY, max_results: "100",
      "tweet.fields": "created_at,public_metrics,author_id", expansions: "author_id",
      "user.fields": "name,username,public_metrics" });
    if (nextToken) params.set("next_token", nextToken);
    let r = await fetch(`${X_BASE}/tweets/search/recent?${params}`, { headers: X_H });
    if (r.status === 429) { await new Promise(res => setTimeout(res, 60000));
      r = await fetch(`${X_BASE}/tweets/search/recent?${params}`, { headers: X_H }); }
    if (!r.ok) throw new Error(`X API ${r.status}`);
    const data = await r.json();
    const users = Object.fromEntries((data.includes?.users||[]).map(u => [u.id, u]));
    for (const t of data.data || []) {
      const a = users[t.author_id]||{}, m = t.public_metrics||{};
      tweets.push({ text: t.text, username: a.username||"", followers: a.public_metrics?.followers_count||0,
        likes: m.like_count||0, retweets: m.retweet_count||0 });
    }
    nextToken = data.meta?.next_token; if (!nextToken) break;
    await new Promise(r => setTimeout(r, 1000));
  }

  const block = tweets.sort((a,b) => (b.likes+b.retweets)-(a.likes+a.retweets)).slice(0,50)
    .map(t => `@${t.username} (${t.followers.toLocaleString()}) | ${t.likes}♥ ${t.retweets}🔁\n${t.text}`).join("\n\n");
  const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Classify ${tweets.length} tweets.\n\n${block}\n\nProduce: Sentiment, Clusters, High-Impact, Issues, Actions.` }),
  }).then(r => r.json());
  console.log((analysis.content||"").slice(0,2000));
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
Collected 247 brand mentions

## Sentiment: Positive 112 (45%), Negative 68 (28%), Neutral 67 (27%)

## Top Clusters
1. Feature Launch — 54 tweets, 78% positive: "shipped the dashboard I begged for"
2. Pricing — 38 tweets, 71% negative: "$49/seat for 30 people is insane"

## High-Impact: @techreviewer (142K) positive → request testimonial
   @startupfounder (89K) negative → DM for feedback, escalate

## Emerging: Mobile crash (7 mentions, HIGH) — iOS dashboard since v4.2.1
```

## Error Handling

<AccordionGroup>
  <Accordion title="Basic tier read limits">10,000 reads/month. Three pages (300 tweets) × 30 daily runs = 9,000 reads. Monitor usage in Developer Portal.</Accordion>
  <Accordion title="Time window">Recent search covers last 7 days. For historical analysis, use full-archive search (Pro, \$5K/mo).</Accordion>
  <Accordion title="Rate limit headers">X returns `x-rate-limit-remaining` and `x-rate-limit-reset` (Unix timestamp). Code handles 429s by sleeping.</Accordion>
</AccordionGroup>

***

<CardGroup cols={2}>
  <Card title="X / Twitter Integration" icon="arrow-left" href="/integrations/x-twitter" />

  <Card title="Mave Agent" icon="brain" href="/features/mave-agent" />
</CardGroup>
