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.

Mavera Surfaces Used

SurfaceRole
Personas (POST /personas, GET /personas)Create budget-segment personas (price-sensitive, mid-market, enterprise)
Focus Groups (POST /focus-groups)Run pricing sensitivity study with Slider, Likert, and Open-Ended questions
Chat + response_formatSynthesize results into a price sensitivity report with optimal price range
The Van Westendorp Price Sensitivity Meter uses four questions to find optimal pricing. This playbook adapts those questions to Mavera’s Slider question type, running them across budget-segment personas to produce a synthetic price sensitivity analysis.

What Value Does Mavera Add?

ValueHow
InsuranceTest pricing with 4 distinct budget segments before announcing. Catch price-perception mismatches early.
Opening new doorsRun pricing studies for new features, bundles, or market segments in an afternoon — no recruiting, no incentives.
Saving timeTraditional Van Westendorp studies take 2-4 weeks and $10K+. This runs in 30 minutes for ~300 credits.

When to Use This

  • You’re setting launch pricing for a new product or feature and need segment-specific sensitivity data.
  • You’re considering a price increase and want to estimate the elasticity before announcing.
  • You’re designing tiered pricing and need to validate where tier boundaries should fall.
  • You’re entering a new market and need a pricing anchor based on local buyer expectations.

What You Need

RequirementDetails
Mavera API keyStarts with mvra_live_. Get one at Developer Settings.
Workspace IDFrom your dashboard URL (ws_...).
Product descriptionClear description of what’s being priced (features, value prop, competitive context).
Price rangeReasonable min/max for the Slider questions (e.g. 00-500/month).
Credits~200–500 total. See Credits Estimate.
Python 3.8+ or Node.js 18+requests / openai for Python; native fetch for Node.
MAVERA_API_KEY=mvra_live_your_key_here
MAVERA_WORKSPACE_ID=ws_your_workspace_id

The Van Westendorp Framework

The original Van Westendorp asks four questions. Mavera adapts each to a Slider question type:
Van Westendorp QuestionMavera SliderWhat It Finds
”At what price would this be too expensive — you wouldn’t consider buying?”Slider: 00 – maxUpper bound (ceiling)
“At what price would this be expensive but worth considering?”Slider: 00 – maxHigh-end acceptable range
”At what price would this be a great deal — a bargain?”Slider: 00 – maxLow-end sweet spot
”At what price would this be so cheap you’d question the quality?”Slider: 00 – maxLower bound (floor)
The intersections of these four curves define:
  • Point of Marginal Cheapness (PMC): Where “too cheap” meets “bargain”
  • Point of Marginal Expensiveness (PME): Where “too expensive” meets “expensive but worth it”
  • Optimal Price Point (OPP): Where “too cheap” meets “too expensive”
  • Acceptable Price Range: Between PMC and PME

Architecture


The Flow

1

Define your product and price range

Write a clear product description and set a realistic price range for the Slider min/max. Too narrow misses the range; too wide reduces precision.
2

Create 4 budget-segment personas

Build personas representing distinct budget tiers: bootstrapped startup, growth-stage, mid-market, and enterprise. Each brings a different price sensitivity.
3

Run the pricing Focus Group

40 respondents across 4 personas (10 per persona). 4 Van Westendorp Slider questions + 1 Likert (value-for-money) + 1 Open-Ended (pricing model preference).
4

Poll for completion

Pricing focus groups with 40 respondents and 6 questions typically complete in 5-10 minutes.
5

Calculate price sensitivity curves

Extract Slider medians and distributions. Compute the four intersection points.
6

Generate the pricing report

Use Chat with structured output to synthesize findings into a pricing recommendation with segment breakdowns.

Code: Full Pricing Research Pipeline

Setup

import os
import json
import time
import statistics
import requests
from openai import OpenAI

MAVERA_API_KEY = os.environ["MAVERA_API_KEY"]
WORKSPACE_ID = os.environ["MAVERA_WORKSPACE_ID"]
BASE = "https://app.mavera.io/api/v1"
HEADERS = {
    "Authorization": f"Bearer {MAVERA_API_KEY}",
    "Content-Type": "application/json",
}
mavera = OpenAI(api_key=MAVERA_API_KEY, base_url=BASE)

