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

# News Sentiment Tracking

> Track media sentiment around your brand or topics week over week with batched analysis and trend arrows

### Scenario

Track how media sentiment around your brand, product category, or key topics shifts week over week. This job searches for mentions across all sources, batches them into weekly windows, sends each batch to Mavera Chat for structured sentiment analysis, and outputs a time-series sentiment dashboard with trend arrows.

**Flow:** NewsAPI `GET /everything?q={topic}` (weekly batches) → Mavera `POST /mave/chat` per batch → Sentiment time series

### Code

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

  NA_KEY = os.environ["NEWSAPI_KEY"]
  NA_BASE = "https://newsapi.org/v2"
  NA_H = {"X-Api-Key": NA_KEY}
  MV = os.environ["MAVERA_API_KEY"]
  MV_BASE = "https://app.mavera.io/api/v1"
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  TOPIC = "artificial intelligence marketing"
  WEEKS = 4

  # 1. Fetch articles in weekly batches
  weekly_data = []
  for week in range(WEEKS):
      end = datetime.now() - timedelta(weeks=week)
      start = end - timedelta(days=7)
      r = requests.get(f"{NA_BASE}/everything", headers=NA_H, params={
          "q": TOPIC, "from": start.strftime("%Y-%m-%d"),
          "to": end.strftime("%Y-%m-%d"), "sortBy": "relevancy",
          "language": "en", "pageSize": 25,
      })
      if not r.ok:
          print(f"Week {week}: API error {r.status_code}")
          continue
      articles = r.json().get("articles", [])
      weekly_data.append({
          "week_label": f"{start.strftime('%b %d')} – {end.strftime('%b %d')}",
          "articles": articles, "count": len(articles),
      })
      print(f"Week {week}: {len(articles)} articles ({start.strftime('%b %d')} – {end.strftime('%b %d')})")
      time.sleep(1)

  # 2. Batch sentiment analysis via Mave
  sentiment_series = []
  for week in weekly_data:
      corpus = "\n".join(
          f"- [{a.get('source',{}).get('name','')}] {a['title']}: {a.get('description','')[:150]}"
          for a in week["articles"][:20]
      )
      analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
          "message": f"Sentiment analyst. Analyze media coverage for '{TOPIC}' during {week['week_label']}.\n\n"
              f"ARTICLES ({week['count']}):\n{corpus}\n\n"
              "Return JSON with: overall_sentiment (positive/negative/neutral/mixed), "
              "confidence (0-100), positive_pct, negative_pct, neutral_pct, "
              "top_positive_theme, top_negative_theme, notable_shift, key_quote."
      }).json()
      content = analysis.get("content", "")
      sentiment_series.append({"week": week["week_label"], "count": week["count"], "analysis": content[:600]})
      time.sleep(0.5)

  # 3. Trend summary
  trend = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"Given these {WEEKS} weeks of sentiment data for '{TOPIC}', identify the overall trend.\n\n"
          + "\n\n".join(f"**{s['week']}** ({s['count']} articles):\n{s['analysis']}" for s in sentiment_series)
          + "\n\nProvide: trend direction (improving/declining/stable), confidence, biggest driver, prediction for next week, recommended action."
  }).json()

  print(f"\n{'='*60}\nSENTIMENT TREND: {TOPIC}\n{'='*60}")
  for s in sentiment_series:
      print(f"\n{s['week']} ({s['count']} articles):\n{s['analysis'][:300]}")
  print(f"\n{'='*60}\nTREND ANALYSIS\n{'='*60}")
  print(trend.get("content", "")[:800])
  ```

  ```javascript JavaScript theme={"dark"}
  const NA_KEY = process.env.NEWSAPI_KEY;
  const NA_BASE = "https://newsapi.org/v2";
  const NA_H = { "X-Api-Key": NA_KEY };
  const MV = process.env.MAVERA_API_KEY;
  const MV_BASE = "https://app.mavera.io/api/v1";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const TOPIC = "artificial intelligence marketing";
  const WEEKS = 4;

  // 1. Weekly batches
  const weeklyData = [];
  for (let week = 0; week < WEEKS; week++) {
    const end = new Date(Date.now() - week * 7 * 86400000);
    const start = new Date(end - 7 * 86400000);
    const r = await fetch(
      `${NA_BASE}/everything?q=${encodeURIComponent(TOPIC)}&from=${start.toISOString().slice(0,10)}&to=${end.toISOString().slice(0,10)}&sortBy=relevancy&language=en&pageSize=25`,
      { headers: NA_H });
    if (!r.ok) { console.log(`Week ${week}: error ${r.status}`); continue; }
    const articles = (await r.json()).articles || [];
    const label = `${start.toLocaleDateString("en",{month:"short",day:"numeric"})} – ${end.toLocaleDateString("en",{month:"short",day:"numeric"})}`;
    weeklyData.push({ week_label: label, articles, count: articles.length });
    console.log(`Week ${week}: ${articles.length} articles (${label})`);
    await new Promise(r => setTimeout(r, 1000));
  }

  // 2. Batch sentiment
  const sentimentSeries = [];
  for (const week of weeklyData) {
    const corpus = week.articles.slice(0, 20)
      .map(a => `- [${a.source?.name||""}] ${a.title}: ${(a.description||"").slice(0,150)}`).join("\n");
    const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
      body: JSON.stringify({ message: `Sentiment analyst. '${TOPIC}' coverage for ${week.week_label}.\n\nARTICLES (${week.count}):\n${corpus}\n\nReturn JSON: overall_sentiment, confidence, positive_pct, negative_pct, neutral_pct, top themes, key_quote.` }),
    }).then(r => r.json());
    sentimentSeries.push({ week: week.week_label, count: week.count, analysis: (analysis.content||"").slice(0,600) });
    await new Promise(r => setTimeout(r, 500));
  }

  // 3. Trend
  const trend = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `${WEEKS}-week sentiment trend for '${TOPIC}'.\n\n${sentimentSeries.map(s=>`**${s.week}** (${s.count} articles):\n${s.analysis}`).join("\n\n")}\n\nTrend direction, confidence, biggest driver, next-week prediction, recommended action.` }),
  }).then(r => r.json());

  console.log(`\n${"=".repeat(60)}\nSENTIMENT TREND: ${TOPIC}`);
  for (const s of sentimentSeries)
    console.log(`\n${s.week} (${s.count} articles):\n${s.analysis.slice(0,300)}`);
  console.log(`\n${"=".repeat(60)}\nTREND ANALYSIS`);
  console.log((trend.content || "").slice(0, 800));
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Week 0: 25 articles (Mar 10 – Mar 17)
Week 1: 22 articles (Mar 03 – Mar 10)
Week 2: 19 articles (Feb 24 – Mar 03)
Week 3: 23 articles (Feb 17 – Feb 24)

