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

# Competitor Reel

> Download publicly available competitor ads, upload to Mavera, run Video Analysis on each, and chat with Mave to extract competitive creative intelligence

## Mavera Surfaces

| Surface                                             | Role                                                                        |
| --------------------------------------------------- | --------------------------------------------------------------------------- |
| **Files** (`POST /files/upload-url`, `POST /files`) | Upload competitor video ads                                                 |
| **Video Analysis** (`POST /video-analyses`)         | Frame-level scoring of each competitor creative                             |
| **Mave** (`POST /mave/chat`)                        | Synthesize cross-competitor findings into a competitive intelligence report |

***

## What Value Does Mavera Add?

| Value                 | How                                                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Insurance**         | Know what competitors are doing *before* you finalize your own creative. Avoid launching something they already dominate. |
| **Opening new doors** | Frame-level analysis reveals tactics invisible from just watching: pacing patterns, CTA timing, emotional arcs.           |
| **Saving time**       | A human competitive review is subjective and takes days. This pipeline produces a structured report in minutes.           |

***

## When to Use This

* Quarterly competitive review: what creative strategies are competitors using?
* Pre-campaign: before building your next ad, see what's already in market.
* Client pitch: show up with data on the competitive landscape.
* Creative team onboarding: give new hires a data-driven view of the competitive environment.

<Info>
  **Where to find competitor ads:** Meta Ad Library, TikTok Creative Center, YouTube channels, or screen-recording public content. This playbook assumes you already have the video files locally.
</Info>

***

## What You Need

