Skip to main content

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.

Mavera Surfaces

SurfaceRole
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?

ValueHow
InsuranceKnow what competitors are doing before you finalize your own creative. Avoid launching something they already dominate.
Opening new doorsFrame-level analysis reveals tactics invisible from just watching: pacing patterns, CTA timing, emotional arcs.
Saving timeA 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.
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.

What You Need

RequirementDetails
Mavera API keyStarts with mvra_live_. Get one at Developer Settings.
Workspace IDFrom your dashboard URL (ws_...).
3–8 competitor video adsMP4 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.
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

1

Collect competitor ads

Download 3–8 ads from public sources. Organize by competitor: nike_hero_30s.mp4, adidas_brand_45s.mp4.
2

Upload all ads to Mavera

Files API uploads with competitor name auto-extracted from filenames.
3

Run Video Analysis on each

Batch create + poll with a competitive-analysis-oriented goal prompt.
4

Build comparison matrix and ask Mave

Group by competitor, compute averages, feed into Mave: “What are our competitors doing better in their video creative?”

Stage 1 — Upload Competitor Ads

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

Stage 2 — Video Analysis (Batch)

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

Stage 3 — Build the Comparison Matrix

Group by competitor. For competitors with multiple ads, compute averages.
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)

Stage 4 — Mave Competitive Intelligence

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"]

Running the Full Pipeline

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

Example Output

# 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

Name your ads with an ours_ prefix. They’ll appear in the matrix for direct comparison.
Save each matrix to JSON for trend analysis over time.
After Video Analysis, run a Focus Group showing the top competitor ad vs yours for simulated audience preference.
Tag files by platform (nike_tiktok_hero.mp4) and add platform as a grouping dimension in the matrix.
Use a manual COMPETITOR_MAP dict if filenames don’t follow the competitor_type.mp4 convention.

Credits Estimate

StageTypical CostNotes
File uploads (×N)0Free
Video Analysis (×N)100–250 eachDepends on video length
Mave synthesis15–30Single research query
6-ad reel~615–1,5305 competitors + 1 own
10-ad reel~1,015–2,530Large competitive landscape
Prioritize quality over quantity. 2 ads from each of 3 key competitors gives more actionable intel than 1 ad from 8 competitors.

See Also

Ad Creative Audit

Audit your own ads instead of competitors’

Hook Analysis Sprint

Deep-dive into hook strategies across variants

Video + Focus Group Double

Layer competitor analysis with synthetic audience reactions

Competitive Ad Analysis Pipeline

Head-to-head pipeline with Focus Group and Mave report

Video Analysis

Metrics reference and chunk configuration

Mave Agent

Research agent for synthesis and recommendations