import os, requests, time
TT = os.environ["TIKTOK_ACCESS_TOKEN"]
ADV = os.environ["TIKTOK_ADVERTISER_ID"]
MV = os.environ["MAVERA_API_KEY"]
TT_BASE = "https://business-api.tiktok.com/open_api/v1.3"
MV_BASE = "https://app.mavera.io/api/v1"
TT_H = {"Access-Token": TT}
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
# 1. Pull audience demographics
demo = requests.post(f"{TT_BASE}/reports/integrated/get/",
headers=TT_H,
json={
"advertiser_id": ADV,
"report_type": "AUDIENCE",
"data_level": "AUCTION_ADVERTISER",
"dimensions": ["age", "gender"],
"metrics": ["impressions", "clicks", "spend", "conversion"],
"start_date": "2025-01-01", "end_date": "2025-12-31",
"page_size": 50,
}).json()
if demo.get("code") != 0:
raise SystemExit(f"TikTok: {demo.get('message')}")
age_data = {}
for row in demo.get("data", {}).get("list", []):
age = row.get("dimensions", {}).get("age", "Unknown")
m = row.get("metrics", {})
existing = age_data.get(age, {"impressions": 0, "clicks": 0, "spend": 0})
age_data[age] = {
"impressions": existing["impressions"] + int(m.get("impressions", 0)),
"clicks": existing["clicks"] + int(m.get("clicks", 0)),
"spend": existing["spend"] + float(m.get("spend", 0)),
}
# 2. Map age brackets to generational personas
GEN_MAP = {
"AGE_13_17": {"gen": "Gen Alpha/Z", "desc": "13-17. Digital native. Discovers brands through TikTok, values authenticity and humor. Skeptical of polished ads. Influenced by creators, not brands."},
"AGE_18_24": {"gen": "Gen Z", "desc": "18-24. Core TikTok demographic. Values transparency, social impact, and UGC. Short attention span. Responds to trends, not traditional marketing."},
"AGE_25_34": {"gen": "Millennial", "desc": "25-34. Established spending power. Values convenience and brand story. Uses TikTok for discovery. Responds to aspirational + practical content."},
"AGE_35_44": {"gen": "Elder Millennial", "desc": "35-44. Decision-makers and parents. Uses TikTok as entertainment. Responds to relatable, informational content. Less trend-driven."},
"AGE_45_54": {"gen": "Gen X", "desc": "45-54. Fastest-growing TikTok segment. Values expertise and straightforward messaging. Skeptical but engaged when content is relevant."},
}
persona_ids = []
for age_bracket, gen_info in GEN_MAP.items():
stats = age_data.get(age_bracket, {})
if stats.get("impressions", 0) < 100:
continue
ctr = (stats["clicks"] / stats["impressions"] * 100) if stats["impressions"] else 0
p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
"name": f"TikTok: {gen_info['gen']}",
"description": f"{gen_info['desc']} Campaign data: {stats['impressions']:,} impressions, {ctr:.2f}% CTR, ${stats['spend']:,.0f} spend.",
"demographic": {"age_range": age_bracket, "platform": "TikTok"},
"psychographic": {"content_preferences": "short-form video", "generation": gen_info["gen"]},
}).json()
persona_ids.append({"id": p["id"], "gen": gen_info["gen"], "ctr": ctr})
time.sleep(0.3)
# 3. Run Focus Group with ad concepts
AD_CONCEPTS = """
CONCEPT A — "Day in My Life" UGC:
Creator shows morning routine using your product. Lo-fi aesthetic, trending audio, text overlays.
CONCEPT B — "3 Things You Didn't Know":
Founder on camera, fast cuts, educational hook. "Stop scrolling if you..." opener.
CONCEPT C — "Transition Reveal":
Before/after transformation. Product reveal at the cut. Trending transition effect.
CONCEPT D — "Stitch Response":
Stitches a popular creator's question. Authentic, conversational, no script.
"""
fg = requests.post(f"{MV_BASE}/focus-groups", headers=MV_H, json={
"name": "TikTok Ad Concept — Generational Testing",
"persona_ids": [p["id"] for p in persona_ids],
"questions": [
f"Review these TikTok ad concepts:\n\n{AD_CONCEPTS}\n\nWhich concept would stop your scroll? Why?",
"Which concept feels most authentic to TikTok? Which feels like an ad?",
"Would you share any of these with friends? Which one and why?",
"What trending format or audio would make you more likely to engage?",
],
"responses_per_persona": 2,
}).json()
for _ in range(20):
time.sleep(5)
data = requests.get(f"{MV_BASE}/focus-groups/{fg['id']}", headers=MV_H).json()
if data.get("status") == "completed": break
for resp in data.get("responses", []):
gen = next((p["gen"] for p in persona_ids if p["id"] == resp.get("persona_id")), "?")
print(f"[{gen}] {resp.get('question','')[:60]}...")
print(f" → {resp.get('answer','')[:250]}\n")