Scenario
Your brand is investing heavily in Reels but you have no framework for why some Reels outperform others. This job pulls Reels creatives from your ad account, runs Video Analysis on each for hook effectiveness and emotional scoring, then runs a Focus Group asking personas about brand recall, hook quality, and purchase intent. The output is a quantitative + qualitative Reels performance framework.Architecture
Code
import os, requests, time, tempfile
META = os.environ["META_ACCESS_TOKEN"]
ACCT = os.environ["META_AD_ACCOUNT_ID"]
MV = os.environ["MAVERA_API_KEY"]
GRAPH = "https://graph.facebook.com/v24.0"
MB = "https://app.mavera.io/api/v1"
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
# 1. Pull Instagram placement ads (Reels)
ads = requests.get(
f"{GRAPH}/{ACCT}/ads",
params={
"access_token": META,
"fields": "id,name,creative{id,name,video_id,title,body,instagram_permalink_url},targeting{publisher_platforms}",
"effective_status": '["ACTIVE","PAUSED"]',
"limit": 50,
},
).json().get("data", [])
reels = [a for a in ads if a.get("creative", {}).get("video_id")
and "instagram" in str(a.get("targeting", {}).get("publisher_platforms", [])).lower()]
print(f"Instagram Reels creatives: {len(reels)}")
# 2. Video Analysis on each
analyses = []
for reel in reels[:8]:
vid = reel["creative"]["video_id"]
video_info = requests.get(f"{GRAPH}/{vid}",
params={"access_token": META, "fields": "source,length,title"}).json()
if not video_info.get("source"):
continue
vid_resp = requests.get(video_info["source"], stream=True)
tmp = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
for chunk in vid_resp.iter_content(8192):
tmp.write(chunk)
tmp.close()
with open(tmp.name, "rb") as f:
asset = requests.post(f"{MB}/assets",
headers={"Authorization": f"Bearer {MV}"},
files={"file": (f"reel_{vid}.mp4", f, "video/mp4")}).json()
os.unlink(tmp.name)
analysis = requests.post(f"{MB}/video-analyses", headers=MH,
json={"asset_id": asset["id"], "name": f"Reel: {reel.get('name', vid)}"}).json()
for _ in range(30):
time.sleep(10)
status = requests.get(f"{MB}/video-analyses/{analysis['id']}",
headers={"Authorization": f"Bearer {MV}"}).json()
if status.get("status") in ("completed", "failed"):
break
if status.get("status") == "completed":
analyses.append({
"name": reel.get("name", vid),
"video_id": vid,
"duration": video_info.get("length"),
"body": reel["creative"].get("body", "")[:150],
"scores": status.get("scores", {}),
"analysis_id": analysis["id"],
})
time.sleep(1)
# 3. Focus Group on hook, recall, purchase intent
reel_descriptions = "\n\n".join(
f"Reel {i+1}: \"{a['name']}\" ({a['duration']}s)\n"
f"Copy: {a['body']}\n"
f"Emotional score: {a['scores'].get('emotional','?')}, "
f"Hook strength: {a['scores'].get('attention_curve',['?'])[0] if isinstance(a['scores'].get('attention_curve'), list) else '?'}"
for i, a in enumerate(analyses)
)
PERSONA_IDS = os.environ.get("PERSONA_IDS", "").split(",")
if not PERSONA_IDS[0]:
personas = requests.get(f"{MB}/personas",
headers={"Authorization": f"Bearer {MV}"}).json()
PERSONA_IDS = [p["id"] for p in (personas if isinstance(personas, list) else [])[:4]]
fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
"name": "Instagram Reels Analysis",
"persona_ids": PERSONA_IDS,
"questions": [
f"Based on these Reels descriptions, which hook would stop your scroll? Why?\n\n{reel_descriptions}",
"Rate your brand recall after seeing each Reel (1-10). Which brand/product sticks?",
"Which Reel would make you most likely to purchase or visit a website? Why?",
"What makes a Reel hook effective for you? First 3 seconds — what grabs attention?",
"Would you share any of these Reels? Which one and why?",
],
"context": "You are evaluating Instagram Reels ads for effectiveness.",
"responses_per_persona": 2,
}).json()
for _ in range(24):
time.sleep(5)
fg_data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
if fg_data.get("status") == "completed":
break
# 4. Combined report
print("=== Instagram Reels Analysis Report ===\n")
print("--- Video Analysis Scores ---")
for a in analyses:
s = a["scores"]
print(f" {a['name']}: emotional={s.get('emotional','?')} cognitive={s.get('cognitive','?')} behavioral={s.get('behavioral','?')}")
print("\n--- Focus Group Insights ---")
for resp in fg_data.get("responses", [])[:12]:
print(f" [{resp.get('persona_id','?')[:12]}] {resp.get('question','')[:50]}...")
print(f" → {resp.get('answer','')[:200]}\n")
const META = process.env.META_ACCESS_TOKEN;
const ACCT = process.env.META_AD_ACCOUNT_ID;
const MV = process.env.MAVERA_API_KEY;
const GRAPH = "https://graph.facebook.com/v24.0";
const MB = "https://app.mavera.io/api/v1";
const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
// 1. Pull Instagram placement ads
const ads = await fetch(
`${GRAPH}/${ACCT}/ads?access_token=${META}&fields=id,name,creative{id,name,video_id,title,body,instagram_permalink_url},targeting{publisher_platforms}&effective_status=["ACTIVE","PAUSED"]&limit=50`
).then(r => r.json()).then(d => d.data || []);
const reels = ads.filter(a => a.creative?.video_id
&& JSON.stringify(a.targeting?.publisher_platforms || []).toLowerCase().includes("instagram"));
console.log(`Instagram Reels: ${reels.length}`);
// 2. Video Analysis
const analyses = [];
for (const reel of reels.slice(0, 8)) {
const vid = reel.creative.video_id;
const videoInfo = await fetch(
`${GRAPH}/${vid}?access_token=${META}&fields=source,length,title`
).then(r => r.json());
if (!videoInfo.source) continue;
const vidBuffer = Buffer.from(await fetch(videoInfo.source).then(r => r.arrayBuffer()));
const form = new FormData();
form.append("file", new Blob([vidBuffer], { type: "video/mp4" }), `reel_${vid}.mp4`);
const asset = await fetch(`${MB}/assets`, {
method: "POST", headers: { Authorization: `Bearer ${MV}` }, body: form,
}).then(r => r.json());
const analysis = await fetch(`${MB}/video-analyses`, {
method: "POST", headers: MH,
body: JSON.stringify({ asset_id: asset.id, name: `Reel: ${reel.name || vid}` }),
}).then(r => r.json());
let status;
for (let i = 0; i < 30; i++) {
await new Promise(r => setTimeout(r, 10000));
status = await fetch(`${MB}/video-analyses/${analysis.id}`,
{ headers: { Authorization: `Bearer ${MV}` } }).then(r => r.json());
if (status.status === "completed" || status.status === "failed") break;
}
if (status?.status === "completed") {
analyses.push({
name: reel.name || vid, video_id: vid, duration: videoInfo.length,
body: (reel.creative.body || "").slice(0, 150),
scores: status.scores || {}, analysis_id: analysis.id,
});
}
await new Promise(r => setTimeout(r, 1000));
}
// 3. Focus Group
const reelDescs = analyses.map((a, i) =>
`Reel ${i+1}: "${a.name}" (${a.duration}s)\nCopy: ${a.body}\nEmotional: ${a.scores.emotional ?? "?"}`
).join("\n\n");
let personaIds = (process.env.PERSONA_IDS || "").split(",").filter(Boolean);
if (!personaIds.length) {
const personas = await fetch(`${MB}/personas`, { headers: MH }).then(r => r.json());
personaIds = (Array.isArray(personas) ? personas : []).slice(0, 4).map(p => p.id);
}
const fg = await fetch(`${MB}/focus-groups`, {
method: "POST", headers: MH,
body: JSON.stringify({
name: "Instagram Reels Analysis", persona_ids: personaIds,
questions: [
`Which hook would stop your scroll?\n\n${reelDescs}`,
"Rate brand recall 1-10 for each Reel.",
"Which Reel would most likely drive you to purchase?",
"What makes a Reel hook effective in the first 3 seconds?",
"Would you share any of these? Which and why?",
],
context: "Evaluating Instagram Reels ads.", responses_per_persona: 2,
}),
}).then(r => r.json());
let fgData;
for (let i = 0; i < 24; i++) {
await new Promise(r => setTimeout(r, 5000));
fgData = await fetch(`${MB}/focus-groups/${fg.id}`, { headers: MH }).then(r => r.json());
if (fgData.status === "completed") break;
}
console.log("=== Reels Analysis Report ===\n--- Scores ---");
analyses.forEach(a => {
const s = a.scores;
console.log(` ${a.name}: emotional=${s.emotional ?? "?"} cognitive=${s.cognitive ?? "?"} behavioral=${s.behavioral ?? "?"}`);
});
console.log("\n--- Focus Group ---");
(fgData.responses || []).slice(0, 12).forEach(r => {
console.log(` [${(r.persona_id || "?").slice(0, 12)}] ${(r.question || "").slice(0, 50)}...`);
console.log(` → ${(r.answer || "").slice(0, 200)}\n`);
});
Example Output
=== Instagram Reels Analysis Report ===
--- Video Analysis Scores ---
Brand Story Reel: emotional=8.7 cognitive=5.2 behavioral=6.1
Product Demo Reel: emotional=5.4 cognitive=8.3 behavioral=7.9
UGC Testimonial Reel: emotional=9.1 cognitive=6.8 behavioral=8.5
Behind-the-Scenes Reel: emotional=7.3 cognitive=4.1 behavioral=3.8
--- Focus Group Insights ---
[per_meta_f25] Which hook would stop your scroll?
→ The UGC Testimonial — it looks like a real person, not an ad. The first frame
showing someone's genuine reaction is more authentic than polished brand content.
[per_meta_m35] Which Reel would drive you to purchase?
→ Product Demo. I need to see how it actually works. The testimonial builds trust
but the demo answers "does this solve my specific problem?"
[per_meta_f18] Would you share any of these?
→ Behind-the-Scenes — I share content that makes me look "in the know." BTS gives
me insider knowledge I can share with friends who are into the space.
Error Handling
Filtering Reels vs Feed video
Filtering Reels vs Feed video
The API doesn’t have a
placement=reels filter on creatives. Use targeting.publisher_platforms or filter by aspect ratio (9:16 for Reels, 1:1 or 16:9 for Feed).Large video files
Large video files
Reels over 60s can be 100MB+. Set
stream=True when downloading and check available disk space on your server.Combining Video Analysis + Focus Group
Combining Video Analysis + Focus Group
Run Video Analysis first, then feed scores into the Focus Group context. This gives personas quantitative data to react to, producing richer qualitative feedback.
All Meta Ads Jobs
Browse all Meta Ads integration jobs
Video Analysis
Full guide to Mavera Video Analysis