| Requirement                        | Details                                                                                              |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Mavera API key**                 | Starts with `mvra_live_`. Get one at [Developer Settings](https://app.mavera.io/settings/developer). |
| **Workspace ID**                   | From your dashboard URL (`ws_...`).                                                                  |
| **3–8 competitor video ads**       | MP4 or MOV, 15–60 s each. At least 2 competitors for meaningful comparison.                          |
| **Your own ad (optional)**         | Include one of yours for direct benchmarking.                                                        |
| **Credits**                        | \~100–250 per video + \~15–30 for Mave. See [Credits Estimate](#credits-estimate).                   |
| **Python 3.8+** or **Node.js 18+** | `requests` for Python; native `fetch` for Node.                                                      |

```
MAVERA_API_KEY=mvra_live_your_key_here
MAVERA_WORKSPACE_ID=ws_your_workspace_id
```

***

## The Flow

```mermaid theme={"dark"}
flowchart LR
    A["Collect Ads"] --> B["Upload"]
    B --> C["Video Analysis"]
    C --> D["Comparison Matrix"]
    D --> E["Mave Report"]
```

<Steps>
  <Step title="Collect competitor ads">
    Download 3–8 ads from public sources. Organize by competitor: `nike_hero_30s.mp4`, `adidas_brand_45s.mp4`.
  </Step>

  <Step title="Upload all ads to Mavera">
    Files API uploads with competitor name auto-extracted from filenames.
  </Step>

  <Step title="Run Video Analysis on each">
    Batch create + poll with a competitive-analysis-oriented goal prompt.
  </Step>

  <Step title="Build comparison matrix and ask Mave">
    Group by competitor, compute averages, feed into Mave: "What are our competitors doing better in their video creative?"
  </Step>
</Steps>

***

## Stage 1 — Upload Competitor Ads

<CodeGroup>
  ```python Python theme={"dark"}
  import os, re, time, json, glob, requests
  from collections import defaultdict

  API_KEY = os.environ["MAVERA_API_KEY"]
  WORKSPACE_ID = os.environ["MAVERA_WORKSPACE_ID"]
  BASE = "https://app.mavera.io/api/v1"
  HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}


  def upload_video(path: str) -> dict:
      with open(path, "rb") as f:
          content = f.read()
      name = os.path.basename(path)
      mime = "video/mp4" if path.lower().endswith(".mp4") else "video/quicktime"

      url_resp = requests.post(f"{BASE}/files/upload-url", headers=HEADERS, json={
          "file_name": name, "file_type": mime, "file_size": len(content), "workspace_id": WORKSPACE_ID,
      }).json()
      if "error" in url_resp:
          raise Exception(url_resp["error"]["message"])

      requests.put(url_resp["upload_url"], data=content, headers={"Content-Type": mime}).raise_for_status()

      file_rec = requests.post(f"{BASE}/files", headers=HEADERS, json={
          "name": name, "type": mime, "url": url_resp["public_url"],
          "workspace_id": WORKSPACE_ID, "file_size": len(content),
      }).json()
      if "error" in file_rec:
          raise Exception(file_rec["error"]["message"])
      return {"id": file_rec["id"], "name": name}


  def extract_competitor(filename: str) -> str:
      """Extract competitor name from filename prefix before first _ or -."""
      base = os.path.splitext(filename)[0]
      return re.split(r"[_\-]", base)[0]


  def upload_competitor_ads(directory: str) -> list[dict]:
      paths = sorted(glob.glob(os.path.join(directory, "*.mp4"))
                     + glob.glob(os.path.join(directory, "*.mov")))
      if not paths:
          raise FileNotFoundError(f"No video files in {directory}")
      assets = []
      for p in paths:
          asset = upload_video(p)
          asset["competitor"] = extract_competitor(asset["name"])
          print(f"  {asset['name']} ({asset['competitor']}) → {asset['id']}")
          assets.append(asset)
      return assets
  ```

  ```javascript JavaScript theme={"dark"}
  const fs = require("fs");
  const path = require("path");

  const API_KEY = process.env.MAVERA_API_KEY;
  const WORKSPACE_ID = process.env.MAVERA_WORKSPACE_ID;
  const BASE = "https://app.mavera.io/api/v1";
  const HEADERS = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" };

  async function uploadVideo(videoPath) {
    const content = fs.readFileSync(videoPath);
    const name = path.basename(videoPath);
    const mime = videoPath.toLowerCase().endsWith(".mp4") ? "video/mp4" : "video/quicktime";

    const urlResp = await fetch(`${BASE}/files/upload-url`, {
      method: "POST", headers: HEADERS,
      body: JSON.stringify({ file_name: name, file_type: mime, file_size: content.length, workspace_id: WORKSPACE_ID }),
    }).then((r) => r.json());
    if (urlResp.error) throw new Error(urlResp.error.message);

    await fetch(urlResp.upload_url, { method: "PUT", body: content, headers: { "Content-Type": mime } });

    const fileRec = await fetch(`${BASE}/files`, {
      method: "POST", headers: HEADERS,
      body: JSON.stringify({ name, type: mime, url: urlResp.public_url, workspace_id: WORKSPACE_ID, file_size: content.length }),
    }).then((r) => r.json());
    if (fileRec.error) throw new Error(fileRec.error.message);
    return { id: fileRec.id, name };
  }

  function extractCompetitor(filename) {
    return path.basename(filename, path.extname(filename)).split(/[_-]/)[0] || "unknown";
  }

  async function uploadCompetitorAds(directory) {
    const files = fs.readdirSync(directory).filter((f) => /\.(mp4|mov)$/i.test(f)).sort().map((f) => path.join(directory, f));
    if (!files.length) throw new Error(`No video files in ${directory}`);
    const assets = [];
    for (const fp of files) {
      const asset = await uploadVideo(fp);
      asset.competitor = extractCompetitor(asset.name);
      console.log(`  ${asset.name} (${asset.competitor}) → ${asset.id}`);
      assets.push(asset);
    }
    return assets;
  }
  ```
</CodeGroup>

<Tip>
  Use consistent naming: `competitorname_adtype.mp4` (e.g., `nike_hero_30s.mp4`). The pipeline auto-extracts the competitor name from the prefix before the first `_` or `-`.
</Tip>

***

## Stage 2 — Video Analysis (Batch)

<CodeGroup>
  ```python Python theme={"dark"}
  def create_analysis(asset_id: str, label: str, competitor: str) -> dict:
      resp = requests.post(f"{BASE}/video-analyses", headers=HEADERS, json={
          "title": f"Competitor Reel: {competitor} — {label}", "asset_id": asset_id,
          "goal": "Analyze competitor ad: hook effectiveness, emotional arc, pacing, CTA, brand integration",
          "brand": competitor, "product": "Competitor product",
          "primary_intent": "Understand competitor creative strategy",
          "chunk_duration": 5, "frames_per_chunk": 3, "workspace_id": WORKSPACE_ID,
      }).json()
      if "error" in resp:
          raise Exception(resp["error"]["message"])
      return resp


  def poll_analysis(analysis_id: str, timeout_min: int = 20) -> dict:
      for _ in range(timeout_min * 4):
          resp = requests.get(f"{BASE}/video-analyses/{analysis_id}", headers=HEADERS).json()
          if "error" in resp:
              raise Exception(resp["error"]["message"])
          if resp["status"] == "COMPLETED":
              return resp
          if resp["status"] == "FAILED":
              raise Exception(f"Analysis {analysis_id} failed")
          time.sleep(15)
      raise TimeoutError(f"Analysis {analysis_id} timed out")


  def analyze_all_competitors(assets: list[dict]) -> list[dict]:
      jobs = []
      for asset in assets:
          job = create_analysis(asset["id"], asset["name"], asset["competitor"])
          print(f"  Created analysis {job['id']} for {asset['competitor']}: {asset['name']}")
          jobs.append({"analysis_id": job["id"], "name": asset["name"],
                        "competitor": asset["competitor"], "asset_id": asset["id"]})

      results = []
      for job in jobs:
          result = poll_analysis(job["analysis_id"])
          metrics = result.get("results", {}).get("full_video_metrics", {})
          results.append({"name": job["name"], "competitor": job["competitor"],
                          "asset_id": job["asset_id"], "metrics": metrics,
                          "chunks": metrics.get("chunks", [])})
          print(f"  Completed {job['competitor']}: {job['name']} (overall={metrics.get('overall_score')})")
      return results
  ```

  ```javascript JavaScript theme={"dark"}
  async function createAnalysis(assetId, label, competitor) {
    const resp = await fetch(`${BASE}/video-analyses`, {
      method: "POST", headers: HEADERS,
      body: JSON.stringify({
        title: `Competitor Reel: ${competitor} — ${label}`, asset_id: assetId,
        goal: "Analyze competitor ad: hook effectiveness, emotional arc, pacing, CTA, brand integration",
        brand: competitor, product: "Competitor product",
        primary_intent: "Understand competitor creative strategy",
        chunk_duration: 5, frames_per_chunk: 3, workspace_id: WORKSPACE_ID,
      }),
    }).then((r) => r.json());
    if (resp.error) throw new Error(resp.error.message);
    return resp;
  }

  async function pollAnalysis(analysisId, timeoutMin = 20) {
    for (let i = 0; i < timeoutMin * 4; i++) {
      const resp = await fetch(`${BASE}/video-analyses/${analysisId}`, { headers: HEADERS }).then((r) => r.json());
      if (resp.error) throw new Error(resp.error.message);
      if (resp.status === "COMPLETED") return resp;
      if (resp.status === "FAILED") throw new Error(`Analysis ${analysisId} failed`);
      await new Promise((r) => setTimeout(r, 15000));
    }
    throw new Error(`Analysis ${analysisId} timed out`);
  }

  async function analyzeAllCompetitors(assets) {
    const jobs = [];
    for (const asset of assets) {
      const job = await createAnalysis(asset.id, asset.name, asset.competitor);
      console.log(`  Created analysis ${job.id} for ${asset.competitor}: ${asset.name}`);
      jobs.push({ analysisId: job.id, name: asset.name, competitor: asset.competitor, assetId: asset.id });
    }
    const results = [];
    for (const job of jobs) {
      const result = await pollAnalysis(job.analysisId);
      const metrics = result.results?.full_video_metrics || {};
      results.push({ name: job.name, competitor: job.competitor, assetId: job.assetId,
                      metrics, chunks: metrics.chunks || [] });
      console.log(`  Completed ${job.competitor}: ${job.name} (overall=${metrics.overall_score})`);
    }
    return results;
  }
  ```
</CodeGroup>

***

## Stage 3 — Build the Comparison Matrix

Group by competitor. For competitors with multiple ads, compute averages.

<CodeGroup>
  ```python Python theme={"dark"}
  def build_comparison_matrix(results: list[dict]) -> dict:
      grouped = defaultdict(list)
      for r in results:
          grouped[r["competitor"]].append(r)

      matrix = {}
      for comp, ads in grouped.items():
          all_m = [a["metrics"] for a in ads]
          avg = lambda key: round(sum(m.get(key, 0) for m in all_m) / len(all_m), 1)
          matrix[comp] = {
              "ad_count": len(ads), "ads": [a["name"] for a in ads],
              "avg_overall": avg("overall_score"), "avg_emotion": avg("emotional_impact"),
              "avg_attention": avg("attention_score"), "avg_cta": avg("cta_effectiveness"),
              "best_ad": max(ads, key=lambda a: a["metrics"].get("overall_score", 0))["name"],
          }
      return matrix


  def format_competitor_table(matrix: dict) -> str:
      lines = ["| Competitor | Ads | Avg Overall | Avg Emotion | Avg Attention | Avg CTA | Best Ad |",
               "|------------|-----|-------------|-------------|---------------|---------|---------|"]
      for comp, d in sorted(matrix.items(), key=lambda x: x[1]["avg_overall"], reverse=True):
          lines.append(f"| {comp} | {d['ad_count']} | {d['avg_overall']}/100 | {d['avg_emotion']}/10 "
                        f"| {d['avg_attention']}/10 | {d['avg_cta']}/10 | {d['best_ad']} |")
      return "\n".join(lines)


  def format_individual_table(results: list[dict]) -> str:
      ranked = sorted(results, key=lambda r: r["metrics"].get("overall_score", 0), reverse=True)
      lines = ["| Competitor | Ad | Overall | Emotion | Attention | Brand Recall | CTA |",
               "|------------|----|---------|---------|-----------|--------------|----|"]
      for r in ranked:
          m = r["metrics"]
          lines.append(f"| {r['competitor']} | {r['name']} | {m.get('overall_score', '—')}/100 "
                        f"| {m.get('emotional_impact', '—')}/10 | {m.get('attention_score', '—')}/10 "
                        f"| {m.get('brand_recall_likelihood', '—')} | {m.get('cta_effectiveness', '—')}/10 |")
      return "\n".join(lines)


  def format_hook_comparison(results: list[dict]) -> str:
      lines = ["| Competitor | Ad | Hook Engagement | Hook Emotion | Hook Attention |",
               "|------------|----|-----------------|--------------:|----------------|"]
      for r in results:
          c = r["chunks"][0] if r["chunks"] else {}
          lines.append(f"| {r['competitor']} | {r['name']} | {c.get('engagement', '—')}/100 "
                        f"| {c.get('emotional_intensity', '—')}/10 | {c.get('attention', '—')}/10 |")
      return "\n".join(lines)
  ```

  ```javascript JavaScript theme={"dark"}
  function buildComparisonMatrix(results) {
    const grouped = {};
    for (const r of results) { (grouped[r.competitor] ||= []).push(r); }
    const matrix = {};
    for (const [comp, ads] of Object.entries(grouped)) {
      const metrics = ads.map((a) => a.metrics);
      const avg = (k) => +(metrics.reduce((s, m) => s + (m[k] || 0), 0) / metrics.length).toFixed(1);
      matrix[comp] = {
        adCount: ads.length, ads: ads.map((a) => a.name),
        avgOverall: avg("overall_score"), avgEmotion: avg("emotional_impact"),
        avgAttention: avg("attention_score"), avgCta: avg("cta_effectiveness"),
        bestAd: ads.reduce((a, b) => (b.metrics.overall_score || 0) > (a.metrics.overall_score || 0) ? b : a).name,
      };
    }
    return matrix;
  }

  function formatCompetitorTable(matrix) {
    const lines = ["| Competitor | Ads | Avg Overall | Avg Emotion | Avg Attention | Avg CTA | Best Ad |",
                    "|------------|-----|-------------|-------------|---------------|---------|---------|"];
    for (const [comp, d] of Object.entries(matrix).sort((a, b) => b[1].avgOverall - a[1].avgOverall))
      lines.push(`| ${comp} | ${d.adCount} | ${d.avgOverall}/100 | ${d.avgEmotion}/10 | ${d.avgAttention}/10 | ${d.avgCta}/10 | ${d.bestAd} |`);
    return lines.join("\n");
  }

  function formatIndividualTable(results) {
    const ranked = [...results].sort((a, b) => (b.metrics.overall_score || 0) - (a.metrics.overall_score || 0));
    const lines = ["| Competitor | Ad | Overall | Emotion | CTA |", "|------------|----|---------|---------|----|"];
    for (const r of ranked) { const m = r.metrics;
      lines.push(`| ${r.competitor} | ${r.name} | ${m.overall_score ?? "—"}/100 | ${m.emotional_impact ?? "—"}/10 | ${m.cta_effectiveness ?? "—"}/10 |`);
    }
    return lines.join("\n");
  }
  ```
</CodeGroup>

***

## Stage 4 — Mave Competitive Intelligence

<CodeGroup>
  ```python Python theme={"dark"}
  def generate_competitive_report(results: list[dict], matrix: dict) -> str:
      comp_table = format_competitor_table(matrix)
      ind_table = format_individual_table(results)
      hook_table = format_hook_comparison(results)
      competitors = list(matrix.keys())

      prompt = f"""You are a senior creative strategist conducting competitive creative intelligence.

  ## Competitor Summary (Averages)
  {comp_table}

  ## Individual Ad Scores
  {ind_table}

  ## Hook Comparison (First 5 Seconds)
  {hook_table}

  ## Competitors: {', '.join(competitors)}

  ## Your Task
  Produce a competitive creative intelligence report:
  1. **Executive Summary** — Who's winning the creative war and why? 3 sentences.
  2. **Competitor Rankings** — Rank by creative quality. What separates #1 from last?
  3. **What Competitors Do Better** — Specific elements where competitors outperform. Cite scores.
  4. **Where Competitors Are Weak** — Weaknesses to exploit. Low emotion? Poor hooks? Weak CTAs?
  5. **Hook Strategies** — Compare openings across competitors. Who stops the scroll best?
  6. **Emotional Arcs** — Who builds emotional journeys vs flat-line ads?
  7. **Brand Integration** — How do competitors integrate branding? Early logo? End card?
  8. **CTA Strategies** — When and how do competitors deliver their CTA?
  9. **Opportunities** — 5 creative strategies to steal, adapt, or counter.
  10. **Threats** — 3 competitor trends that could hurt your position if ignored.

  Reference competitor names and ad filenames. Cite scores."""

      resp = requests.post(f"{BASE}/mave/chat", headers=HEADERS,
                           json={"message": prompt}, timeout=180).json()
      if "error" in resp:
          raise Exception(resp["error"]["message"])
      return resp["content"]
  ```

  ```javascript JavaScript theme={"dark"}
  async function generateCompetitiveReport(results, matrix) {
    const compTable = formatCompetitorTable(matrix);
    const indTable = formatIndividualTable(results);
    const competitors = Object.keys(matrix);

    const prompt = `You are a senior creative strategist conducting competitive creative intelligence.

  ## Competitor Summary
  ${compTable}

  ## Individual Ad Scores
  ${indTable}

  ## Competitors: ${competitors.join(", ")}

  ## Your Task
  Produce a competitive creative intelligence report:
  1. **Executive Summary** — Who's winning and why? 3 sentences.
  2. **Competitor Rankings** — Rank by creative quality. What separates #1 from last?
  3. **What Competitors Do Better** — Elements where they outperform. Cite scores.
  4. **Where Competitors Are Weak** — Weaknesses to exploit.
  5. **Hook Strategies** — Compare openings. Who stops the scroll best?
  6. **Emotional Arcs** — Who builds emotional journeys?
  7. **Brand Integration** — How do competitors brand? Early logo? End card?
  8. **CTA Strategies** — When and how is the CTA delivered?
  9. **Opportunities** — 5 strategies to steal, adapt, or counter.
  10. **Threats** — 3 trends that could hurt if ignored.

  Reference names and filenames. Cite scores.`;

    const resp = await fetch(`${BASE}/mave/chat`, {
      method: "POST", headers: HEADERS,
      body: JSON.stringify({ message: prompt }), signal: AbortSignal.timeout(180000),
    }).then((r) => r.json());
    if (resp.error) throw new Error(resp.error.message);
    return resp.content;
  }
  ```
</CodeGroup>

***

## Running the Full Pipeline

<CodeGroup>
  ```python Python theme={"dark"}
  def run_competitor_reel(ad_directory: str = "./competitor_ads"):
      assets = upload_competitor_ads(ad_directory)
      results = analyze_all_competitors(assets)
      matrix = build_comparison_matrix(results)
      print(format_competitor_table(matrix))
      report = generate_competitive_report(results, matrix)

      competitors = set(a["competitor"] for a in assets)
      with open("competitor_reel_report.md", "w") as f:
          f.write(f"# Competitive Intelligence — {time.strftime('%B %Y')}\n\n")
          f.write(format_competitor_table(matrix) + "\n\n" + format_individual_table(results))
          f.write(f"\n\n---\n\n{report}")
      print("Saved to competitor_reel_report.md")
      return matrix, report

  if __name__ == "__main__":
      import sys
      run_competitor_reel(sys.argv[1] if len(sys.argv) > 1 else "./competitor_ads")
  ```

  ```javascript JavaScript theme={"dark"}
  async function runCompetitorReel(dir = "./competitor_ads") {
    const assets = await uploadCompetitorAds(dir);
    const results = await analyzeAllCompetitors(assets);
    const matrix = buildComparisonMatrix(results);
    console.log(formatCompetitorTable(matrix));
    const report = await generateCompetitiveReport(results, matrix);
    const month = new Date().toLocaleString("default", { month: "long", year: "numeric" });
    fs.writeFileSync("competitor_reel_report.md",
      `# Competitive Creative Intelligence — ${month}\n\n${formatCompetitorTable(matrix)}\n\n` +
      `${formatIndividualTable(results)}\n\n---\n\n${report}`);
    return { matrix, report };
  }
  runCompetitorReel(process.argv[2] || "./competitor_ads");
  ```
</CodeGroup>

***

## Example Output

```markdown theme={"dark"}
# Competitive Creative Intelligence — March 2026

