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

# Competitor Content Audit

> Audit competitor top pages from SEMrush and validate messaging differentiators with Mavera Focus Groups

### Scenario

Understand what content works for competitors — which pages drive the most organic traffic and where messaging gaps exist. Pull `url_organic` for a competitor, send to Mave for gap analysis, then validate your differentiators with a focus group.

### Architecture

```mermaid theme={"dark"}
flowchart LR
    A["SEMrush url_organic (competitor top pages)"] --> B["Parse semicolon-delimited"]
    B --> C["Extract URLs, traffic, keywords"]
    C --> D["POST /api/v1/mave/chat"]
    D --> E["POST /api/v1/focus-groups"]
```

### Code

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

  SR, MV = os.environ["SEMRUSH_API_KEY"], os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  COMP = "competitor.com"

  resp = requests.get("https://api.semrush.com/", params={
      "type": "url_organic", "key": SR, "domain": COMP,
      "database": "us", "display_limit": 50, "display_sort": "tr_desc",
      "export_columns": "Ur,Fl,Fk,Fp,Nq,Td",
  })
  reader = csv.reader(io.StringIO(resp.text), delimiter=";")
  next(reader)
  pages = [{"url": r[0], "traffic": int(r[1] or 0), "keywords": int(r[2] or 0),
            "top_kw": r[4]} for r in reader if len(r) >= 6]

  page_block = "\n".join(f"- {p['url']} | traffic: {p['traffic']} | top: \"{p['top_kw']}\""
                         for p in sorted(pages, key=lambda p: -p["traffic"])[:25])
  mave = requests.post(f"{MB}/mave/chat", headers=MH, json={
      "message": f"Analyze competitor content gaps.\n\nCOMPETITOR: {COMP}\n"
                 f"TOP PAGES:\n{page_block}\n\nAnalyze: 1) Content themes 2) Gaps "
                 f"3) Messaging patterns 4) Our opportunities 5) 5 article concepts",
  }).json()
  print(mave.get("content", "")[:1500])

  fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
      "name": f"Audit: {COMP}",
      "auto_personas": {"count": 3, "description": "B2B marketing managers evaluating content tools"},
      "questions": [
          "Rank: 1) AI personas from real data 2) Pre-publish focus groups 3) Brand voice extraction",
          "What's missing? What would make you switch tools?",
          f"What would make you trust our content over {COMP}'s?",
      ], "responses_per_persona": 2,
  }).json()

  for _ in range(24):
      time.sleep(5)
      data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
      if data.get("status") == "completed": break
  for r in data.get("responses", [])[:6]:
      print(f"[{r.get('persona_id','?')}] {r.get('answer','')[:250]}\n")
  ```

  ```javascript JavaScript theme={"dark"}
  const SR = process.env.SEMRUSH_API_KEY, MV = process.env.MAVERA_API_KEY;
  const MB = "https://app.mavera.io/api/v1";
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const COMP = "competitor.com";

  const params = new URLSearchParams({
    type: "url_organic", key: SR, domain: COMP,
    database: "us", display_limit: "50", display_sort: "tr_desc",
    export_columns: "Ur,Fl,Fk,Fp,Nq,Td",
  });
  const text = await fetch(`https://api.semrush.com/?${params}`).then((r) => r.text());
  const pages = text.trim().split("\n").slice(1).map((line) => {
    const c = line.split(";");
    return c.length >= 6 ? { url: c[0], traffic: parseInt(c[1]) || 0, topKw: c[4] } : null;
  }).filter(Boolean).sort((a, b) => b.traffic - a.traffic);

  const block = pages.slice(0, 25).map((p) => `- ${p.url} | traffic: ${p.traffic} | "${p.topKw}"`).join("\n");
  const mave = await fetch(`${MB}/mave/chat`, { method: "POST", headers: MH,
    body: JSON.stringify({
      message: `Competitor gaps.\nCOMPETITOR: ${COMP}\nPAGES:\n${block}\nAnalyze: 1) Themes 2) Gaps 3) Messaging 4) Opportunities 5) 5 concepts`,
    }),
  }).then((r) => r.json());
  console.log((mave.content || "").slice(0, 1500));

  const fg = await fetch(`${MB}/focus-groups`, { method: "POST", headers: MH,
    body: JSON.stringify({
      name: `Audit: ${COMP}`,
      auto_personas: { count: 3, description: "B2B marketers evaluating content tools" },
      questions: ["Rank: 1) AI personas 2) Pre-publish focus groups 3) Brand voice extraction",
        "What's missing? What would make you switch?",
        "What would make you trust our content over theirs?"],
      responses_per_persona: 2,
    }),
  }).then((r) => r.json());

  let data;
  for (let i = 0; i < 24; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    data = await fetch(`${MB}/focus-groups/${fg.id}`, { headers: MH }).then((r) => r.json());
    if (data.status === "completed") break;
  }
  for (const r of (data.responses || []).slice(0, 6))
    console.log(`[${r.persona_id}] ${(r.answer || "").slice(0, 250)}\n`);
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
## Themes: SEO guides (38%), tool comparisons (22%), industry reports (18%)

## Opportunities
1. Persona-driven SEO — no competitor covers audience intelligence. We own it.
2. Pre-publish validation — they A/B test after launch. Focus groups pre-launch
   is a unique differentiator.

[per_auto_01] #1: Focus group validation before publishing. Pre-publish testing
  with structured questions is genuinely different.
```

### Error Handling

<AccordionGroup>
  <Accordion title="ERROR 50 :: NOTHING FOUND">The competitor domain may lack organic data in the selected database. Try a different regional database or verify domain spelling.</Accordion>
  <Accordion title="Traffic estimates are modeled">SEMrush traffic numbers are estimates based on position and CTR models. Use for relative comparison, not absolute counts.</Accordion>
  <Accordion title="URL encoding">Some URLs in the response may contain encoded characters. Decode before using in further API calls.</Accordion>
</AccordionGroup>
