Skip to main content

Scenario

Through LinkedIn’s Recruiter System Connect (RSC) or manual CSV exports from LinkedIn Recruiter, you have rich candidate profile data — titles, skills, industries, seniority levels, locations. Instead of building talent personas from intuition, you feed real candidate data into Mavera to create data-grounded personas, then test your employer value propositions against them. Flow: LinkedIn RSC export (CSV/JSON) → Parse profiles → Mavera POST /personas (per segment) → POST /focus-groups (test EVPs) → Validated employer value props

Architecture

Code

import os, json, csv, requests, time
from collections import defaultdict

MV = os.environ["MAVERA_API_KEY"]
MV_BASE = "https://app.mavera.io/api/v1"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

# 1. Load exported LinkedIn candidate data
# In production, this comes from RSC integration or Recruiter CSV export
SAMPLE_CANDIDATES = [
    {"title": "Staff Engineer", "skills": ["Python", "Kubernetes", "System Design"], "industry": "Technology", "seniority": "Senior", "location": "SF Bay Area"},
    {"title": "Engineering Manager", "skills": ["Team Leadership", "Agile", "Java"], "industry": "Technology", "seniority": "Manager", "location": "NYC"},
    {"title": "Senior Data Scientist", "skills": ["ML", "Python", "Statistics"], "industry": "Finance", "seniority": "Senior", "location": "Chicago"},
    {"title": "VP Engineering", "skills": ["Strategy", "Team Building", "Architecture"], "industry": "SaaS", "seniority": "Executive", "location": "Remote"},
    {"title": "Backend Developer", "skills": ["Node.js", "PostgreSQL", "AWS"], "industry": "E-commerce", "seniority": "Mid", "location": "Austin"},
    {"title": "ML Engineer", "skills": ["PyTorch", "MLOps", "Python"], "industry": "AI/ML", "seniority": "Senior", "location": "Seattle"},
    {"title": "Platform Engineer", "skills": ["Terraform", "Kubernetes", "Go"], "industry": "Fintech", "seniority": "Senior", "location": "London"},
]

# For CSV files from LinkedIn Recruiter export:
# with open("linkedin_export.csv") as f:
#     reader = csv.DictReader(f)
#     SAMPLE_CANDIDATES = [
#         {"title": row["Current Title"], "skills": row.get("Skills","").split(","),
#          "industry": row.get("Industry",""), "seniority": row.get("Seniority","")}
#         for row in reader
#     ]

# 2. Segment by seniority
segments = defaultdict(list)
for c in SAMPLE_CANDIDATES:
    segments[c["seniority"]].append(c)

# 3. Create personas per segment
persona_ids = []
for seniority, candidates in segments.items():
    if not candidates:
        continue
    titles = list({c["title"] for c in candidates})[:5]
    all_skills = [s for c in candidates for s in c.get("skills", [])]
    top_skills = sorted(set(all_skills), key=all_skills.count, reverse=True)[:5]
    industries = list({c["industry"] for c in candidates})[:3]
    locations = list({c.get("location", "N/A") for c in candidates})[:3]

    p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
        "name": f"LI Talent: {seniority}",
        "description": (
            f"{seniority}-level talent. N={len(candidates)}. "
            f"Titles: {', '.join(titles)}. Skills: {', '.join(top_skills)}. "
            f"Industries: {', '.join(industries)}. Locations: {', '.join(locations)}."
        ),
        "demographic": {
            "job_titles": titles,
            "industries": industries,
            "locations": locations,
        },
        "psychographic": {
            "seniority": seniority,
            "skills": top_skills,
            "career_stage": seniority.lower(),
        },
    }).json()
    persona_ids.append({"id": p["id"], "seniority": seniority, "n": len(candidates)})
    print(f"Persona: {p['id']}{seniority} ({len(candidates)} profiles)")
    time.sleep(0.3)

# 4. Test employer value propositions
EVPS = {
    "mission": "We're on a mission to make AI accessible to every business, not just tech giants.",
    "growth": "Engineers here get promoted 2x faster than industry average. We invest in your career.",
    "tech": "Our stack is modern (Go, K8s, Postgres) and you'll ship to production on day one.",
    "culture": "Async-first, no meeting Wednesdays, unlimited PTO that people actually take (avg 28 days).",
}

evp_block = "\n".join(f"- **{k.title()}**: {v}" for k, v in EVPS.items())

fg = requests.post(f"{MV_BASE}/focus-groups", headers=MV_H, json={
    "name": "Employer Value Proposition Test",
    "persona_ids": [p["id"] for p in persona_ids],
    "questions": [
        {"type": "ranking", "text": f"Rank these EVPs by how compelling they are to YOU:\n{evp_block}"},
        "Which EVP would make you respond to a recruiter's InMail?",
        "Which EVP feels like empty marketing? Why?",
        "What's missing from these value props that you'd need to hear?",
        "Write a one-line EVP that would make YOU apply.",
    ],
    "context": f"Company: Series B AI startup, 120 employees, $40M raised.\n\nValue Propositions:\n{evp_block}",
    "responses_per_persona": 3,
}).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", []):
    seniority = next((p["seniority"] for p in persona_ids if p["id"] == resp.get("persona_id")), "?")
    print(f"\n[{seniority}] {resp.get('question','')[:60]}")
    print(f"  → {resp.get('answer','')[:300]}")

Example Output

{
  "evp_rankings_by_seniority": {
    "Senior": ["tech", "culture", "growth", "mission"],
    "Manager": ["growth", "culture", "mission", "tech"],
    "Executive": ["mission", "growth", "culture", "tech"],
    "Mid": ["growth", "tech", "culture", "mission"]
  },
  "key_findings": [
    {
      "seniority": "Senior",
      "insight": "Tech stack is #1. 'Go + K8s + Postgres is the exact stack I want to work in. Ship on day one — prove it with a GitHub repo.'"
    },
    {
      "seniority": "Executive",
      "insight": "Mission is #1 but needs proof. 'Every AI company says this. Show me 3 non-tech customers using your product.'"
    },
    {
      "seniority": "Mid",
      "insight": "Growth is #1. '2x faster promotions — how? Show me the rubric and the data. Otherwise it's just a recruiting line.'"
    }
  ],
  "custom_evps": [
    { "seniority": "Senior", "evp": "We need someone to redesign our payment pipeline from 800ms to 200ms. Interested?" },
    { "seniority": "Executive", "evp": "120 engineers, $40M raised, and the exec team still writes code on Fridays. Join us." }
  ]
}

Error Handling

LinkedIn Recruiter exports come as CSV or XLSX. Parse with csv (Python) or csv-parse (Node). Column names vary by export version — map them dynamically.
Never store or send candidate PII (names, emails, profile URLs) to Mavera. Aggregate to title/skill/industry level only. Comply with LinkedIn’s Terms of Service.
LinkedIn Recruiter exports may be capped at 1,000-2,500 profiles. For larger datasets, use RSC integration for programmatic access or run multiple exports by search criteria.