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

# Cross-Platform Creative Optimization

> Analyze the same video creative across Facebook Feed, Instagram Stories, and Audience Network placements, then get placement-specific optimization recommendations.

### Scenario

The same video creative performs differently on Facebook Feed, Instagram Stories, and Audience Network. You're making budget allocation decisions in the dark. This job takes one creative, runs separate Video Analyses for each placement context, then uses Mave to compare placement-specific scores and recommend edits — aspect ratio crops, hook timing adjustments, CTA overlay placement — for each surface.

### Architecture

```mermaid theme={"dark"}
flowchart LR
    A["Meta GET insights (platform, position)"] --> B[Same creative across placements] --> C[Video Analysis per placement] --> D["POST /api/v1/mave/chat"] --> E[Placement-specific optimization]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  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 performance by placement for recent ads
  placement_data = requests.get(
      f"{GRAPH}/{ACCT}/insights",
      params={
          "access_token": META,
          "fields": "ad_id,ad_name,impressions,clicks,ctr,cpc,spend,actions",
          "breakdowns": "publisher_platform,platform_position",
          "level": "ad",
          "date_preset": "last_30d",
          "limit": 200,
      },
  ).json().get("data", [])

  # 2. Group by ad, find ads with multi-placement data
  from collections import defaultdict
  ad_placements = defaultdict(list)
  for row in placement_data:
      ad_placements[row["ad_id"]].append({
          "platform": row.get("publisher_platform", "unknown"),
          "position": row.get("platform_position", "unknown"),
          "impressions": int(row.get("impressions", 0)),
          "clicks": int(row.get("clicks", 0)),
          "ctr": float(row.get("ctr", 0)),
          "spend": float(row.get("spend", 0)),
          "name": row.get("ad_name", ""),
      })

  multi_placement = {k: v for k, v in ad_placements.items() if len(v) >= 2}
  print(f"Ads with 2+ placements: {len(multi_placement)}")

  # 3. Pick the top ad by total spend and analyze
  if not multi_placement:
      print("No multi-placement ads found.")
      exit()

  target_ad_id = max(multi_placement, key=lambda k: sum(p["spend"] for p in multi_placement[k]))
  placements = multi_placement[target_ad_id]
  ad_name = placements[0]["name"]
  print(f"Analyzing: {ad_name} across {len(placements)} placements")

  # 4. Get the creative video
  ad_detail = requests.get(
      f"{GRAPH}/{target_ad_id}",
      params={"access_token": META, "fields": "creative{video_id,title,body}"},
  ).json()
  vid = ad_detail.get("creative", {}).get("video_id")
  if not vid:
      print("Selected ad has no video creative.")
      exit()

  video_info = requests.get(f"{GRAPH}/{vid}",
      params={"access_token": META, "fields": "source,length"}).json()

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

  # 5. Run Video Analysis for each placement context
  placement_analyses = []
  for pl in placements:
      label = f"{pl['platform']} — {pl['position']}"
      with open(tmp.name, "rb") as f:
          asset = requests.post(f"{MB}/assets",
              headers={"Authorization": f"Bearer {MV}"},
              files={"file": (f"{vid}_{pl['platform']}_{pl['position']}.mp4", f, "video/mp4")}).json()

      analysis = requests.post(f"{MB}/video-analyses", headers=MH, json={
          "asset_id": asset["id"],
          "name": f"{ad_name} — {label}",
      }).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":
          placement_analyses.append({
              "placement": label,
              "platform": pl["platform"],
              "position": pl["position"],
              "ctr": pl["ctr"],
              "spend": pl["spend"],
              "scores": status.get("scores", {}),
          })
      time.sleep(1)

  os.unlink(tmp.name)

  # 6. Mave comparison
  analysis_summary = "\n".join(
      f"- {pa['placement']}: CTR={pa['ctr']:.2f}%, spend=${pa['spend']:.0f}, "
      f"emotional={pa['scores'].get('emotional','?')}, cognitive={pa['scores'].get('cognitive','?')}, "
      f"behavioral={pa['scores'].get('behavioral','?')}"
      for pa in placement_analyses
  )

  comparison = requests.post(f"{MB}/mave/chat", headers=MH, json={
      "message": f"""Compare this video creative's performance across Meta placements.

  CREATIVE: "{ad_name}" ({video_info.get('length',0)}s video)

  PLACEMENT ANALYSIS:
  {analysis_summary}

  For each placement:
  1. Why does performance differ? (viewing context, user intent, format fit)
  2. Specific edits for this placement (aspect ratio, hook timing, CTA overlay position)
  3. Budget reallocation recommendation based on efficiency
  4. Which placement should get the most budget and why
  5. Should any placement get a dedicated creative variant?"""
  }).json()

  print("\n=== Cross-Platform Creative Analysis ===")
  print(comparison.get("content", ""))
  ```

  ```javascript JavaScript theme={"dark"}
  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. Placement breakdown
  const placementData = await fetch(
    `${GRAPH}/${ACCT}/insights?access_token=${META}&fields=ad_id,ad_name,impressions,clicks,ctr,cpc,spend,actions&breakdowns=publisher_platform,platform_position&level=ad&date_preset=last_30d&limit=200`
  ).then(r => r.json()).then(d => d.data || []);

  // 2. Group by ad
  const adPlacements = {};
  for (const row of placementData) {
    (adPlacements[row.ad_id] ??= []).push({
      platform: row.publisher_platform || "unknown",
      position: row.platform_position || "unknown",
      impressions: parseInt(row.impressions || "0"),
      clicks: parseInt(row.clicks || "0"),
      ctr: parseFloat(row.ctr || "0"),
      spend: parseFloat(row.spend || "0"),
      name: row.ad_name || "",
    });
  }

  const multiPlacement = Object.entries(adPlacements).filter(([, v]) => v.length >= 2);
  console.log(`Ads with 2+ placements: ${multiPlacement.length}`);
  if (!multiPlacement.length) { console.log("No multi-placement ads."); process.exit(); }

  // 3. Pick top ad
  const [targetAdId, placements] = multiPlacement.sort(
    ([, a], [, b]) => b.reduce((s, p) => s + p.spend, 0) - a.reduce((s, p) => s + p.spend, 0)
  )[0];
  const adName = placements[0].name;
  console.log(`Analyzing: ${adName} across ${placements.length} placements`);

  // 4. Get video
  const adDetail = await fetch(
    `${GRAPH}/${targetAdId}?access_token=${META}&fields=creative{video_id,title,body}`
  ).then(r => r.json());
  const vid = adDetail.creative?.video_id;
  if (!vid) { console.log("No video creative."); process.exit(); }

  const videoInfo = await fetch(
    `${GRAPH}/${vid}?access_token=${META}&fields=source,length`
  ).then(r => r.json());
  const vidBuffer = Buffer.from(await fetch(videoInfo.source).then(r => r.arrayBuffer()));

  // 5. Analyze per placement
  const placementAnalyses = [];
  for (const pl of placements) {
    const label = `${pl.platform} — ${pl.position}`;
    const form = new FormData();
    form.append("file", new Blob([vidBuffer], { type: "video/mp4" }), `${vid}_${pl.platform}_${pl.position}.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: `${adName} — ${label}` }),
    }).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") {
      placementAnalyses.push({
        placement: label, platform: pl.platform, position: pl.position,
        ctr: pl.ctr, spend: pl.spend, scores: status.scores || {},
      });
    }
    await new Promise(r => setTimeout(r, 1000));
  }

  // 6. Mave comparison
  const summary = placementAnalyses.map(pa =>
    `- ${pa.placement}: CTR=${pa.ctr.toFixed(2)}%, spend=$${pa.spend.toFixed(0)}, emotional=${pa.scores.emotional ?? "?"}, cognitive=${pa.scores.cognitive ?? "?"}, behavioral=${pa.scores.behavioral ?? "?"}`
  ).join("\n");

  const comparison = await fetch(`${MB}/mave/chat`, {
    method: "POST", headers: MH,
    body: JSON.stringify({
      message: `Compare "${adName}" across placements:\n\n${summary}\n\nFor each: 1) Why different 2) Specific edits 3) Budget reallocation 4) Best placement 5) Need dedicated variant?`,
    }),
  }).then(r => r.json());

  console.log("\n=== Cross-Platform Analysis ===");
  console.log(comparison.content || "");
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
=== Cross-Platform Creative Analysis ===