PRODUCT_DESCRIPTION = (
    "An AI-powered market research platform that lets you create synthetic audience "
    "personas and run focus groups, pricing studies, and message testing in minutes. "
    "Replaces traditional research agencies for positioning, pricing, and campaign validation. "
    "Includes: unlimited personas, focus groups (up to N=100), AI research agent, "
    "video analysis, and content generation."
)

PRICE_RANGE = {"min": 0, "max": 500, "unit": "$/month"}

BUDGET_PERSONAS = [
    {
        "name": "Bootstrapped Startup Founder",
        "description": (
            "Solo founder or 2-person team at a pre-revenue startup. "
            "Total marketing budget: $500-$2K/month. Every dollar is scrutinized. "
            "Uses free tools wherever possible. Will pay for tools that directly "
            "generate revenue or save significant time. Compares against doing it manually."
        ),
    },
    {
        "name": "Growth-Stage Marketing Lead",
        "description": (
            "Head of Marketing at a Series A/B startup (20-80 employees). "
            "Marketing budget: $10K-$50K/month. Has budget flexibility but needs to justify "
            "ROI for each tool. Currently spending $5-15K/quarter on ad hoc research. "
            "Evaluates based on cost-per-insight vs traditional agencies."
        ),
    },
    {
        "name": "Mid-Market VP Marketing",
        "description": (
            "VP of Marketing at a $10M-$50M ARR company (100-500 employees). "
            "Marketing budget: $50K-$200K/month. Has a research budget line item. "
            "Currently uses a mix of agencies ($20-50K/project) and DIY surveys. "
            "Values speed and always-on availability over cost savings."
        ),
    },
    {
        "name": "Enterprise CMO",
        "description": (
            "CMO at a $100M+ company with a dedicated insights team. "
            "Research budget: $500K-$2M/year. Uses top-tier agencies for major projects. "
            "Evaluates platforms on capability depth, security, and integration with "
            "existing insights stack. Price is secondary to reliability and scalability."
        ),
    },
]

Stage 1 — Create Budget-Segment Personas

def create_budget_personas() -> list[str]:
    """Create 4 budget-segment personas and return their IDs."""
    persona_ids = []

    for persona in BUDGET_PERSONAS:
        resp = requests.post(
            f"{BASE}/personas",
            headers=HEADERS,
            json={
                "name": persona["name"],
                "description": persona["description"],
                "workspace_id": WORKSPACE_ID,
            },
        ).json()

        if "error" in resp:
            raise Exception(f"Failed to create persona '{persona['name']}': {resp['error']['message']}")

        persona_ids.append(resp["id"])
        print(f"✓ Created persona: {persona['name']} ({resp['id']})")

    return persona_ids

Stage 2 — Run Pricing Focus Group

