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.

Scenario

Your Vimeo account holds hundreds of marketing videos spanning campaigns, product demos, testimonials, and event recordings — but you have no systematic way to know which are your strongest creative assets. This job pulls your entire video library via GET /me/videos, uploads each to Mavera Assets, and runs Video Analysis scoring every video on emotional impact, message clarity, and behavioral effectiveness. The result is a ranked catalog that tells you which videos to promote, which to retire, and which patterns your best-performing creative shares.

Architecture

Code

import os, requests, time

VM = os.environ["VIMEO_ACCESS_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
VM_BASE = "https://api.vimeo.com"
MV_BASE = "https://app.mavera.io/api/v1"
VM_H = {"Authorization": f"Bearer {VM}", "Accept": "application/vnd.vimeo.*+json;version=3.4"}
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

# 1. Fetch entire video library (paginated, 100 per page)
all_videos = []
page = 1
while True:
    resp = requests.get(f"{VM_BASE}/me/videos", headers=VM_H, params={
        "per_page": 100, "page": page,
        "fields": "uri,name,link,duration,stats,created_time,pictures.sizes",
        "sort": "date", "direction": "desc",
    })
    if resp.status_code == 429:
        retry = int(resp.headers.get("Retry-After", 60))
        print(f"Rate limited — waiting {retry}s")
        time.sleep(retry)
        continue
    resp.raise_for_status()
    data = resp.json()

    for v in data.get("data", []):
        vid_id = v["uri"].split("/")[-1]
        all_videos.append({
            "id": vid_id, "name": v["name"], "link": v["link"],
            "duration": v.get("duration", 0),
            "plays": v.get("stats", {}).get("plays", 0),
            "created": v.get("created_time", "")[:10],
        })

    if not data.get("paging", {}).get("next"):
        break
    page += 1
    time.sleep(0.6)

print(f"Library size: {len(all_videos)} videos")

# 2. Analyze each video via Mavera (sample top 20 by plays)
scored = []
for video in sorted(all_videos, key=lambda v: -v["plays"])[:20]:
    upload = requests.post(f"{MV_BASE}/assets", headers=MV_H, json={
        "url": video["link"], "name": video["name"][:80], "type": "video",
    }).json()

    analysis = requests.post(f"{MV_BASE}/video-analysis", headers=MV_H, json={
        "asset_id": upload["id"],
        "analysis_types": [
            "message_clarity", "emotional_impact", "behavioral_effectiveness",
            "hook_score", "pacing", "cognitive_load",
        ],
        "metadata": {"vimeo_id": video["id"], "plays": video["plays"]},
    }).json()

    for _ in range(30):
        time.sleep(3)
        status = requests.get(
            f"{MV_BASE}/video-analysis/{analysis['id']}", headers=MV_H
        ).json()
        if status.get("status") == "completed":
            break

    r = status.get("results", {})
    scored.append({
        **video,
        "clarity": r.get("message_clarity", {}).get("score", 0),
        "emotion": r.get("emotional_impact", {}).get("score", 0),
        "behavior": r.get("behavioral_effectiveness", {}).get("score", 0),
        "hook": r.get("hook_score", {}).get("score", 0),
    })
    time.sleep(1)

# 3. Catalog ranking via Mave
scores_block = "\n".join(
    f"- \"{s['name'][:50]}\" — plays: {s['plays']:,}, clarity: {s['clarity']}/100, "
    f"emotion: {s['emotion']}/100, behavior: {s['behavior']}/100, hook: {s['hook']}/100"
    for s in scored
)

ranking = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
    "message": f"""Rank this marketing video library by creative quality.

SCORED CATALOG ({len(scored)} videos):
{scores_block}

Produce:
1. **Tier 1 — Promote** (top creative assets): which videos and why
2. **Tier 2 — Optimize** (strong but fixable): what to improve
3. **Tier 3 — Retire** (underperforming creative): replace with what
4. **Patterns**: What do Tier 1 videos share that Tier 3 lacks?
5. **Recommendations**: 3 specific creative briefs for new videos based on the winning patterns""",
}).json()

print("MARKETING VIDEO LIBRARY ANALYSIS")
print("=" * 60)
for s in sorted(scored, key=lambda x: -x["emotion"]):
    print(f"  {s['name'][:40]:<42} Plays:{s['plays']:>8,}  "
          f"Clarity:{s['clarity']:>3}  Emotion:{s['emotion']:>3}  "
          f"Hook:{s['hook']:>3}")
print("\n" + ranking.get("content", "")[:2000])

Example Output

MARKETING VIDEO LIBRARY ANALYSIS
============================================================
  Q1 Brand Campaign — Feel the Differen    Plays:  84,200  Clarity: 91  Emotion: 94  Hook: 87
  Customer Story — Acme Corp Transformat   Plays:  42,100  Clarity: 88  Emotion: 82  Hook: 76
  Product Demo — Enterprise Dashboard      Plays:  31,500  Clarity: 79  Emotion: 45  Hook: 62
  Webinar Replay — State of the Market     Plays:  18,300  Clarity: 72  Emotion: 38  Hook: 41
  Trade Show Booth Walkthrough             Plays:   9,400  Clarity: 54  Emotion: 29  Hook: 33

## Catalog Ranking

### Tier 1 — Promote
- "Q1 Brand Campaign" (composite: 91/100) — highest emotional impact in library.
  Opens with face close-up and music swell in first 2 seconds. Redistribute as
  paid social hero and homepage embed.
- "Customer Story — Acme Corp" (composite: 82/100) — strong narrative arc with
  clear problem→solution→result structure. Feature on case study landing pages.

### Tier 2 — Optimize
- "Product Demo" — clarity 79 but emotion 45. The demo is thorough but clinical.
  Re-cut with a customer voiceover narrating their workflow instead of feature
  bullet points. Hook score 62 suggests the first 5 seconds need a problem statement.

### Tier 3 — Retire
- "Trade Show Booth Walkthrough" — all scores below 55. This format doesn't
  translate to digital. Replace with a 60-second highlight reel from the booth
  with customer sound bites.

### Patterns
Tier 1 videos share: human faces in first frame, emotional audio within 2 seconds,
single-message focus (1 CTA per video). Tier 3 videos share: wide establishing shots,
multiple messages crammed in, no clear CTA.

Error Handling

Vimeo returns a Retry-After header in seconds. The code respects this value. Free accounts hit ~100 req/min; Pro/Business accounts get ~600 req/10 min. Add exponential backoff for large libraries.
The video_files scope is required to access download URLs for some endpoints. Without it, use the link field (public Vimeo URL) for Mavera asset uploads instead.
Libraries with 500+ videos require multiple pages. Each page costs one API call. The code paginates automatically but add a 600ms delay between pages to stay within rate limits.

What’s Next

Vimeo Integration

Back to Vimeo integration overview

Pre-Publish Creative Testing

Gate publishing with quality thresholds

Video Analysis API

Full reference for POST /api/v1/video-analysis

Mave Agent

Full reference for POST /api/v1/mave/chat