Scenario
You distribute job postings through LinkedIn using the Simple Job Posting API. After posting, you track view and apply rates. When a posting underperforms, you run the description through a Mavera Focus Group to identify weaknesses, then use Generate to create improved versions — and repost. This creates a feedback loop: post → measure → test → iterate → repost. Flow: LinkedInPOST /rest/simpleJobPostings → Track view/apply rates → Mavera POST /focus-groups (test current description) → POST /generations (improved version) → LinkedIn POST /rest/simpleJobPostings (updated)
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}",
"Content-Type": "application/json",
"LinkedIn-Version": "202401",
"X-Restli-Protocol-Version": "2.0.0",
}
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
# 1. Post a job
job_posting = {
"integrationContext": "urn:li:organization:12345",
"jobPostingOperationType": "CREATE",
"title": "Senior Backend Engineer",
"description": {
"text": (
"We're looking for a Senior Backend Engineer to join our platform team. "
"You'll design and build APIs that serve 10M+ requests/day, mentor junior engineers, "
"and drive architecture decisions. Stack: Python, Go, PostgreSQL, Kubernetes. "
"5+ years experience required. Competitive salary, equity, and fully remote."
),
},
"location": "San Francisco, CA",
"listedAt": int(time.time() * 1000),
"jobPostingStatus": "LISTED",
}
post_resp = requests.post(f"{LI_BASE}/simpleJobPostings",
headers=LI_H, json=job_posting)
post_resp.raise_for_status()
job_id = post_resp.headers.get("X-RestLi-Id", "unknown")
print(f"Posted job: {job_id}")
# 2. Wait for data to accumulate (in production, run this days later)
time.sleep(2)
# 3. Simulate performance metrics (replace with real analytics in production)
metrics = {
"views": 1240,
"applies": 31,
"apply_rate": 2.5,
"benchmark_apply_rate": 5.0,
}
# 4. If underperforming, test with Focus Group
if metrics["apply_rate"] < metrics["benchmark_apply_rate"]:
print(f"Apply rate {metrics['apply_rate']}% below benchmark {metrics['benchmark_apply_rate']}%")
candidate_personas = []
for archetype in [
{"name": "Passive Staff Engineer", "desc": "10+ yrs, employed at FAANG. Only moves for exceptional roles."},
{"name": "Active Senior IC", "desc": "5-7 yrs, actively looking. Applying to 10+ roles. Values clarity."},
{"name": "Career Transitioner", "desc": "Backend dev moving from enterprise to startup. Evaluating risk."},
]:
p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
"name": f"LI Talent: {archetype['name']}",
"description": archetype["desc"],
}).json()
candidate_personas.append(p["id"])
time.sleep(0.2)
fg = requests.post(f"{MV_BASE}/focus-groups", headers=MV_H, json={
"name": f"Job Posting Review: {job_posting['title']}",
"persona_ids": candidate_personas,
"questions": [
{"type": "likert", "text": "Rate appeal of this posting (1=skip, 5=apply now)", "scale": 5},
"What about this posting makes you hesitant to apply?",
"What information is missing that you'd need before applying?",
"How does this compare to other Senior Backend Engineer postings you've seen?",
"Rewrite the first two sentences to make them more compelling for YOU.",
],
"context": job_posting["description"]["text"],
"responses_per_persona": 3,
}).json()
for _ in range(20):
time.sleep(5)
fg_data = requests.get(f"{MV_BASE}/focus-groups/{fg['id']}", headers=MV_H).json()
if fg_data.get("status") == "completed":
break
feedback_summary = "\n".join(
f"- [{r.get('persona_id','?')[:8]}] {r.get('question','')[:40]}: {r.get('answer','')[:200]}"
for r in fg_data.get("responses", [])
)
# 5. Generate improved description
gen = requests.post(f"{MV_BASE}/generations", headers=MV_H, json={
"prompt": (
f"Rewrite this job posting based on candidate feedback.\n\n"
f"ORIGINAL:\n{job_posting['description']['text']}\n\n"
f"FEEDBACK:\n{feedback_summary}\n\n"
f"REQUIREMENTS:\n"
f"- Address every concern raised\n"
f"- Keep under 300 words\n"
f"- Lead with impact, not requirements\n"
f"- Include salary range and specific benefits\n"
f"- Make the first sentence irresistible"
),
}).json()
improved = gen.get("output", gen.get("content", gen.get("text", "")))
print(f"\n=== Improved Description ===\n{improved[:800]}")
# 6. Update the posting (in production)
# job_posting["description"]["text"] = improved
# job_posting["jobPostingOperationType"] = "UPDATE"
# requests.post(f"{LI_BASE}/simpleJobPostings", headers=LI_H, json=job_posting)
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}`, "Content-Type": "application/json",
"LinkedIn-Version": "202401", "X-Restli-Protocol-Version": "2.0.0",
};
const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
// 1. Post job
const jobPosting = {
integrationContext: "urn:li:organization:12345",
jobPostingOperationType: "CREATE",
title: "Senior Backend Engineer",
description: {
text: "We're looking for a Senior Backend Engineer to join our platform team. " +
"You'll design and build APIs serving 10M+ req/day, mentor juniors, drive architecture. " +
"Stack: Python, Go, PostgreSQL, Kubernetes. 5+ years. Competitive salary, equity, fully remote.",
},
location: "San Francisco, CA",
listedAt: Date.now(),
jobPostingStatus: "LISTED",
};
const postResp = await fetch(`${LI_BASE}/simpleJobPostings`, {
method: "POST", headers: LI_H, body: JSON.stringify(jobPosting),
});
if (!postResp.ok) throw new Error(`LinkedIn ${postResp.status}`);
const jobId = postResp.headers.get("X-RestLi-Id") || "unknown";
console.log(`Posted: ${jobId}`);
// 2. Simulated metrics (replace with real analytics)
const metrics = { views: 1240, applies: 31, applyRate: 2.5, benchmark: 5.0 };
// 3. Focus Group if underperforming
if (metrics.applyRate < metrics.benchmark) {
console.log(`Apply rate ${metrics.applyRate}% below ${metrics.benchmark}% benchmark`);
const archetypes = [
{ name: "Passive Staff Engineer", desc: "10+ yrs, FAANG. Only moves for exceptional roles." },
{ name: "Active Senior IC", desc: "5-7 yrs, actively looking. Compares 10+ roles." },
{ name: "Career Transitioner", desc: "Enterprise to startup. Evaluating risk." },
];
const personaIds = [];
for (const arch of archetypes) {
const p = await fetch(`${MV_BASE}/personas`, {
method: "POST", headers: MV_H,
body: JSON.stringify({ name: `LI: ${arch.name}`, description: arch.desc }),
}).then((r) => r.json());
personaIds.push(p.id);
await new Promise((r) => setTimeout(r, 200));
}
const fg = await fetch(`${MV_BASE}/focus-groups`, {
method: "POST", headers: MV_H,
body: JSON.stringify({
name: `Posting Review: ${jobPosting.title}`,
persona_ids: personaIds,
questions: [
{ type: "likert", text: "Rate appeal (1=skip, 5=apply now)", scale: 5 },
"What makes you hesitant to apply?",
"What information is missing?",
"How does this compare to similar postings?",
"Rewrite the first two sentences for YOU.",
],
context: jobPosting.description.text,
responses_per_persona: 3,
}),
}).then((r) => r.json());
let fgData;
for (let i = 0; i < 20; i++) {
await new Promise((r) => setTimeout(r, 5000));
fgData = await fetch(`${MV_BASE}/focus-groups/${fg.id}`, { headers: MV_H }).then((r) => r.json());
if (fgData.status === "completed") break;
}
const feedbackSummary = (fgData.responses || [])
.map((r) => `- ${(r.question || "").slice(0, 40)}: ${(r.answer || "").slice(0, 200)}`)
.join("\n");
// 4. Generate improved version
const gen = await fetch(`${MV_BASE}/generations`, {
method: "POST", headers: MV_H,
body: JSON.stringify({
prompt: `Rewrite this job posting based on feedback.\n\nORIGINAL:\n${jobPosting.description.text}\n\nFEEDBACK:\n${feedbackSummary}\n\nAddress every concern. Under 300 words. Lead with impact. Include salary range.`,
}),
}).then((r) => r.json());
console.log("\n=== Improved ===");
console.log((gen.output || gen.content || gen.text || "").slice(0, 800));
}
Example Output
Apply rate 2.5% below benchmark 5.0%
Focus Group feedback:
- [Passive Staff] Appeal: 2/5. "Generic. Every company says 'APIs at scale.' What makes YOUR APIs interesting?"
- [Active Senior] Missing info: "No salary range. I won't apply without knowing comp."
- [Transitioner] Hesitant: "'5+ years required' — I have 4 years backend + 3 years adjacent. Am I welcome?"
=== Improved Description ===
Our platform processes 10M+ API requests per day — and we need your help
making that feel like 10. As a Senior Backend Engineer, you'll own the
request lifecycle from edge to database, eliminate bottlenecks that wake
people up at 3am, and mentor a team of 4 engineers who ship weekly.
What you'll actually do:
- Redesign our payment processing pipeline (currently 800ms → target 200ms)
- Build the multi-region architecture for our APAC launch
- Lead our Python → Go migration for latency-critical services
Compensation: $185-225K base + 0.1-0.2% equity + full remote (async-first)
We're looking for someone with 4+ years of backend experience who has
opinions about database design and isn't afraid of a Kubernetes manifest.
Career changers with relevant experience welcome — we care about what you
can build, not where you built it.
Error Handling
Partner access required
Partner access required
The Simple Job Postings API returns
403 without partner approval. Apply at LinkedIn Developer Portal. For testing, mock the LinkedIn calls and focus on the Mavera feedback loop.LinkedIn-Version header
LinkedIn-Version header
All REST API calls require the
LinkedIn-Version header (format: YYYYMM). Using an outdated version returns 400. Update quarterly.Analytics delay
Analytics delay
LinkedIn job analytics aren’t real-time. Allow 24-48 hours after posting before checking performance. The code simulates metrics — replace with real analytics calls in production.