Six questions: 4 Van Westendorp Sliders, 1 Likert (value-for-money), 1 Open-Ended (pricing model).
def run_pricing_focus_group(persona_ids: list[str]) -> dict:
    """Run the Van Westendorp pricing focus group."""
    product_context = (
        f"Product: {PRODUCT_DESCRIPTION}\n\n"
        f"Pricing is per month ({PRICE_RANGE['unit']}). "
        f"Consider the full feature set when answering."
    )

    payload = {
        "name": "Pricing Research — Van Westendorp × 4 Segments",
        "sample_size": 40,
        "persona_ids": persona_ids,
        "workspace_id": WORKSPACE_ID,
        "context": product_context,
        "questions": [
            {
                "question": (
                    f"At what price per month would this product be SO EXPENSIVE "
                    f"that you would not consider buying it, regardless of quality?\n\n"
                    f"Product: {PRODUCT_DESCRIPTION}"
                ),
                "type": "SLIDER",
                "min_value": PRICE_RANGE["min"],
                "max_value": PRICE_RANGE["max"],
                "min_label": f"${PRICE_RANGE['min']}/mo",
                "max_label": f"${PRICE_RANGE['max']}/mo",
                "order": 1,
            },
            {
                "question": (
                    f"At what price per month would this product be EXPENSIVE "
                    f"but you would still consider buying it because of its value?"
                ),
                "type": "SLIDER",
                "min_value": PRICE_RANGE["min"],
                "max_value": PRICE_RANGE["max"],
                "min_label": f"${PRICE_RANGE['min']}/mo",
                "max_label": f"${PRICE_RANGE['max']}/mo",
                "order": 2,
            },
            {
                "question": (
                    f"At what price per month would this product be a BARGAIN — "
                    f"a great deal for what you get?"
                ),
                "type": "SLIDER",
                "min_value": PRICE_RANGE["min"],
                "max_value": PRICE_RANGE["max"],
                "min_label": f"${PRICE_RANGE['min']}/mo",
                "max_label": f"${PRICE_RANGE['max']}/mo",
                "order": 3,
            },
            {
                "question": (
                    f"At what price per month would this product be SO CHEAP that "
                    f"you would question its quality and reliability?"
                ),
                "type": "SLIDER",
                "min_value": PRICE_RANGE["min"],
                "max_value": PRICE_RANGE["max"],
                "min_label": f"${PRICE_RANGE['min']}/mo",
                "max_label": f"${PRICE_RANGE['max']}/mo",
                "order": 4,
            },
            {
                "question": (
                    "If this product were priced at $149/month, would it represent "
                    "good value for money? Rate 1-10."
                ),
                "type": "LIKERT",
                "scale": 10,
                "order": 5,
            },
            {
                "question": (
                    "Which pricing model would you prefer for this type of product?\n"
                    "- Flat monthly fee (unlimited usage)\n"
                    "- Usage-based (pay per focus group / research query)\n"
                    "- Tiered plans (starter / pro / enterprise)\n"
                    "- Annual contract with monthly billing\n\n"
                    "Explain your reasoning."
                ),
                "type": "OPEN_ENDED",
                "order": 6,
            },
        ],
    }

    resp = requests.post(
        f"{BASE}/focus-groups",
        headers=HEADERS,
        json=payload,
    ).json()

    if "error" in resp:
        raise Exception(resp["error"]["message"])

    print(f"✓ Pricing focus group created: {resp['id']}")
    print(f"  Sample size: 40 (10 per segment)")
    print(f"  Questions: 6 (4 sliders + 1 likert + 1 open-ended)")
    return resp


def poll_focus_group(fg_id: str, timeout_min: int = 15) -> dict:
    """Poll until the focus group completes."""
    for attempt in range(timeout_min * 6):
        resp = requests.get(
            f"{BASE}/focus-groups/{fg_id}",
            headers=HEADERS,
        ).json()

        if "error" in resp:
            raise Exception(resp["error"]["message"])
        if resp.get("status") == "COMPLETED":
            print(f"✓ Pricing focus group completed")
            return resp
        if resp.get("status") == "FAILED":
            raise Exception(f"Focus group failed: {resp.get('error', 'Unknown')}")

        time.sleep(10)

    raise TimeoutError(f"Focus group {fg_id} timed out")
Set Slider max_value high enough to capture the true ceiling. If most “too expensive” responses cluster at the max, your range is too narrow and you’ll miss price points.

Stage 3 — Calculate Price Sensitivity

Extract Slider values and compute the Van Westendorp intersection points.
VW_QUESTION_MAP = {
    "too_expensive": "SO EXPENSIVE",
    "expensive_worth_it": "EXPENSIVE but you would still consider",
    "bargain": "BARGAIN",
    "too_cheap": "SO CHEAP",
}


def extract_slider_data(fg_results: dict) -> dict:
    """Extract Van Westendorp slider responses from focus group results."""
    slider_data = {}

    for result in fg_results.get("results", []):
        if result.get("type") != "SLIDER":
            continue

        question = result.get("question", "")
        for key, marker in VW_QUESTION_MAP.items():
            if marker in question:
                slider_data[key] = {
                    "values": result.get("values", []),
                    "mean": result.get("mean_value", 0),
                    "median": result.get("median_value", 0),
                    "std_dev": result.get("std_dev", 0),
                    "distribution": result.get("distribution", {}),
                    "per_persona": result.get("per_persona", []),
                }
                break

    return slider_data


