Skip to main content

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.

Scenario

You’ve written 4 headline variants for a new campaign and need to know which one drives clicks before spending budget on live A/B tests. This job pulls existing ad copy variants (or you provide new ones), creates a Focus Group with your target personas, and asks them to rate each headline 1–10 and answer “Would you click?” The result is a pre-launch signal on which copy to promote.

Architecture

Code

import os, requests, time

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 ad copy variants from the same campaign
CAMPAIGN_ID = os.environ.get("META_CAMPAIGN_ID", "")
if CAMPAIGN_ID:
    ads = requests.get(
        f"{GRAPH}/{CAMPAIGN_ID}/ads",
        params={
            "access_token": META,
            "fields": "id,name,creative{title,body,link_description,call_to_action_type}",
            "limit": 20,
        },
    ).json().get("data", [])
else:
    ads = requests.get(
        f"{GRAPH}/{ACCT}/ads",
        params={
            "access_token": META,
            "fields": "id,name,creative{title,body,link_description,call_to_action_type}",
            "effective_status": '["ACTIVE","PAUSED"]',
            "limit": 20,
        },
    ).json().get("data", [])

variants = []
for ad in ads:
    c = ad.get("creative", {})
    if c.get("title") or c.get("body"):
        variants.append({
            "ad_id": ad["id"],
            "ad_name": ad.get("name", "Untitled"),
            "headline": c.get("title", ""),
            "body": c.get("body", ""),
            "cta": c.get("call_to_action_type", "LEARN_MORE"),
        })

variants = variants[:6]
print(f"Testing {len(variants)} ad copy variants")

# 2. Build Focus Group questions
variant_block = "\n\n".join(
    f"**Variant {i+1}: \"{v['headline']}\"**\n{v['body'][:200]}\nCTA: {v['cta']}"
    for i, v in enumerate(variants)
)

PERSONA_IDS = os.environ.get("PERSONA_IDS", "").split(",")
if not PERSONA_IDS[0]:
    personas = requests.get(f"{MB}/personas", headers=MH).json()
    PERSONA_IDS = [p["id"] for p in (personas if isinstance(personas, list) else [])[:4]]

questions = []
for i, v in enumerate(variants):
    questions.append(f"Rate this headline 1-10 for how much it makes you want to learn more: \"{v['headline']}\"")
    questions.append(f"Would you click on this ad? (Yes/No): \"{v['headline']}\"{v['body'][:100]}")

questions.extend([
    "Which variant is the most compelling overall? Give the variant number and explain why.",
    "What would make you scroll past all of these? What's missing?",
])

# 3. Run Focus Group
fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
    "name": "Meta Ad Copy A/B Test",
    "persona_ids": PERSONA_IDS,
    "questions": questions,
    "context": f"You are evaluating ad copy variants for a social media campaign. Here are the variants:\n\n{variant_block}",
    "responses_per_persona": 2,
}).json()

# 4. Poll for results
for _ in range(24):
    time.sleep(5)
    data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
    if data.get("status") == "completed":
        break

# 5. Tabulate scores
scores = {i: {"ratings": [], "clicks": []} for i in range(len(variants))}
for resp in data.get("responses", []):
    q = resp.get("question", "")
    a = resp.get("answer", "")
    for i in range(len(variants)):
        if variants[i]["headline"] in q:
            if "Rate" in q:
                try:
                    score = int("".join(c for c in a if c.isdigit())[:2])
                    if 1 <= score <= 10:
                        scores[i]["ratings"].append(score)
                except ValueError:
                    pass
            elif "click" in q.lower():
                scores[i]["clicks"].append(1 if a.lower().startswith("yes") else 0)

print("\n=== Ad Copy A/B Results ===")
for i, v in enumerate(variants):
    s = scores[i]
    avg_rating = sum(s["ratings"]) / max(len(s["ratings"]), 1)
    click_rate = sum(s["clicks"]) / max(len(s["clicks"]), 1)
    print(f"Variant {i+1}: \"{v['headline']}\"")
    print(f"  Rating: {avg_rating:.1f}/10 | Click intent: {click_rate:.0%}")

Example Output

=== Ad Copy A/B Results ===
Variant 1: "Stop Guessing. Start Growing."
  Rating: 7.8/10 | Click intent: 75%
Variant 2: "Your Competitors Already Know This"
  Rating: 8.4/10 | Click intent: 88%
Variant 3: "Save 10 Hours a Week on Reporting"
  Rating: 6.9/10 | Click intent: 63%
Variant 4: "Built for Teams That Ship Fast"
  Rating: 7.2/10 | Click intent: 50%

Winner: Variant 2 — curiosity gap + competitive framing
  "Makes me wonder what I'm missing. I'd click to find out." — Meta Female 25-34
  "The competitive angle creates urgency." — Meta Male 35-44

Error Handling

Some ad formats (Dynamic Creative, Catalog) don’t have a top-level title. Check object_story_spec.link_data.message for the primary text instead.
6 variants × 2 questions × 4 personas = 48 responses. Allow 2+ minutes for polling. Reduce responses_per_persona if speed matters more than depth.
Personas respond naturally (“I’d give it an 8”). The regex extraction handles most formats but may miss edge cases like “eight out of ten”.

Meta Ads Integration

All Meta Ads jobs

Focus Groups

Full reference for synthetic focus groups