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

# Revenue Analysis → Pricing Focus Group

> Pull Amplitude revenue metrics and ARPU by segment, then validate pricing changes with a Mavera Focus Group using Slider and NPS questions

### Scenario

Amplitude tracks revenue events with properties like plan type, ARPU, and segment. You pull revenue metrics, compute ARPU by user segment, and run a Mavera Focus Group asking synthetic personas to rate willingness to pay at specific price points using Slider and NPS question types. The result is pricing validation grounded in real revenue data and tested against persona reactions.

### Architecture

```mermaid theme={"dark"}
flowchart LR
A["Amplitude revenue metrics"] --> B["Identify price points and tiers"] --> C["POST /api/v1/personas"] --> D["POST /api/v1/focus-groups"] --> E["Willingness-to-pay insights"]
```

### Code

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

  AMP_KEY = os.environ["AMPLITUDE_API_KEY"]
  AMP_SECRET = os.environ["AMPLITUDE_SECRET_KEY"]
  MV = os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  amp_auth = (AMP_KEY, AMP_SECRET)

  r = requests.get(
      "https://amplitude.com/api/2/revenue/day",
      auth=amp_auth,
      params={
          "start": "20260215",
          "end": "20260317",
          "m": "revenue",
      },
  )
  if r.status_code == 429:
      time.sleep(int(r.headers.get("Retry-After", 60)))
      r = requests.get(
          "https://amplitude.com/api/2/revenue/day",
          auth=amp_auth,
          params={"start": "20260215", "end": "20260317", "m": "revenue"},
      )
  r.raise_for_status()
  revenue_data = r.json()

  series = revenue_data.get("data", {}).get("series", [])
  xvals = revenue_data.get("data", {}).get("xValues", [])

  total_revenue = 0
  paying_users = 0
  for seg in series:
      vals = seg.get("values", seg.get("value", []))
      if isinstance(vals, list):
          total_revenue += sum(v for v in vals if isinstance(v, (int, float)))

  SEGMENTS = {
      "Starter": {"price": 29, "arpu": 29, "users_est": 1200},
      "Pro": {"price": 79, "arpu": 74, "users_est": 800},
      "Enterprise": {"price": 249, "arpu": 312, "users_est": 150},
  }

  segment_summary = "\n".join(
      f"- {name}: ${info['price']}/mo list, ${info['arpu']} actual ARPU, ~{info['users_est']} users"
      for name, info in SEGMENTS.items()
  )

  personas = []
  for seg_name, info in SEGMENTS.items():
      persona = requests.post(f"{MB}/personas", headers=MH, json={
          "name": f"Amplitude Revenue: {seg_name}",
          "description": (
              f"{seg_name} plan subscriber. Paying ${info['arpu']}/mo (list ${info['price']}/mo). "
              f"Estimated {info['users_est']} users in this tier. "
              f"{'Price-sensitive individual or small team.' if info['price'] < 50 else 'Mid-market team with budget authority.' if info['price'] < 150 else 'Enterprise buyer with procurement process.'}"
          ),
          "psychographic": {
              "plan": seg_name.lower(),
              "arpu": info["arpu"],
              "price_sensitivity": "high" if info["price"] < 50 else "medium" if info["price"] < 150 else "low",
          },
      }).json()
      personas.append(persona)
      time.sleep(0.3)

  PROPOSED_PRICES = {
      "Starter": {"current": 29, "proposed": 39},
      "Pro": {"current": 79, "proposed": 99},
      "Enterprise": {"current": 249, "proposed": 299},
  }

  price_context = "\n".join(
      f"- {name}: ${p['current']}/mo → ${p['proposed']}/mo ({(p['proposed']-p['current'])/p['current']:.0%} increase)"
      for name, p in PROPOSED_PRICES.items()
  )

  fg = requests.post(f"{MB}/focus-groups", headers=MH, json={
      "name": "Amplitude: Pricing Willingness-to-Pay Study",
      "persona_ids": [p["id"] for p in personas],
      "questions": [
          f"We're considering adjusting pricing:\n{price_context}\nRate your willingness to pay the new price on a scale of 1-10, where 1 = 'I would cancel immediately' and 10 = 'No hesitation, still great value'. Explain your rating.",
          "At what monthly price would this product become too expensive for you to justify? Give a specific dollar amount and explain the tipping point.",
          "If the price increased but included ONE new feature, what feature would make the increase worth it? Be specific about the capability and why it matters to you.",
          "On a scale of 0-10, how likely are you to recommend this product to a colleague at the CURRENT price? What about at the NEW price? (NPS format)",
          "If we offered annual billing at a 20% discount, would that change your reaction to the price increase? Why or why not?",
      ],
      "context": f"""Revenue analysis from Amplitude (last 30 days):

  CURRENT SEGMENTS:
  {segment_summary}

  PROPOSED PRICE CHANGES:
  {price_context}

  Total estimated MRR: ${sum(s['arpu'] * s['users_est'] for s in SEGMENTS.values()):,}
  Projected MRR at new prices (assuming 0% churn): ${sum(PROPOSED_PRICES[n]['proposed'] * SEGMENTS[n]['users_est'] for n in SEGMENTS):,}""",
      "responses_per_persona": 2,
  }).json()

  for _ in range(30):
      time.sleep(5)
      data = requests.get(f"{MB}/focus-groups/{fg['id']}", headers=MH).json()
      if data.get("status") == "completed":
          break

  print(f"Focus Group: {data.get('id')} — {data.get('status')}\n")
  for resp in data.get("responses", []):
      print(f"[{resp.get('persona_id','?')}] {resp.get('question','')[:80]}")
      print(f"  → {resp.get('answer','')[:300]}\n")
  ```

  ```javascript JavaScript theme={"dark"}
  const AMP_KEY = process.env.AMPLITUDE_API_KEY;
  const AMP_SECRET = process.env.AMPLITUDE_SECRET_KEY;
  const MV = process.env.MAVERA_API_KEY;
  const MB = "https://app.mavera.io/api/v1";
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const ampAuth = "Basic " + Buffer.from(`${AMP_KEY}:${AMP_SECRET}`).toString("base64");

  let revRes = await fetch(
    "https://amplitude.com/api/2/revenue/day?start=20260215&end=20260317&m=revenue",
    { headers: { Authorization: ampAuth } }
  );
  if (revRes.status === 429) {
    await new Promise((r) => setTimeout(r, parseInt(revRes.headers.get("Retry-After") || "60") * 1000));
    revRes = await fetch(
      "https://amplitude.com/api/2/revenue/day?start=20260215&end=20260317&m=revenue",
      { headers: { Authorization: ampAuth } }
    );
  }
  const revenueData = await revRes.json();

  const SEGMENTS = {
    Starter: { price: 29, arpu: 29, usersEst: 1200 },
    Pro: { price: 79, arpu: 74, usersEst: 800 },
    Enterprise: { price: 249, arpu: 312, usersEst: 150 },
  };

  const PROPOSED = {
    Starter: { current: 29, proposed: 39 },
    Pro: { current: 79, proposed: 99 },
    Enterprise: { current: 249, proposed: 299 },
  };

  const segSummary = Object.entries(SEGMENTS)
    .map(([n, s]) => `- ${n}: $${s.price}/mo list, $${s.arpu} ARPU, ~${s.usersEst} users`)
    .join("\n");
  const priceContext = Object.entries(PROPOSED)
    .map(([n, p]) => `- ${n}: $${p.current}/mo → $${p.proposed}/mo (${Math.round((p.proposed - p.current) / p.current * 100)}% increase)`)
    .join("\n");

  const personas = [];
  for (const [name, info] of Object.entries(SEGMENTS)) {
    const sens = info.price < 50 ? "Price-sensitive individual." : info.price < 150 ? "Mid-market team." : "Enterprise buyer.";
    const p = await fetch(`${MB}/personas`, { method: "POST", headers: MH,
      body: JSON.stringify({
        name: `Amplitude Revenue: ${name}`,
        description: `${name} subscriber. $${info.arpu}/mo ARPU. ~${info.usersEst} users. ${sens}`,
        psychographic: { plan: name.toLowerCase(), arpu: info.arpu, price_sensitivity: info.price < 50 ? "high" : info.price < 150 ? "medium" : "low" },
      }),
    }).then((r) => r.json());
    personas.push(p);
    await new Promise((r) => setTimeout(r, 300));
  }

  const fg = await fetch(`${MB}/focus-groups`, { method: "POST", headers: MH,
    body: JSON.stringify({
      name: "Amplitude: Pricing WTP Study",
      persona_ids: personas.map((p) => p.id),
      questions: [
        `Pricing change:\n${priceContext}\nRate willingness to pay new price 1-10. Explain.`,
        "At what monthly price would this be too expensive? Give a dollar amount and explain.",
        "If price increased but included ONE new feature, what feature justifies it?",
        "NPS: How likely to recommend at CURRENT price (0-10)? At NEW price? Explain difference.",
        "Annual billing at 20% discount — does that change your reaction? Why?",
      ],
      context: `Revenue analysis (30d):\n\n${segSummary}\n\nProposed:\n${priceContext}\n\nCurrent MRR: $${Object.values(SEGMENTS).reduce((s, v) => s + v.arpu * v.usersEst, 0).toLocaleString()}\nProjected MRR (0% churn): $${Object.entries(PROPOSED).reduce((s, [n, p]) => s + p.proposed * SEGMENTS[n].usersEst, 0).toLocaleString()}`,
      responses_per_persona: 2,
    }),
  }).then((r) => r.json());

  let data;
  for (let i = 0; i < 30; 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;
  }

  console.log(`Focus Group: ${data.id} — ${data.status}\n`);
  for (const resp of data.responses || []) {
    console.log(`[${resp.persona_id}] ${(resp.question || "").slice(0, 80)}`);
    console.log(`  → ${(resp.answer || "").slice(0, 300)}\n`);
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Focus Group: fg_amp_pricing_4m7b — completed

[Starter] Rate willingness to pay $39/mo (up from $29)
  → 4/10. That's a 34% increase with no new value. I chose this plan
    because it was under $30/mo — a threshold I can expense without
    approval. At $39, I need my manager's sign-off, which means a
    conversation I don't want to have. You'd lose me at $35.

[Pro] At what price does this become too expensive?
  → $120/mo is my ceiling. Right now at $79 I feel good about the
    value. At $99 I'd start comparing alternatives. At $120 I'd run
    a formal evaluation of competitors. The gap between "easy renewal"
    and "procurement review" is about $100/mo for our team size.

[Enterprise] If price increased but included ONE new feature
  → SSO/SAML integration. We're paying $249/mo and still managing
    user access manually. Every enterprise tool we buy has SSO. If
    you added it at $299, that's a no-brainer — I'm already spending
    more than that on the workaround.

[Starter] NPS: Current vs. new price
  → Current price: 8/10 — I actively recommend it to freelancer
    friends. New price: 5/10 — I'd still use it but stop recommending
    it because cheaper alternatives exist. That 3-point NPS drop
    represents your entire word-of-mouth engine for small users.

[Pro] Annual billing at 20% discount
  → Yes, completely changes it. $99/mo × 12 = $1,188/yr. At 20%
    off that's $950/yr or ~$79/mo — exactly what I'm paying now.
    You get cash upfront, I get price stability. Win-win. But only
    if you lock the rate for at least 2 years.
```

### Error Handling

<AccordionGroup>
  <Accordion title="Revenue API requires revenue tracking">The `/api/2/revenue/day` endpoint only returns data if you track revenue events via `amplitude.revenue()` or the Revenue API. If you only track subscriptions externally, import revenue data via the HTTP API or use a CDP integration.</Accordion>
  <Accordion title="ARPU calculation caveats">The sample ARPU values are hardcoded for demonstration. In production, calculate ARPU from the Revenue API response by dividing total revenue by paying user count per segment.</Accordion>
  <Accordion title="Focus Group with Slider/NPS semantics">Mavera Focus Groups support free-text responses. To simulate Slider and NPS question types, phrase questions with explicit scales (1-10, 0-10) and ask personas to provide both a numeric rating and explanation. Parse numeric values from the response text.</Accordion>
  <Accordion title="Price sensitivity anchoring">Personas may anchor to the current price in their responses. For more accurate willingness-to-pay data, run separate Focus Groups with different proposed prices (Van Westendorp method: too cheap, cheap, expensive, too expensive).</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Amplitude Integration" icon="wave-square" href="/integrations/amplitude">
    Back to Amplitude integration overview
  </Card>

  <Card title="Persona Calibration" icon="user-gear" href="/integrations/amplitude/persona-calibration">
    Calibrate personas from Amplitude composition data
  </Card>

  <Card title="Focus Groups API" icon="comments" href="/api-reference/focus-groups">
    Full reference for POST /api/v1/focus-groups
  </Card>

  <Card title="Personas API" icon="users" href="/api-reference/personas">
    Full reference for POST /api/v1/personas
  </Card>
</CardGroup>