def calculate_vw_points(slider_data: dict) -> dict:
    """Calculate Van Westendorp intersection points from slider medians."""
    too_expensive = slider_data.get("too_expensive", {}).get("median", 0)
    expensive_ok = slider_data.get("expensive_worth_it", {}).get("median", 0)
    bargain = slider_data.get("bargain", {}).get("median", 0)
    too_cheap = slider_data.get("too_cheap", {}).get("median", 0)

    # Approximate intersection points
    pmc = (too_cheap + bargain) / 2       # Point of Marginal Cheapness
    pme = (too_expensive + expensive_ok) / 2  # Point of Marginal Expensiveness
    opp = (too_cheap + too_expensive) / 2  # Optimal Price Point
    idp = (bargain + expensive_ok) / 2     # Indifference Price Point

    return {
        "point_of_marginal_cheapness": round(pmc, 2),
        "point_of_marginal_expensiveness": round(pme, 2),
        "optimal_price_point": round(opp, 2),
        "indifference_price_point": round(idp, 2),
        "acceptable_range": {
            "low": round(pmc, 2),
            "high": round(pme, 2),
        },
        "raw_medians": {
            "too_expensive": too_expensive,
            "expensive_worth_it": expensive_ok,
            "bargain": bargain,
            "too_cheap": too_cheap,
        },
    }


def segment_analysis(slider_data: dict) -> dict:
    """Break down pricing by persona segment."""
    segments = {}

    for key, data in slider_data.items():
        for persona_result in data.get("per_persona", []):
            pid = persona_result.get("persona_id", "unknown")
            if pid not in segments:
                segments[pid] = {}
            segments[pid][key] = {
                "mean": persona_result.get("mean_value", 0),
                "median": persona_result.get("median_value", 0),
            }

    # Calculate per-segment VW points
    for pid, data in segments.items():
        te = data.get("too_expensive", {}).get("median", 0)
        eo = data.get("expensive_worth_it", {}).get("median", 0)
        bg = data.get("bargain", {}).get("median", 0)
        tc = data.get("too_cheap", {}).get("median", 0)

        segments[pid]["optimal_price"] = round((tc + te) / 2, 2)
        segments[pid]["acceptable_range"] = {
            "low": round((tc + bg) / 2, 2),
            "high": round((te + eo) / 2, 2),
        }

    return segments

Stage 4 — Generate Pricing Report

PRICING_REPORT_SCHEMA = {"type": "json_schema", "json_schema": {
    "name": "pricing_report", "strict": True,
    "schema": {
        "type": "object",
        "properties": {
            "executive_summary": {"type": "string"},
            "optimal_price_point": {"type": "number"},
            "acceptable_range_low": {"type": "number"},
            "acceptable_range_high": {"type": "number"},
            "recommended_price": {"type": "number"},
            "recommended_pricing_model": {"type": "string"},
            "segment_recommendations": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "segment": {"type": "string"},
                        "optimal_price": {"type": "number"},
                        "willingness_to_pay": {"type": "string"},
                        "pricing_model_preference": {"type": "string"},
                    },
                    "required": ["segment", "optimal_price", "willingness_to_pay", "pricing_model_preference"],
                },
            },
            "tier_suggestions": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "tier_name": {"type": "string"},
                        "price_point": {"type": "number"},
                        "target_segment": {"type": "string"},
                        "key_features": {"type": "array", "items": {"type": "string"}},
                    },
                    "required": ["tier_name", "price_point", "target_segment", "key_features"],
                },
            },
            "risks": {
                "type": "array",
                "items": {"type": "string"},
            },
            "next_steps": {
                "type": "array",
                "items": {"type": "string"},
            },
        },
        "required": [
            "executive_summary", "optimal_price_point", "acceptable_range_low",
            "acceptable_range_high", "recommended_price", "recommended_pricing_model",
            "segment_recommendations", "tier_suggestions", "risks", "next_steps",
        ],
    },
}}


