Scenario
Your top-performing ad campaigns reveal what messaging resonates with your audience, but that intelligence stays siloed in Ads Manager. This job pulls the highest-CTR campaigns, extracts winning themes and copy patterns, establishes a Brand Voice from ad copy, then generates long-form content (blog posts, social posts) that extends those proven themes across channels.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 campaign insights sorted by CTR
campaigns = requests.get(
f"{GRAPH}/{ACCT}/insights",
params={
"access_token": META,
"fields": "campaign_name,campaign_id,impressions,clicks,ctr,conversions,spend,actions",
"level": "campaign",
"date_preset": "last_30d",
"sort": "ctr_descending",
"limit": 20,
},
).json().get("data", [])
top_campaigns = [c for c in campaigns if float(c.get("ctr", 0)) > 1.0][:5]
print(f"Top {len(top_campaigns)} campaigns by CTR")
# 2. Pull ad copy from top campaigns
all_copy = []
for camp in top_campaigns:
cid = camp.get("campaign_id")
if not cid:
continue
ads = requests.get(
f"{GRAPH}/{cid}/ads",
params={
"access_token": META,
"fields": "creative{title,body,link_description}",
"limit": 10,
},
).json().get("data", [])
for ad in ads:
c = ad.get("creative", {})
copy_text = f"Headline: {c.get('title', '')}\nBody: {c.get('body', '')}\nDescription: {c.get('link_description', '')}"
if c.get("title") or c.get("body"):
all_copy.append(copy_text)
time.sleep(0.5)
# 3. Create Brand Voice from winning ad copy
samples = "\n\n---\n\n".join(all_copy[:15])
bv = requests.post(f"{MB}/brand-voices", headers=MH, json={
"name": "Meta Top Campaign Voice",
"samples": [samples],
}).json()
print(f"Brand Voice: {bv['id']}")
# 4. Extract themes
themes = [
f"Campaign: {c.get('campaign_name','')}, CTR: {c.get('ctr','?')}%, "
f"Clicks: {c.get('clicks','?')}, Spend: ${c.get('spend','?')}"
for c in top_campaigns
]
theme_summary = "\n".join(themes)
# 5. Generate blog post
blog = requests.post(f"{MB}/generations", headers=MH, json={
"brand_voice_id": bv["id"],
"prompt": f"""Based on these top-performing Meta ad campaign themes, write a 600-word blog post
that extends the most resonant messaging into long-form content.
TOP CAMPAIGNS:
{theme_summary}
WINNING COPY EXCERPTS:
{samples[:2000]}
Write a blog post that: uses the same tone and key phrases, expands on the core value proposition,
includes a clear CTA, and is optimized for SEO.""",
}).json()
# 6. Generate social posts
social = requests.post(f"{MB}/generations", headers=MH, json={
"brand_voice_id": bv["id"],
"prompt": f"""Create 5 social media posts (mix of LinkedIn, Twitter/X, Instagram caption)
inspired by these top campaign themes:
{theme_summary}
Each post should: match the ad copy voice, be platform-appropriate length,
include a hook in the first line, end with CTA.""",
}).json()
print("\n=== Blog Post ===")
print((blog.get("output") or blog.get("content", ""))[:500])
print("\n=== Social Posts ===")
print((social.get("output") or social.get("content", ""))[:500])
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. Top campaigns by CTR
const campaigns = await fetch(
`${GRAPH}/${ACCT}/insights?access_token=${META}&fields=campaign_name,campaign_id,impressions,clicks,ctr,conversions,spend,actions&level=campaign&date_preset=last_30d&sort=ctr_descending&limit=20`
).then(r => r.json()).then(d => d.data || []);
const topCampaigns = campaigns.filter(c => parseFloat(c.ctr || "0") > 1.0).slice(0, 5);
console.log(`Top ${topCampaigns.length} campaigns by CTR`);
// 2. Pull copy from top campaigns
const allCopy = [];
for (const camp of topCampaigns) {
if (!camp.campaign_id) continue;
const ads = await fetch(
`${GRAPH}/${camp.campaign_id}/ads?access_token=${META}&fields=creative{title,body,link_description}&limit=10`
).then(r => r.json()).then(d => d.data || []);
for (const ad of ads) {
const c = ad.creative || {};
if (c.title || c.body)
allCopy.push(`Headline: ${c.title || ""}\nBody: ${c.body || ""}\nDescription: ${c.link_description || ""}`);
}
await new Promise(r => setTimeout(r, 500));
}
// 3. Brand Voice
const samples = allCopy.slice(0, 15).join("\n\n---\n\n");
const bv = await fetch(`${MB}/brand-voices`, {
method: "POST", headers: MH,
body: JSON.stringify({ name: "Meta Top Campaign Voice", samples: [samples] }),
}).then(r => r.json());
console.log(`Brand Voice: ${bv.id}`);
// 4. Theme summary
const themeSummary = topCampaigns.map(c =>
`Campaign: ${c.campaign_name}, CTR: ${c.ctr}%, Clicks: ${c.clicks}, Spend: $${c.spend}`
).join("\n");
// 5. Generate blog + social in parallel
const [blog, social] = await Promise.all([
fetch(`${MB}/generations`, {
method: "POST", headers: MH,
body: JSON.stringify({
brand_voice_id: bv.id,
prompt: `Write a 600-word blog post extending these top Meta campaigns:\n\n${themeSummary}\n\nCopy excerpts:\n${samples.slice(0, 2000)}\n\nMatch tone, expand value prop, include CTA.`,
}),
}).then(r => r.json()),
fetch(`${MB}/generations`, {
method: "POST", headers: MH,
body: JSON.stringify({
brand_voice_id: bv.id,
prompt: `Create 5 social posts (LinkedIn, Twitter/X, Instagram) from these themes:\n\n${themeSummary}\n\nPlatform-appropriate, hook first line, CTA at end.`,
}),
}).then(r => r.json()),
]);
console.log("\n=== Blog Post ===");
console.log((blog.output || blog.content || "").slice(0, 500));
console.log("\n=== Social Posts ===");
console.log((social.output || social.content || "").slice(0, 500));
Example Output
Brand Voice: bv_meta_top_8k3n
=== Blog Post ===
# Your Competitors Already Know This — Here's What They're Doing Differently
The data doesn't lie. After analyzing 50,000 ad impressions last month,
one pattern stands out: teams that centralize their analytics make decisions
40% faster. Not incrementally — fundamentally faster.
But here's what most articles about analytics won't tell you...
=== Social Posts ===
1. [LinkedIn] Most teams spend 60% of their time building reports nobody reads.
We asked 200 marketing leaders what changed when they stopped. The answer
surprised us. → Link in comments
2. [Twitter/X] "Stop guessing. Start growing." isn't just a tagline.
We tracked 1,200 teams for 6 months. The ones who centralized analytics?
3.2x faster campaign iterations. Here's the breakdown: 🧵
3. [Instagram] POV: You just cut your reporting time from 4 hours to 15 minutes.
That's not a dream — it's what happens when you stop duct-taping dashboards.
Link in bio for the free guide.
Error Handling
CTR field is a string
CTR field is a string
Meta returns
ctr as a string like "2.345". Always parse with float() / parseFloat() before comparing.Campaign-level insights vs ad-level
Campaign-level insights vs ad-level
Use
level=campaign param. Without it, you get ad-account-level aggregates which won’t have campaign_id.Brand Voice needs enough samples
Brand Voice needs enough samples
Under 5 copy samples produces a generic voice. The code caps at 15 samples from top campaigns — aim for 8+ distinct pieces.
All Meta Ads Jobs
Browse all Meta Ads integration jobs
Brand Voices
Full guide to creating and managing Brand Voices