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

# Trending Content Gap Analysis

> Pull YouTube trending videos, identify content themes missing from your channel, and generate specific video concepts to fill the gaps

## Scenario

YouTube's Most Popular chart reveals what the platform is amplifying right now — but your brand isn't on it. This job pulls the top 50 trending videos via `videos.list?chart=mostPopular`, extracts their categories, tags, and descriptions, then sends the full dataset to Mave with your brand context. Mave identifies content themes that are trending but absent from your channel — gaps your brand could own with timely content. The result is a content gap report with specific video concepts ranked by opportunity size.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A["YouTube videos.list?chart=mostPopular"] --> B[Extract categories + tags] --> C["POST /api/v1/mave/chat"] --> D[Content gap analysis + recommendations]
```

## Code

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

  YT = os.environ["YOUTUBE_API_KEY"]
  MV = os.environ["MAVERA_API_KEY"]
  YT_BASE = "https://www.googleapis.com/youtube/v3"
  MV_BASE = "https://app.mavera.io/api/v1"
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  BRAND_CONTEXT = """
  Brand: [Your Brand] — DTC fitness/wellness company
  Channel focus: Workout tutorials, nutrition science, product reviews
  Audience: 18-35, health-conscious, 60% female, US-based
  Current content gaps we suspect: trending challenges, reaction content, collaborations
  """

  # 1. Fetch top 50 trending videos (1 quota unit per call)
  trending = requests.get(f"{YT_BASE}/videos", params={
      "key": YT, "chart": "mostPopular",
      "part": "snippet,contentDetails,statistics,topicDetails",
      "maxResults": 50, "regionCode": "US",
  }).json()

  if "error" in trending:
      raise SystemExit(f"YouTube API error: {trending['error']['message']}")

  videos = []
  category_counts = {}
  all_tags = []

  for item in trending.get("items", []):
      snippet = item["snippet"]
      stats = item.get("statistics", {})
      category = snippet.get("categoryId", "0")
      tags = snippet.get("tags", [])

      category_counts[category] = category_counts.get(category, 0) + 1
      all_tags.extend(tags[:10])

      videos.append({
          "title": snippet["title"],
          "channel": snippet["channelTitle"],
          "category": category,
          "tags": tags[:10],
          "views": int(stats.get("viewCount", 0)),
          "likes": int(stats.get("likeCount", 0)),
          "description": snippet.get("description", "")[:200],
      })

  # 2. Resolve category IDs to names
  cat_ids = ",".join(set(category_counts.keys()))
  categories = requests.get(f"{YT_BASE}/videoCategories", params={
      "key": YT, "id": cat_ids, "part": "snippet",
  }).json()
  cat_map = {
      item["id"]: item["snippet"]["title"]
      for item in categories.get("items", [])
  }

  # 3. Build trending summary
  trending_block = "\n".join(
      f"- \"{v['title']}\" by {v['channel']} | Category: {cat_map.get(v['category'], v['category'])} | "
      f"Views: {v['views']:,} | Tags: {', '.join(v['tags'][:5])}"
      for v in sorted(videos, key=lambda x: -x["views"])[:30]
  )

  category_summary = "\n".join(
      f"- {cat_map.get(cid, cid)}: {count} videos"
      for cid, count in sorted(category_counts.items(), key=lambda x: -x[1])
  )

  # 4. Gap analysis via Mave
  gap = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"""Identify content gaps between what's trending on YouTube and what our brand covers.

  BRAND CONTEXT:
  {BRAND_CONTEXT}

  TRENDING NOW (top 30 by views):
  {trending_block}

  CATEGORY DISTRIBUTION:
  {category_summary}

  TOP TRENDING TAGS: {', '.join(set(all_tags)[:30])}

  Produce:
  1. **Trending themes** our brand is NOT covering (ranked by opportunity size)
  2. **Specific video concepts** for each gap (title, format, estimated audience size)
  3. **Cross-category opportunities** — trends in non-fitness categories our fitness brand could adapt
  4. **Timing urgency** — which gaps are time-sensitive (trend peaking) vs. evergreen
  5. **Risk assessment** — which trends could backfire for our brand""",
  }).json()

  print("TRENDING CONTENT GAP ANALYSIS")
  print("=" * 60)
  print(f"Analyzed: {len(videos)} trending videos across {len(category_counts)} categories")
  print(f"Top categories: {', '.join(cat_map.get(c, c) for c, _ in sorted(category_counts.items(), key=lambda x: -x[1])[:5])}")
  print("\n" + gap.get("content", "")[:2500])
  ```

  ```javascript JavaScript theme={"dark"}
  const YT = process.env.YOUTUBE_API_KEY;
  const MV = process.env.MAVERA_API_KEY;
  const YT_BASE = "https://www.googleapis.com/youtube/v3";
  const MV_BASE = "https://app.mavera.io/api/v1";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const BRAND_CONTEXT = `Brand: [Your Brand] — DTC fitness/wellness
  Channel: Workout tutorials, nutrition science, product reviews
  Audience: 18-35, health-conscious, 60% female, US
  Suspected gaps: trending challenges, reaction content, collaborations`;

  // 1. Trending videos (1 quota unit)
  const trending = await fetch(
    `${YT_BASE}/videos?key=${YT}&chart=mostPopular&part=snippet,contentDetails,statistics,topicDetails&maxResults=50&regionCode=US`
  ).then(r => r.json());

  if (trending.error) throw new Error(trending.error.message);

  const videos = [];
  const categoryCounts = {};
  const allTags = [];

  for (const item of trending.items || []) {
    const s = item.snippet;
    const stats = item.statistics || {};
    const cat = s.categoryId || "0";
    const tags = (s.tags || []).slice(0, 10);
    categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
    allTags.push(...tags);
    videos.push({
      title: s.title, channel: s.channelTitle, category: cat, tags,
      views: parseInt(stats.viewCount || "0", 10),
      likes: parseInt(stats.likeCount || "0", 10),
      description: (s.description || "").slice(0, 200),
    });
  }

  // 2. Category names
  const catIds = [...new Set(Object.keys(categoryCounts))].join(",");
  const categories = await fetch(
    `${YT_BASE}/videoCategories?key=${YT}&id=${catIds}&part=snippet`
  ).then(r => r.json());
  const catMap = Object.fromEntries(
    (categories.items || []).map(i => [i.id, i.snippet.title])
  );

  // 3. Build summary
  const trendingBlock = videos
    .sort((a, b) => b.views - a.views).slice(0, 30)
    .map(v => `- "${v.title}" by ${v.channel} | ${catMap[v.category] || v.category} | Views: ${v.views.toLocaleString()} | Tags: ${v.tags.slice(0, 5).join(", ")}`)
    .join("\n");

  const catSummary = Object.entries(categoryCounts)
    .sort(([, a], [, b]) => b - a)
    .map(([cid, count]) => `- ${catMap[cid] || cid}: ${count} videos`)
    .join("\n");

  // 4. Gap analysis
  const gap = await fetch(`${MV_BASE}/mave/chat`, {
    method: "POST", headers: MV_H,
    body: JSON.stringify({
      message: `Identify content gaps between trending YouTube and our brand.\n\nBRAND:\n${BRAND_CONTEXT}\n\nTRENDING (top 30):\n${trendingBlock}\n\nCATEGORIES:\n${catSummary}\n\nTOP TAGS: ${[...new Set(allTags)].slice(0, 30).join(", ")}\n\nProduce: 1) Trending themes we miss 2) Specific video concepts 3) Cross-category opportunities 4) Timing urgency 5) Risk assessment`,
    }),
  }).then(r => r.json());

  console.log("TRENDING CONTENT GAP ANALYSIS");
  console.log("=".repeat(60));
  console.log(`Analyzed: ${videos.length} videos, ${Object.keys(categoryCounts).length} categories`);
  console.log((gap.content || "").slice(0, 2500));
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
TRENDING CONTENT GAP ANALYSIS
============================================================
Analyzed: 50 trending videos across 12 categories
Top categories: Entertainment, Music, Sports, Gaming, People & Blogs

## Content Gaps (Ranked by Opportunity)

### 1. "Get Ready With Me" Fitness Edition (HIGH URGENCY)
- 4 trending GRWM videos, none fitness-focused
- Concept: "GRWM for a 5AM Workout" — morning routine + product integration
- Format: 8-12 min, vlog-style, trending audio
- Audience: 2.4M views on average for GRWM content this week

### 2. Reaction/Commentary on Fitness Trends (MEDIUM URGENCY)
- 6 trending reaction videos, fitness commentary absent
- Concept: "Trainer Reacts to Viral Workout Hacks" — debunk + educate
- Format: 10-15 min, split-screen reaction format
- Audience: Commentary format averages 1.8M views

### 3. Challenge Format — Adapted (EVERGREEN)
- 3 trending challenge videos, zero fitness challenges this week
- Concept: "30-Day Morning Routine Challenge" — serialized, daily uploads
- Risk: Challenge fatigue — must feel fresh, not derivative

### 4. Cross-Category: Gaming × Fitness (EXPERIMENTAL)
- Gaming dominates trending (8 videos). No gaming-fitness crossover exists
- Concept: "I Trained Like a Pro Gamer for 7 Days" — posture, nutrition, endurance
- Risk: Low — unique angle, high shareability

## Risk Assessment
- Avoid: Political commentary trending videos — brand misalignment
- Caution: Music trends — licensing complexity for brand channels
```

## Error Handling

<AccordionGroup>
  <Accordion title="mostPopular requires regionCode">Without `regionCode`, results default to the US. Set this explicitly for non-US brands. Not all regions return 50 results.</Accordion>
  <Accordion title="Category ID mapping">YouTube uses numeric category IDs (e.g., 17 = Sports). The `videoCategories` endpoint translates these. Some categories are region-specific.</Accordion>
  <Accordion title="Quota efficiency">This entire job uses \~2 quota units (1 for `videos.list`, 1 for `videoCategories`). It's the most quota-efficient job in this guide.</Accordion>
</AccordionGroup>

***

<CardGroup cols={2}>
  <Card title="YouTube Integration" icon="arrow-left" href="/integrations/youtube" />

  <Card title="Mave Agent" icon="brain" href="/api-reference/mave" />
</CardGroup>