def generate_pricing_report(
    vw_points: dict, segments: dict, fg_results: dict
) -> dict:
    """Synthesize pricing data into a recommendation report."""
    # Extract Likert and open-ended results
    likert_result = None
    open_ended_result = None
    for result in fg_results.get("results", []):
        if result.get("type") == "LIKERT":
            likert_result = result
        elif result.get("type") == "OPEN_ENDED":
            open_ended_result = result

    prompt = (
        "You are a pricing strategist. Analyze this Van Westendorp pricing data "
        "and produce a pricing recommendation.\n\n"
        f"## Product\n{PRODUCT_DESCRIPTION}\n\n"
        f"## Van Westendorp Results (All Segments)\n{json.dumps(vw_points, indent=2)}\n\n"
        f"## Segment-Level Analysis\n{json.dumps(segments, indent=2)}\n\n"
    )

    if likert_result:
        prompt += f"## Value-for-Money at $149/mo\nMean score: {likert_result.get('mean_score', 'N/A')}/10\n\n"

    if open_ended_result:
        prompt += f"## Pricing Model Preferences\n{open_ended_result.get('summary', 'N/A')}\n\n"

    prompt += (
        "Produce a recommendation with:\n"
        "1. Optimal price point and acceptable range\n"
        "2. Per-segment pricing sensitivity\n"
        "3. Suggested pricing tiers\n"
        "4. Risks of pricing too high or too low\n"
        "5. Next steps for validation"
    )

    resp = mavera.responses.create(
        model="mavera-1",
        input=[{"role": "user", "content": prompt}],
        extra_body={"response_format": PRICING_REPORT_SCHEMA},
    )

    return json.loads(resp.output[0].content[0].text)

Running the Full Pipeline

def run_pricing_research():
    print("=" * 60)
    print("PRICING RESEARCH — VAN WESTENDORP")
    print("=" * 60)

    # Stage 1: Create personas
    print("\n--- Stage 1: Creating Budget-Segment Personas ---")
    persona_ids = create_budget_personas()

    # Stage 2: Run focus group
    print("\n--- Stage 2: Running Pricing Focus Group ---")
    fg = run_pricing_focus_group(persona_ids)
    fg_results = poll_focus_group(fg["id"])

    # Stage 3: Calculate price sensitivity
    print("\n--- Stage 3: Calculating Price Sensitivity ---")
    slider_data = extract_slider_data(fg_results)
    vw_points = calculate_vw_points(slider_data)
    segments = segment_analysis(slider_data)

    print(f"\nVan Westendorp Results:")
    print(f"  Too cheap (floor):    ${vw_points['raw_medians']['too_cheap']}/mo")
    print(f"  Bargain:              ${vw_points['raw_medians']['bargain']}/mo")
    print(f"  Expensive but OK:     ${vw_points['raw_medians']['expensive_worth_it']}/mo")
    print(f"  Too expensive (ceil): ${vw_points['raw_medians']['too_expensive']}/mo")
    print(f"\n  Optimal Price Point:  ${vw_points['optimal_price_point']}/mo")
    print(f"  Acceptable Range:     ${vw_points['acceptable_range']['low']} – ${vw_points['acceptable_range']['high']}/mo")

    # Stage 4: Generate report
    print("\n--- Stage 4: Generating Pricing Report ---")
    report = generate_pricing_report(vw_points, segments, fg_results)

    print(f"\nRecommended price: ${report['recommended_price']}/mo")
    print(f"Pricing model: {report['recommended_pricing_model']}")
    print(f"\nTier suggestions:")
    for tier in report.get("tier_suggestions", []):
        print(f"  {tier['tier_name']}: ${tier['price_point']}/mo → {tier['target_segment']}")

    # Save outputs
    output = {
        "van_westendorp": vw_points,
        "segments": segments,
        "report": report,
    }
    with open("pricing_research.json", "w") as f:
        json.dump(output, f, indent=2)

    print("\n✓ Saved pricing_research.json")
    return report


if __name__ == "__main__":
    run_pricing_research()

Example Output