## Placement Performance

| Placement | CTR | Emotional | Behavioral | Verdict |
|-----------|-----|-----------|------------|---------|
| Facebook Feed | 2.1% | 7.5 | 7.2 | Strong — scale |
| Instagram Stories | 3.4% | 8.8 | 8.1 | Best — double budget |
| Audience Network | 0.8% | 4.2 | 3.1 | Weak — pause or rework |

## Why Performance Differs
- **Stories** wins because the full-screen vertical format maximizes emotional impact. 
  The hook at 0:03 fills the viewport — no competing content.
- **Feed** performs well but the 16:9 crop loses the bottom CTA overlay. Horizontal 
  crops are shown smaller in-feed.
- **Audience Network** has low-intent placements (interstitials, banner slots). 
  The creative wasn't designed for these contexts.

## Recommendations
1. **Stories:** Create a 9:16 native cut. Move CTA to 0:08 (swipe-up zone). 
   Add text overlay for sound-off viewing. Allocate 50% of budget here.
2. **Feed:** Add 1:1 square crop variant. Pin CTA as persistent text overlay 
   (many Feed users scroll without tapping). Allocate 40%.
3. **Audience Network:** Pause unless CPA is acceptable. If keeping, create 
   a static 300x250 companion with the strongest frame as hero image. 10% max.
```

### Error Handling

<AccordionGroup>
  <Accordion title="Same video, different analysis">Uploading the same video file multiple times to Mavera creates separate assets. This is intentional — each analysis can capture placement-specific context in the name and metadata.</Accordion>
  <Accordion title="Placement breakdown returns many rows">A single ad across 6 placements × 30 days = 180 rows. Use `date_preset=last_7d` for faster iteration during testing.</Accordion>
  <Accordion title="Audience Network data is sparse">Low-impression Audience Network placements produce unreliable CTR. Filter placements with fewer than 1,000 impressions.</Accordion>
</AccordionGroup>

<CardGroup cols={2}>
  <Card title="All Meta Ads Jobs" icon="meta" href="/integrations/meta-ads">
    Browse all Meta Ads integration jobs
  </Card>

  <Card title="Video Analysis" icon="video" href="/features/video-analysis">
    Full guide to Mavera Video Analysis
  </Card>
</CardGroup>
