Scenario
You have Vimeo engagement data (plays, finishes, average percent watched, engagement graphs) and Mavera Video Analysis scores — but you don’t know which Mavera metric actually predicts real-world engagement. This job pulls engagement stats for your video library from Vimeo, runs Video Analysis on the same videos, then asks Mave to correlate the two datasets: “Which Mavera metric best predicts real engagement?” The result is a data-driven answer to which creative qualities drive actual viewer behavior — so you can optimize future videos for the metrics that matter.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. Pull videos with engagement stats
resp = requests.get(f"{VM_BASE}/me/videos", headers=VM_H, params={
"per_page": 50, "sort": "plays", "direction": "desc",
"fields": "uri,name,link,duration,stats,metadata.connections.likes.total,"
"metadata.connections.comments.total",
}).json()
videos = []
for v in resp.get("data", []):
vid_id = v["uri"].split("/")[-1]
stats = v.get("stats", {})
videos.append({
"id": vid_id, "name": v["name"], "link": v["link"],
"duration": v.get("duration", 0),
"plays": stats.get("plays", 0),
"finishes": stats.get("finishes", 0),
"avg_watched": round(stats.get("finishes", 0) / max(stats.get("plays", 1), 1) * 100, 1),
"likes": v.get("metadata", {}).get("connections", {}).get("likes", {}).get("total", 0),
"comments": v.get("metadata", {}).get("connections", {}).get("comments", {}).get("total", 0),
})
print(f"Pulled engagement data for {len(videos)} videos")
# 2. Run Mavera Video Analysis on top 15
combined = []
for video in videos[:15]:
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", "hook_score",
"behavioral_effectiveness", "pacing", "cognitive_load",
],
}).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", {})
combined.append({
**video,
"clarity": r.get("message_clarity", {}).get("score", 0),
"emotion": r.get("emotional_impact", {}).get("score", 0),
"hook": r.get("hook_score", {}).get("score", 0),
"behavior": r.get("behavioral_effectiveness", {}).get("score", 0),
"pacing_score": r.get("pacing", {}).get("score", 0),
"cog_load": r.get("cognitive_load", {}).get("average", 0),
})
time.sleep(1)
# 3. Build correlation dataset for Mave
data_block = "\n".join(
f"Video: \"{c['name'][:40]}\" | "
f"Plays: {c['plays']:,} | Finish%: {c['avg_watched']}% | Likes: {c['likes']} | Comments: {c['comments']} || "
f"Clarity: {c['clarity']} | Emotion: {c['emotion']} | Hook: {c['hook']} | "
f"Behavior: {c['behavior']} | Pacing: {c['pacing_score']} | CogLoad: {c['cog_load']}"
for c in combined
)
correlation = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
"message": f"""Analyze the correlation between Mavera creative scores and real Vimeo engagement.
DATASET ({len(combined)} videos — engagement metrics || Mavera scores):
{data_block}
Produce:
1. **Best Predictor**: Which single Mavera metric most strongly predicts real engagement (plays, finish rate, likes)?
2. **Metric-by-Metric Correlation**: For each Mavera metric, how well does it predict each engagement metric? (strong/moderate/weak/none)
3. **Surprising Findings**: Any metrics that DON'T correlate with engagement despite seeming important?
4. **Composite Formula**: Suggest a weighted combination of Mavera metrics that best predicts finish rate
5. **Actionable Rule**: "If you optimize for [X Mavera metric], you'll see the biggest lift in [Y engagement metric]"
6. **Outliers**: Videos where Mavera scores and engagement diverge — what explains the gap?""",
}).json()
print("VIDEO ENGAGEMENT × MAVERA SCORING CORRELATION")
print("=" * 65)
print(f"{'Video':<35} {'Plays':>8} {'Finish%':>8} {'Emotion':>8} {'Hook':>6} {'Clarity':>8}")
print("-" * 65)
for c in combined:
print(f" {c['name'][:33]:<35} {c['plays']:>8,} {c['avg_watched']:>7.1f}% "
f"{c['emotion']:>7} {c['hook']:>5} {c['clarity']:>7}")
print("\n" + correlation.get("content", "")[:2000])
const VM = process.env.VIMEO_ACCESS_TOKEN;
const MV = process.env.MAVERA_API_KEY;
const VM_BASE = "https://api.vimeo.com";
const MV_BASE = "https://app.mavera.io/api/v1";
const VM_H = { Authorization: `Bearer ${VM}`, Accept: "application/vnd.vimeo.*+json;version=3.4" };
const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
// 1. Pull engagement stats
const resp = await fetch(
`${VM_BASE}/me/videos?per_page=50&sort=plays&direction=desc` +
`&fields=uri,name,link,duration,stats,metadata.connections.likes.total,metadata.connections.comments.total`,
{ headers: VM_H }
).then(r => r.json());
const videos = (resp.data || []).map(v => {
const stats = v.stats || {};
const plays = stats.plays || 0;
const finishes = stats.finishes || 0;
return {
id: v.uri.split("/").pop(), name: v.name, link: v.link,
duration: v.duration || 0, plays, finishes,
avgWatched: plays > 0 ? Math.round(finishes / plays * 1000) / 10 : 0,
likes: v.metadata?.connections?.likes?.total || 0,
comments: v.metadata?.connections?.comments?.total || 0,
};
});
console.log(`Pulled engagement for ${videos.length} videos`);
// 2. Mavera analysis on top 15
const combined = [];
for (const video of videos.slice(0, 15)) {
const upload = await fetch(`${MV_BASE}/assets`, {
method: "POST", headers: MV_H,
body: JSON.stringify({ url: video.link, name: video.name.slice(0, 80), type: "video" }),
}).then(r => r.json());
const analysis = await fetch(`${MV_BASE}/video-analysis`, {
method: "POST", headers: MV_H,
body: JSON.stringify({
asset_id: upload.id,
analysis_types: ["message_clarity", "emotional_impact", "hook_score", "behavioral_effectiveness", "pacing", "cognitive_load"],
}),
}).then(r => r.json());
let status;
for (let i = 0; i < 30; i++) {
await new Promise(r => setTimeout(r, 3000));
status = await fetch(`${MV_BASE}/video-analysis/${analysis.id}`, { headers: MV_H }).then(r => r.json());
if (status.status === "completed") break;
}
const r = status.results || {};
combined.push({
...video,
clarity: r.message_clarity?.score || 0, emotion: r.emotional_impact?.score || 0,
hook: r.hook_score?.score || 0, behavior: r.behavioral_effectiveness?.score || 0,
pacingScore: r.pacing?.score || 0, cogLoad: r.cognitive_load?.average || 0,
});
await new Promise(r => setTimeout(r, 1000));
}
// 3. Correlation analysis
const dataBlock = combined.map(c =>
`Video: "${c.name.slice(0, 40)}" | Plays: ${c.plays.toLocaleString()} | Finish%: ${c.avgWatched}% | ` +
`Likes: ${c.likes} | Comments: ${c.comments} || ` +
`Clarity: ${c.clarity} | Emotion: ${c.emotion} | Hook: ${c.hook} | Behavior: ${c.behavior}`
).join("\n");
const correlation = await fetch(`${MV_BASE}/mave/chat`, {
method: "POST", headers: MV_H,
body: JSON.stringify({
message: `Correlate Mavera scores vs real Vimeo engagement.\n\nDATASET (${combined.length} videos):\n${dataBlock}\n\nProduce:\n1. Best single predictor of engagement\n2. Metric-by-metric correlation strength\n3. Surprising non-correlations\n4. Composite formula for finish rate\n5. Actionable optimization rule\n6. Outliers and explanations`,
}),
}).then(r => r.json());
console.log("VIDEO ENGAGEMENT × MAVERA SCORING CORRELATION");
console.log("=".repeat(65));
combined.forEach(c =>
console.log(` ${c.name.slice(0, 33).padEnd(35)} ${String(c.plays.toLocaleString()).padStart(8)} ` +
`${(c.avgWatched + "%").padStart(8)} ${String(c.emotion).padStart(8)} ${String(c.hook).padStart(6)} ${String(c.clarity).padStart(8)}`)
);
console.log("\n" + (correlation.content || "").slice(0, 2000));
Example Output
VIDEO ENGAGEMENT × MAVERA SCORING CORRELATION
=================================================================
Video Plays Finish% Emotion Hook Clarity
-----------------------------------------------------------------
Q1 Brand Campaign — Feel the D 84,200 72.3% 94 87 91
Customer Story — Acme Corp Tra 42,100 68.1% 82 76 88
Product Demo — Enterprise Dash 31,500 41.2% 45 62 79
How-To: Getting Started in 5 M 28,700 81.4% 52 71 93
Webinar Replay — State of the 18,300 23.8% 38 41 72
## Correlation Analysis
### Best Single Predictor
**Hook Score** is the strongest predictor of plays (r=0.82). Videos with
hook scores above 75 average 3.2x more plays than those below 60.
### Metric-by-Metric Correlation
| Mavera Metric | → Plays | → Finish% | → Likes |
|------------------|----------|-----------|---------|
| Hook Score | Strong | Moderate | Strong |
| Emotional Impact | Strong | Strong | Strong |
| Message Clarity | Moderate | Strong | Weak |
| Behavioral Eff. | Moderate | Moderate | Moderate|
| Pacing | Weak | Strong | Weak |
| Cognitive Load | Weak | Moderate | None |
### Surprising Finding
**Message Clarity** strongly predicts finish rate (r=0.78) but only weakly
predicts plays. Clear videos retain viewers but don't attract them.
You need hook score to get clicks and clarity to keep them watching.
### Composite Formula for Finish Rate
`Predicted Finish% ≈ (Clarity × 0.4) + (Emotion × 0.3) + (Pacing × 0.2) + (Hook × 0.1)`
### Actionable Rule
Optimize for **hook score** to maximize distribution (plays), and
**message clarity** to maximize retention (finish rate). These are
different creative muscles — treat the first 5 seconds and the body
as separate optimization targets.
Error Handling
Stats availability
Stats availability
Vimeo stats (
plays, finishes) require a Vimeo Pro, Business, or Premium account. Free accounts only get plays. The finishes metric may return 0 for very new videos without sufficient data.Sample size for correlation
Sample size for correlation
15 videos is a minimum viable sample for directional correlation. For statistically significant results, analyze 50+ videos. The code samples the top 15 by plays — consider random sampling for unbiased results.
Engagement graph endpoint
Engagement graph endpoint
For per-second engagement data, use
GET /videos/{id}/stats with fields=engagement_graph. This returns a frame-by-frame attention curve that can be paired with Mavera’s emotional arc for deeper correlation.What’s Next
Vimeo Integration
Back to Vimeo integration overview
Caption Content Extraction
Extract repurposable content from transcripts
Video Analysis API
Full reference for POST /api/v1/video-analysis
Mave Agent
Full reference for POST /api/v1/mave/chat