import os, requests, time
from collections import defaultdict
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 audience insights with demographic breakdowns
insights = requests.get(
f"{GRAPH}/{ACCT}/insights",
params={
"access_token": META,
"fields": "impressions,clicks,spend,actions",
"breakdowns": "age,gender",
"date_preset": "last_30d",
"limit": 100,
},
).json().get("data", [])
# 2. Score segments by conversion efficiency
segments = []
for row in insights:
conversions = 0
for action in row.get("actions", []):
if action.get("action_type") in ("offsite_conversion", "lead", "purchase"):
conversions += int(action.get("value", 0))
segments.append({
"age": row.get("age", "unknown"),
"gender": row.get("gender", "unknown"),
"impressions": int(row.get("impressions", 0)),
"clicks": int(row.get("clicks", 0)),
"spend": float(row.get("spend", 0)),
"conversions": conversions,
"ctr": int(row.get("clicks", 0)) / max(int(row.get("impressions", 1)), 1),
"cpa": float(row.get("spend", 0)) / max(conversions, 1),
})
top_segments = sorted(segments, key=lambda s: s["conversions"], reverse=True)[:10]
# 3. Pull location breakdown separately
geo_insights = requests.get(
f"{GRAPH}/{ACCT}/insights",
params={
"access_token": META,
"fields": "impressions,clicks,spend",
"breakdowns": "country",
"date_preset": "last_30d",
"limit": 20,
},
).json().get("data", [])
top_countries = sorted(geo_insights, key=lambda g: int(g.get("clicks", 0)), reverse=True)[:5]
country_list = [g.get("country", "Unknown") for g in top_countries]
# 4. Fetch existing Mavera personas
existing = requests.get(f"{MB}/personas", headers=MH).json()
existing_names = {p.get("name", "").lower(): p for p in (existing if isinstance(existing, list) else [])}
# 5. Map or create personas
created, mapped = [], []
for seg in top_segments:
name = f"Meta {seg['gender'].title()} {seg['age']}"
search_key = name.lower()
if search_key in existing_names:
mapped.append({"name": name, "id": existing_names[search_key]["id"], "action": "mapped"})
continue
desc = (
f"Meta Ads audience segment: {seg['gender']}, age {seg['age']}. "
f"30-day stats: {seg['impressions']:,} impressions, {seg['clicks']:,} clicks, "
f"CTR {seg['ctr']:.2%}, {seg['conversions']} conversions, CPA ${seg['cpa']:.2f}. "
f"Top markets: {', '.join(country_list[:3])}."
)
r = requests.post(f"{MB}/personas", headers=MH, json={
"name": name,
"description": desc,
"demographic": {
"age_range": seg["age"],
"gender": seg["gender"],
"countries": country_list[:3],
},
"psychographic": {
"conversion_propensity": "high" if seg["conversions"] > 10 else "medium",
"engagement_level": "high" if seg["ctr"] > 0.02 else "moderate",
},
})
r.raise_for_status()
created.append({"name": name, "id": r.json()["id"], "action": "created"})
time.sleep(0.3)
print(f"Mapped: {len(mapped)} existing | Created: {len(created)} new")
for p in mapped + created:
print(f" [{p['action'].upper()}] {p['name']} → {p['id']}")