> ## 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.

# Reply Mining for Persona Development

> Collect reply and quote-tweet patterns around your brand on X, analyze behavioral archetypes, and create data-grounded Mavera personas

## Scenario

People who reply, quote-tweet, and mention your brand reveal interests and engagement patterns. This job collects that data, analyzes behavioral patterns via Mave, and creates data-grounded persona profiles — built from real interactions, not assumptions.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A["X GET /tweets/search/recent (quotes + mentions)"] --> B["Aggregate patterns"]
    B --> C["Mavera POST /mave/chat"]
    C --> D["POST /personas"]
```

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  X = os.environ["X_BEARER_TOKEN"]; MV = os.environ["MAVERA_API_KEY"]
  X_BASE = "https://api.x.com/2"; MV_BASE = "https://app.mavera.io/api/v1"
  X_H = {"Authorization": f"Bearer {X}"}
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  BRAND = "yourbrand"
  queries = [f"@{BRAND} -is:retweet", f"url:x.com/{BRAND} is:quote"]

  # 1. Collect interactions
  interactions = []
  for q in queries:
      nt = None
      for _ in range(3):
          params = {"query": q, "max_results": 100, "tweet.fields": "public_metrics,author_id",
              "expansions": "author_id", "user.fields": "username,description,public_metrics"}
          if nt: params["next_token"] = nt
          r = requests.get(f"{X_BASE}/tweets/search/recent", headers=X_H, params=params)
          if r.status_code == 429:
              time.sleep(int(r.headers.get("x-rate-limit-reset", time.time()+60)) - int(time.time()))
              r = requests.get(f"{X_BASE}/tweets/search/recent", headers=X_H, params=params)
          r.raise_for_status(); data = r.json()
          users = {u["id"]: u for u in data.get("includes",{}).get("users",[])}
          for t in data.get("data",[]):
              a = users.get(t.get("author_id"),{})
              interactions.append({"text": t["text"], "type": "quote" if "is:quote" in q else "reply",
                  "username": a.get("username",""), "bio": a.get("description",""),
                  "followers": a.get("public_metrics",{}).get("followers_count",0)})
          nt = data.get("meta",{}).get("next_token")
          if not nt: break
          time.sleep(1)

  # 2. Pattern analysis
  block = "\n\n".join(f"@{i['username']} ({i['followers']:,} fol) [{i['type']}]\nBio: {i['bio'][:120]}\n{i['text'][:200]}"
      for i in sorted(interactions, key=lambda x: -x["followers"])[:40])

  analysis = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
      "message": f"Analyze reply/quote patterns around @{BRAND}. {len(interactions)} interactions.\n\n{block}\n\n"
          "Identify 4-6 persona archetypes. For each: name, interaction pattern, topics, language, estimated %, value to brand."
  }).json()
  archetypes = analysis.get("content","")
  print(archetypes[:1200])

  # 3. Create personas
  blocks = archetypes.split("##")[1:] if "##" in archetypes else [archetypes]
  for b in blocks[:6]:
      lines = b.strip().split("\n")
      name = lines[0].strip().strip("#").strip() if lines else "X Persona"
      p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
          "name": f"X Audience: {name}", "description": "\n".join(lines[1:])[:500],
          "psychographic": {"source": "x_reply_mining", "brand": BRAND},
      }).json()
      print(f"  Created: {name} — {p['id']}")
      time.sleep(0.3)
  ```

  ```javascript JavaScript theme={"dark"}
  // --- Same X_BASE, MV_BASE, X_H, MV_H setup as Job 1 ---
  const BRAND = "yourbrand";
  const queries = [`@${BRAND} -is:retweet`, `url:x.com/${BRAND} is:quote`];
  const interactions = [];

  for (const q of queries) {
    let nt = null;
    for (let i = 0; i < 3; i++) {
      const params = new URLSearchParams({ query: q, max_results: "100",
        "tweet.fields": "public_metrics,author_id", expansions: "author_id",
        "user.fields": "username,description,public_metrics" });
      if (nt) params.set("next_token", nt);
      let r = await fetch(`${X_BASE}/tweets/search/recent?${params}`, { headers: X_H });
      if (r.status === 429) { await new Promise(res => setTimeout(res, 60000));
        r = await fetch(`${X_BASE}/tweets/search/recent?${params}`, { headers: X_H }); }
      if (!r.ok) break; const data = await r.json();
      const users = Object.fromEntries((data.includes?.users||[]).map(u => [u.id, u]));
      for (const t of data.data||[]) { const a = users[t.author_id]||{};
        interactions.push({ text: t.text, type: q.includes("is:quote")?"quote":"reply",
          username: a.username||"", bio: a.description||"", followers: a.public_metrics?.followers_count||0 }); }
      nt = data.meta?.next_token; if (!nt) break;
      await new Promise(r => setTimeout(r, 1000));
    }
  }

  const block = interactions.sort((a,b) => b.followers-a.followers).slice(0,40)
    .map(i => `@${i.username} (${i.followers.toLocaleString()}) [${i.type}]\nBio: ${i.bio.slice(0,120)}\n${i.text.slice(0,200)}`).join("\n\n");
  const analysis = await fetch(`${MV_BASE}/mave/chat`, { method: "POST", headers: MV_H,
    body: JSON.stringify({ message: `Analyze @${BRAND} patterns. ${interactions.length} interactions.\n\n${block}\n\nIdentify 4-6 archetypes.` }),
  }).then(r => r.json());

  for (const b of ((analysis.content||"").split("##").slice(1)||[analysis.content]).slice(0,6)) {
    const lines = b.trim().split("\n");
    const name = (lines[0]||"Persona").replace(/#/g,"").trim();
    const p = await fetch(`${MV_BASE}/personas`, { method: "POST", headers: MV_H,
      body: JSON.stringify({ name: `X Audience: ${name}`, description: lines.slice(1).join("\n").slice(0,500),
        psychographic: { source: "x_reply_mining", brand: BRAND } }),
    }).then(r => r.json());
    console.log(`  Created: ${name} — ${p.id}`);
  }
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
## Industry Thought Leader (12%) — Quote-tweets with commentary. Influencer.
## Help-Seeker (35%) — "How do I..." replies. Active customer, upsell target.
## Lurker-Turned-Engager (20%) — Silent liker, replies to pain-point tweets. Prospect.
## Competitive Comparer (15%) — Mentions competitors alongside you. Evaluating.

Created: Industry Thought Leader — per_x_tl_01
Created: Help-Seeker — per_x_hs_02
Created: Lurker-Turned-Engager — per_x_le_03
Created: Competitive Comparer — per_x_cc_04
```

## Error Handling

<AccordionGroup>
  <Accordion title="Quote tweet syntax">Use `url:x.com/{username} is:quote` to find quotes. If sparse, broaden to `@{username} is:quote`.</Accordion>
  <Accordion title="Archetype parsing">Code splits Mave output on `##` headers. If Mave uses a different format, fewer personas are created. Review raw analysis.</Accordion>
</AccordionGroup>

***

<CardGroup cols={2}>
  <Card title="X / Twitter Integration" icon="arrow-left" href="/integrations/x-twitter" />

  <Card title="Personas" icon="user" href="/features/personas" />
</CardGroup>
