Scenario
LinkedIn Campaign Manager reports performance by industry, job function, and seniority — but this data lives in spreadsheets. This job pulls analytics pivoted by these dimensions, identifies the top-performing segments (highest CTR and engagement), then uses Mave to write content briefs tailored to each winning segment. Instead of guessing what content to create, you let real campaign data tell you.Architecture
Code
import os, requests, time
LI = os.environ["LINKEDIN_ACCESS_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
LI_BASE = "https://api.linkedin.com/rest"
MV_BASE = "https://app.mavera.io/api/v1"
LI_H = {"Authorization": f"Bearer {LI}", "LinkedIn-Version": "202401", "X-Restli-Protocol-Version": "2.0.0"}
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
CAMPAIGN_URN = "urn:li:sponsoredCampaign:200000001"
# 1. Pull analytics pivoted by industry
pivots = ["MEMBER_INDUSTRY", "MEMBER_JOB_FUNCTION", "MEMBER_SENIORITY"]
all_segments = []
for pivot in pivots:
r = requests.get(f"{LI_BASE}/adAnalytics",
headers=LI_H,
params={
"q": "analytics",
"pivot": pivot,
"dateRange.start.year": 2025, "dateRange.start.month": 1, "dateRange.start.day": 1,
"dateRange.end.year": 2025, "dateRange.end.month": 12, "dateRange.end.day": 31,
"timeGranularity": "ALL",
"campaigns[0]": CAMPAIGN_URN,
"fields": "impressions,clicks,costInLocalCurrency,externalWebsiteConversions",
})
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 60)))
continue
r.raise_for_status()
for el in r.json().get("elements", []):
impressions = el.get("impressions", 0)
clicks = el.get("clicks", 0)
if impressions < 100:
continue
ctr = clicks / impressions if impressions else 0
conversions = el.get("externalWebsiteConversions", 0)
all_segments.append({
"pivot": pivot,
"value": el.get("pivotValue", "Unknown"),
"impressions": impressions,
"clicks": clicks,
"ctr": round(ctr * 100, 2),
"conversions": conversions,
"spend": el.get("costInLocalCurrency", 0),
})
time.sleep(0.5)
# 2. Rank by CTR, take top segments
top = sorted(all_segments, key=lambda x: -x["ctr"])[:6]
# 3. Generate content briefs via Mave
for seg in top:
brief = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
"message": f"""Write a content brief for this high-performing LinkedIn audience segment.
SEGMENT: {seg['value']} ({seg['pivot'].replace('MEMBER_', '').replace('_', ' ').title()})
PERFORMANCE: {seg['impressions']:,} impressions | {seg['clicks']:,} clicks | CTR: {seg['ctr']}% | {seg['conversions']} conversions | ${seg['spend']:,.2f} spend
Produce:
1. Why this segment responds (hypotheses based on role/industry/seniority)
2. Content brief: topic, angle, format (whitepaper / blog / video / carousel)
3. Headline options (3)
4. Key messages and proof points to include
5. Distribution strategy (organic LinkedIn + paid amplification)
6. Recommended content length and CTA"""
}).json()
print(f"\n{'='*50}")
print(f"SEGMENT: {seg['value']} | CTR: {seg['ctr']}% | Clicks: {seg['clicks']:,}")
print(f"{'='*50}")
print(brief.get("content", "")[:600])
const LI = process.env.LINKEDIN_ACCESS_TOKEN;
const MV = process.env.MAVERA_API_KEY;
const LI_BASE = "https://api.linkedin.com/rest";
const MV_BASE = "https://app.mavera.io/api/v1";
const LI_H = { Authorization: `Bearer ${LI}`, "LinkedIn-Version": "202401", "X-Restli-Protocol-Version": "2.0.0" };
const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
const CAMPAIGN_URN = "urn:li:sponsoredCampaign:200000001";
// 1. Pull analytics pivoted by industry/function/seniority
const pivots = ["MEMBER_INDUSTRY", "MEMBER_JOB_FUNCTION", "MEMBER_SENIORITY"];
const allSegments = [];
for (const pivot of pivots) {
const params = new URLSearchParams({
q: "analytics", pivot,
"dateRange.start.year": "2025", "dateRange.start.month": "1", "dateRange.start.day": "1",
"dateRange.end.year": "2025", "dateRange.end.month": "12", "dateRange.end.day": "31",
timeGranularity: "ALL",
"campaigns[0]": CAMPAIGN_URN,
fields: "impressions,clicks,costInLocalCurrency,externalWebsiteConversions",
});
const res = await fetch(`${LI_BASE}/adAnalytics?${params}`, { headers: LI_H });
if (res.status === 429) {
await new Promise(r => setTimeout(r, parseInt(res.headers.get("Retry-After") || "60", 10) * 1000));
continue;
}
if (!res.ok) throw new Error(`LinkedIn ${res.status}`);
for (const el of (await res.json()).elements || []) {
const impressions = el.impressions || 0;
const clicks = el.clicks || 0;
if (impressions < 100) continue;
allSegments.push({
pivot, value: el.pivotValue || "Unknown",
impressions, clicks,
ctr: parseFloat(((clicks / impressions) * 100).toFixed(2)),
conversions: el.externalWebsiteConversions || 0,
spend: el.costInLocalCurrency || 0,
});
}
await new Promise(r => setTimeout(r, 500));
}
// 2. Rank by CTR
const top = allSegments.sort((a, b) => b.ctr - a.ctr).slice(0, 6);
// 3. Content briefs via Mave
for (const seg of top) {
const brief = await fetch(`${MV_BASE}/mave/chat`, {
method: "POST", headers: MV_H,
body: JSON.stringify({
message: `Write a content brief for this LinkedIn segment.\n\nSEGMENT: ${seg.value} (${seg.pivot.replace("MEMBER_", "").replace(/_/g, " ")})\nPERFORMANCE: ${seg.impressions.toLocaleString()} impressions | ${seg.clicks.toLocaleString()} clicks | CTR: ${seg.ctr}% | ${seg.conversions} conversions\n\nProduce: 1) Why this segment responds 2) Content brief 3) 3 headlines 4) Key messages 5) Distribution strategy 6) CTA`,
}),
}).then(r => r.json());
console.log(`\n${"=".repeat(50)}`);
console.log(`SEGMENT: ${seg.value} | CTR: ${seg.ctr}% | Clicks: ${seg.clicks.toLocaleString()}`);
console.log("=".repeat(50));
console.log((brief.content || "").slice(0, 600));
}
Example Output
==================================================
SEGMENT: Information Technology | CTR: 3.42% | Clicks: 1,847
==================================================
## Content Brief: IT Leaders & Digital Transformation
### Why This Segment Responds
IT leaders are actively evaluating tools that reduce manual processes.
Your ad's "60% faster" claim maps to their OKRs around operational efficiency.
### Brief
- Topic: "The Hidden Cost of Manual Reporting in IT Organizations"
- Format: Gated whitepaper (LinkedIn lead gen form)
- Angle: Quantified pain — tie manual hours to opportunity cost
### Headlines
1. "Your IT Team Spends 12 Hours/Week on Reports Nobody Reads"
2. "How [Company] Cut Reporting Time by 60% — Without New Headcount"
3. "The CFO Wants Faster Insights. Here's How IT Delivers."
### Distribution
- Organic: 3-part LinkedIn carousel summarizing key findings
- Paid: Sponsored Content targeting IT directors + VPs, 201-1000 employees
Error Handling
Analytics data delays
Analytics data delays
LinkedIn analytics data can lag 24–48 hours. Don’t expect same-day metrics. Schedule this job to run on T+2 data.
Pivot value mapping
Pivot value mapping
Pivot values like
MEMBER_INDUSTRY return LinkedIn’s internal taxonomy codes (e.g., urn:li:industry:4). Map them via GET /industries for human-readable names.Low-volume segments
Low-volume segments
Segments with under 100 impressions produce unreliable CTR. The code filters these out.