SENTIMENT TREND: artificial intelligence marketing
============================================================

Mar 10 – Mar 17 (25 articles):
  {"overall_sentiment": "positive", "confidence": 78,
   "positive_pct": 56, "negative_pct": 20, "neutral_pct": 24,
   "top_positive_theme": "Personalization ROI gains",
   "top_negative_theme": "Privacy regulation fears"}

TREND ANALYSIS
============================================================
Direction: IMPROVING (↑ 12% positive shift over 4 weeks)
Driver: Enterprise adoption stories replacing hype-cycle skepticism.
Prediction: Continued positive bias until Q2 earnings season.
Action: Publish thought leadership now — sentiment tailwind amplifies reach.
```

### Error Handling

<AccordionGroup>
  <Accordion title="Weekly batch gaps">Free tier limits search to past 30 days. Reduce `WEEKS` to 4 max on free plans. Business tier supports full archive.</Accordion>
  <Accordion title="JSON parsing">Mave returns natural language by default. Prompt for JSON explicitly and parse with `json.loads()` / `JSON.parse()`. Wrap in try/catch for malformed responses.</Accordion>
  <Accordion title="Topic specificity">Broad topics like "AI" return noise. Use quoted phrases and boolean operators: `"AI marketing" AND ("ROI" OR "attribution")`.</Accordion>
</AccordionGroup>