**Competitors:** adidas, nike, ours, puma | **Total ads:** 7

| Competitor | Ads | Avg Overall | Avg Emotion | Avg CTA | Best Ad         |
|------------|-----|-------------|-------------|---------|-----------------|
| nike       | 2   | 86.5/100    | 9.0/10      | 7.5/10  | nike_hero_30s   |
| ours       | 1   | 78.0/100    | 7.0/10      | 8.0/10  | ours_hero_30s   |
| adidas     | 2   | 74.0/100    | 7.5/10      | 6.5/10  | adidas_brand_45s|
| puma       | 2   | 65.0/100    | 6.0/10      | 5.0/10  | puma_launch_30s |

## Executive Summary
Nike dominates (86.5/100) via emotional intensity (9/10) and strong hooks.
Our CTA (8/10) is competitive but overall trails by 8.5 points. Puma's weak
hooks and low emotion are exploitable...
```

***

## Variations

<AccordionGroup>
  <Accordion title="Include your own ads for direct benchmarking">
    Name your ads with an `ours_` prefix. They'll appear in the matrix for direct comparison.
  </Accordion>

  <Accordion title="Quarterly tracking">
    Save each matrix to JSON for trend analysis over time.
  </Accordion>

  <Accordion title="Add Focus Group for audience reaction">
    After Video Analysis, run a Focus Group showing the top competitor ad vs yours for simulated audience preference.
  </Accordion>

  <Accordion title="Platform-specific analysis">
    Tag files by platform (`nike_tiktok_hero.mp4`) and add platform as a grouping dimension in the matrix.
  </Accordion>

  <Accordion title="Override competitor name extraction">
    Use a manual `COMPETITOR_MAP` dict if filenames don't follow the `competitor_type.mp4` convention.
  </Accordion>
</AccordionGroup>

***

## Credits Estimate

| Stage               | Typical Cost      | Notes                       |
| ------------------- | ----------------- | --------------------------- |
| File uploads (×N)   | 0                 | Free                        |
| Video Analysis (×N) | 100–250 each      | Depends on video length     |
| Mave synthesis      | 15–30             | Single research query       |
| **6-ad reel**       | **\~615–1,530**   | 5 competitors + 1 own       |
| **10-ad reel**      | **\~1,015–2,530** | Large competitive landscape |

<Tip>
  Prioritize quality over quantity. 2 ads from each of 3 key competitors gives more actionable intel than 1 ad from 8 competitors.
</Tip>

***

## See Also

<CardGroup cols={2}>
  <Card title="Ad Creative Audit" icon="video" href="/playbooks/ad-creative-audit">
    Audit your own ads instead of competitors'
  </Card>

  <Card title="Hook Analysis Sprint" icon="stopwatch" href="/playbooks/hook-analysis-sprint">
    Deep-dive into hook strategies across variants
  </Card>

  <Card title="Video + Focus Group Double" icon="layer-group" href="/playbooks/video-focus-group-double">
    Layer competitor analysis with synthetic audience reactions
  </Card>

  <Card title="Competitive Ad Analysis Pipeline" icon="scale-balanced" href="/cookbooks/competitive-ad-analysis-pipeline">
    Head-to-head pipeline with Focus Group and Mave report
  </Card>

  <Card title="Video Analysis" icon="video" href="/features/video-analysis">
    Metrics reference and chunk configuration
  </Card>

  <Card title="Mave Agent" icon="brain" href="/features/mave-agent">
    Research agent for synthesis and recommendations
  </Card>
</CardGroup>
