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

Your high-engagement posts generate 30–100 comments each — raw prospect language sitting in plain sight. This job identifies your top-engagement posts, pulls their comment threads, then feeds the full comment corpus into Mavera Chat asking: “What questions are prospects asking? What objections surface? What language do they use to describe their problems?” The output is a messaging map built from real audience language — not internal jargon.

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"}

ORG_URN = "urn:li:organization:12345678"

# 1. Pull recent posts and find high-engagement ones
r = requests.get(f"{LI_BASE}/posts",
    headers=LI_H,
    params={"q": "author", "author": ORG_URN, "count": 50, "sortBy": "LAST_MODIFIED"})
if r.status_code == 429:
    time.sleep(int(r.headers.get("Retry-After", 60)))
    r = requests.get(f"{LI_BASE}/posts", headers=LI_H,
        params={"q": "author", "author": ORG_URN, "count": 50, "sortBy": "LAST_MODIFIED"})
r.raise_for_status()
posts = r.json().get("elements", [])

# 2. Get comment counts to find top posts
post_comments = []
for post in posts:
    post_urn = post.get("id", "")
    commentary = post.get("commentary", "")
    if not commentary:
        continue

    sr = requests.get(f"{LI_BASE}/socialActions/{post_urn}", headers=LI_H)
    if sr.status_code == 429:
        time.sleep(int(sr.headers.get("Retry-After", 30)))
        sr = requests.get(f"{LI_BASE}/socialActions/{post_urn}", headers=LI_H)

    comment_count = 0
    if sr.ok:
        comment_count = sr.json().get("commentsSummary", {}).get("totalFirstLevelComments", 0)

    post_comments.append({
        "urn": post_urn,
        "commentary": commentary[:300],
        "comment_count": comment_count,
    })
    time.sleep(0.3)

# 3. Pull actual comments from top posts (by comment count)
top_posts = sorted(post_comments, key=lambda x: -x["comment_count"])[:8]
all_comments = []

for post in top_posts:
    if post["comment_count"] == 0:
        continue

    cr = requests.get(f"{LI_BASE}/socialActions/{post['urn']}/comments",
        headers=LI_H,
        params={"count": 100})
    if cr.status_code == 429:
        time.sleep(int(cr.headers.get("Retry-After", 30)))
        cr = requests.get(f"{LI_BASE}/socialActions/{post['urn']}/comments",
            headers=LI_H, params={"count": 100})

    if cr.ok:
        comments = cr.json().get("elements", [])
        for c in comments:
            text = c.get("message", {}).get("text", "").strip()
            if text and len(text) > 15:
                all_comments.append({
                    "post_context": post["commentary"][:150],
                    "comment": text[:400],
                })
    time.sleep(0.5)

if not all_comments:
    raise SystemExit("No comments found. Check page permissions and post engagement.")

# 4. Build comment corpus for Mave
corpus = "\n\n---\n\n".join(
    f"[Post context: {c['post_context'][:100]}...]\nComment: {c['comment']}"
    for c in all_comments[:60]
)

# 5. Messaging analysis via Mave
analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
    "message": f"""Analyze these {len(all_comments)} LinkedIn comments from our Company Page posts.
These are real prospects and followers reacting to our content.

{corpus}

Produce a messaging intelligence report:

1. QUESTIONS PROSPECTS ASK: What are the top 5-7 recurring questions? Group by theme (pricing, implementation, comparison, capability). Include exact phrasing from comments.

2. OBJECTIONS THAT SURFACE: What resistance or skepticism appears? List the top 5 objections with representative quotes and suggested counter-messaging.

3. LANGUAGE MAP: What words and phrases do commenters use to describe their problems? List the top 10 prospect-native phrases (not our internal jargon). These should be used verbatim in ad copy and landing pages.

4. SENTIMENT CLUSTERS: Group comments by sentiment (enthusiastic, curious, skeptical, critical). What percentage falls in each?

5. CONTENT GAPS: What topics do commenters ask about that we haven't addressed? List 5 content pieces we should create based on comment demand.

6. COMPETITIVE MENTIONS: Any competitors or alternatives mentioned? Context for each."""
}).json()

print(f"Analyzed {len(all_comments)} comments from {len(top_posts)} high-engagement posts")
print("=" * 60)
print(analysis.get("content", "")[:2500])

Example Output

Analyzed 187 comments from 8 high-engagement posts
============================================================

## 1. Questions Prospects Ask

**Pricing & ROI (34 mentions)**
- "What does this actually cost for a team of 20?"
- "Is there a free tier to test before committing?"
- "How long before we see measurable ROI?"

**Implementation (28 mentions)**
- "How long does onboarding take?"
- "Does this integrate with Salesforce out of the box?"
- "Do we need a dedicated admin?"

**Comparison (19 mentions)**
- "How is this different from [Competitor X]?"
- "We tried [Competitor Y] and it was clunky — is this better?"

## 2. Objections
1. "Sounds great in theory but we're locked into annual contracts" (12x)
   → Counter: Lead with migration support and contract overlap pricing
2. "Our team is too small for this" (9x)
   → Counter: Highlight self-serve tier and 1-person success stories
3. "AI-generated content feels inauthentic" (7x)
   → Counter: Position as augmentation, show human-in-the-loop workflow

## 3. Language Map (use verbatim in copy)
- "scattered across tools" (not "fragmented tech stack")
- "hours on reports nobody reads"
- "flying blind on what works"
- "just guessing at this point"
- "can't prove it to leadership"

## 4. Sentiment: Enthusiastic 38% | Curious 31% | Skeptical 22% | Critical 9%

## 5. Content Gaps
1. Migration/switching guide (28 comments ask about transition)
2. Small team playbook (underserved segment based on comments)
3. ROI calculator or benchmark data

Error Handling

The comments endpoint returns max 100 per call. For posts with 100+ comments, paginate with start parameter. High-comment posts are rare on Company Pages.
Comments under 15 characters (e.g., emoji reactions, “Great!”) add noise. The code filters these out before analysis.
At 60 comments × 400 chars, the prompt stays under typical limits. For 200+ comments, batch into multiple Mave calls and merge results.
LinkedIn’s API does not return commenter profile data unless they’re connected to the token holder. The analysis focuses on comment text, not identity.

LinkedIn Content Integration

Mave Agent