{
  "executive_summary": "Van Westendorp analysis across 4 budget segments reveals an optimal price point of $129/mo with an acceptable range of $79-$229/mo. Strong segment divergence: bootstrapped founders cap at $49/mo while enterprise CMOs find anything under $99/mo suspiciously cheap. Tiered pricing is the clear winner in model preference.",
  "optimal_price_point": 129,
  "acceptable_range_low": 79,
  "acceptable_range_high": 229,
  "recommended_price": 149,
  "recommended_pricing_model": "Tiered plans with usage-based overage",
  "segment_recommendations": [
    {
      "segment": "Bootstrapped Startup Founder",
      "optimal_price": 39,
      "willingness_to_pay": "Low — caps at $49/mo, highly price sensitive",
      "pricing_model_preference": "Usage-based or freemium"
    },
    {
      "segment": "Growth-Stage Marketing Lead",
      "optimal_price": 129,
      "willingness_to_pay": "Moderate — willing to pay $99-$199/mo with clear ROI",
      "pricing_model_preference": "Tiered plans"
    },
    {
      "segment": "Enterprise CMO",
      "optimal_price": 349,
      "willingness_to_pay": "High — price is secondary to capability and reliability",
      "pricing_model_preference": "Annual contract with dedicated support"
    }
  ],
  "tier_suggestions": [
    {
      "tier_name": "Starter",
      "price_point": 49,
      "target_segment": "Bootstrapped startups",
      "key_features": ["5 personas", "3 focus groups/mo", "Basic chat"]
    },
    {
      "tier_name": "Growth",
      "price_point": 149,
      "target_segment": "Growth-stage companies",
      "key_features": ["25 personas", "15 focus groups/mo", "Mave agent", "Video analysis"]
    },
    {
      "tier_name": "Enterprise",
      "price_point": 399,
      "target_segment": "Mid-market and enterprise",
      "key_features": ["Unlimited personas", "Unlimited focus groups", "Priority support", "SSO", "API access"]
    }
  ],
  "risks": [
    "Pricing below $79/mo signals low quality to enterprise buyers",
    "Pricing above $229/mo loses growth-stage segment entirely",
    "Usage-based pricing creates unpredictable bills — monitor NPS"
  ],
  "next_steps": [
    "A/B test $129 vs $149 on pricing page with real traffic",
    "Run a follow-up focus group testing tier feature bundles",
    "Validate enterprise pricing with 5 real enterprise prospects"
  ]
}

Variations

Create personas representing your current customer base. Test the proposed increase against their current price anchor:
{
    "question": (
        "You currently pay $99/mo for this product. "
        "At what price would you cancel your subscription?"
    ),
    "type": "SLIDER",
    "min_value": 99,
    "max_value": 500,
    "order": 1,
}
Add questions that price individual features to find which drive willingness-to-pay:
features = ["AI research agent", "Video analysis", "Focus groups (N=100)"]
for i, feature in enumerate(features):
    questions.append({
        "question": f"How much would you pay monthly for {feature} alone?",
        "type": "SLIDER",
        "min_value": 0, "max_value": 200,
        "order": 7 + i,
    })
Run the same study with region-specific personas (US, EU, APAC, LATAM) to set geographic pricing:
regions = {
    "US": ["us_startup_id", "us_enterprise_id"],
    "EU": ["eu_startup_id", "eu_enterprise_id"],
    "APAC": ["apac_startup_id", "apac_enterprise_id"],
}
for region, ids in regions.items():
    fg = run_pricing_focus_group(ids)
    # Compare VW points across regions
Include competitor pricing in the context so personas anchor relative to known alternatives:
PRODUCT_DESCRIPTION += (
    "\n\nFor reference, similar products are priced at:\n"
    "- Competitor A: $99/mo (basic plan)\n"
    "- Competitor B: $249/mo (all features)\n"
    "- Competitor C: Usage-based, typically $150-$300/mo"
)

Credits Estimate

StageTypical CostNotes
Create 4 personas0Persona creation is free
Focus Group (N=40, 6 questions)200–400 credits4 sliders + 1 likert + 1 open-ended
Pricing report (1 chat call)5–15 creditsSingle structured output
Total~205–415 credits
For a quick directional read, start with N=20 (5 per segment) at roughly half the cost. Scale to N=40+ for tier-worthy data you’d put in a pricing deck.

See Also

Focus Groups

Slider, Likert, and all 12 question types

Positioning Workshop

Test positioning alongside pricing

Market Entry Research

Research the market before pricing

Brand Perception Audit

Understand brand perception context for pricing

Persona Debate

Opposing personas evaluate pricing at different points

Credits & Budget

Track and manage credit usage