Comparing audiences
Same question, different rooms.
Endpoint: POST /api/v1/audiences/compare. Draws n respondents per audience live, then computes the comparison from the real replies.
What it answers
The classic cross-segment question: "If I ask solo founders, mid-market ops leads, and enterprise IT the same thing — do they agree? Where do they split? Is one message enough, or do I need to segment?" This endpoint answers it with evidence, not vibes.
Input
| Field | Type | What it does |
|---|---|---|
question (required) | string | Asked identically to every audience. |
audienceIds (required) | string[] | Two or more audiences to compare. |
labels | string[] | Human names, parallel to audienceIds (falls back to the id). |
respondentsPerAudience | integer | n>1 respondents drawn per audience (default 3, capped at 8 for cost). Each is a distinct seeded draw. |
seed | integer | Reproducible draws across all audiences. |
{
"question": "Would you switch to a $29/mo all-in-one finance app? Why or why not?",
"audienceIds": ["aud_solo_founders", "aud_midmarket_ops", "aud_enterprise_it"],
"labels": ["Solo founders", "Mid-market ops", "Enterprise IT"],
"respondentsPerAudience": 4,
"seed": 42
}Output — the similarity / difference / insight / action schema
{ "ok": true, "data": {
"question": "...",
"audiences": [
{ "label": "Solo founders", "respondents": [ { "seed": 42, "content": "...", "abstained": false, "themes": ["Price","Trust"], "sentiment": 0.5 }, ... ],
"themeShares": [ { "theme": "Price", "share": 1.0 }, { "theme": "Trust", "share": 0.5 } ],
"meanSentiment": 0.42, "abstentionRate": 0.0 },
... ],
"pairs": [
{ "a": "Solo founders", "b": "Enterprise IT",
"themeSimilarity": 0.25, // Jaccard overlap of themes raised [0,1]
"sentimentGap": 0.6, // |meanSentiment difference|
"sharedThemes": ["Price"],
"distinctiveToA": ["Speed"],
"distinctiveToB": ["Security","Compliance"] } ],
"universalThemes": ["Price"], // raised by EVERY audience
"polarizingThemes": [ { "theme": "Compliance", "audiences": ["Enterprise IT"] } ], // raised by exactly one
"insight": "All 3 audiences raised: Price. Biggest divergence: Solo founders vs Enterprise IT (theme overlap 0.25). Sentiment ranges from Enterprise IT (-0.18) to Solo founders (0.42).",
"action": "Segment your approach: Solo founders and Enterprise IT respond on different themes (Speed vs Security, Compliance). A single message will underperform — tailor per audience.",
"honesty": { "basis": "code-computed-from-live-replies", "ranLive": true, "note": "..." }
} }How each number is computed (the proof)
| Field | Computation |
|---|---|
themeShares | Fraction of an audience's respondents whose reply expressed each theme (theme detection over the real text). |
meanSentiment | Signed sentiment lexicon over each reply, averaged across the audience's respondents. Range [-1, 1]. |
themeSimilarity | Jaccard index of the theme sets two audiences raised: |A∩B| / |A∪B|. |
universalThemes | Themes present in every audience's set — your cross-segment common ground. |
polarizingThemes | Themes raised by exactly one audience — where the populations genuinely differ. |
insight / action | Templated from the computed contrast (most-similar/most-different pair, sentiment range, universal themes). Evidence-anchored — never a model assertion of fact. |
The bright line: persona replies are live model output; every number — shares, Jaccard similarity, sentiment, agreement — is computed in code from those replies. The action is a recommendation derived from the computed contrast, not the model declaring what's true. Set respondentsPerAudience higher for a more stable read (more draws = less per-respondent noise).
Reading the result
- High
themeSimilarity(≥0.6) + smallsentimentGap→ the audiences basically agree; one message works. - Low similarity (<0.5) or large sentiment gap → genuine segmentation; the
actionwill tell you to tailor, anddistinctiveToA/Btells you on which themes. universalThemes→ the drivers to lead with everywhere.polarizingThemes→ the per-segment hooks (e.g. "Compliance" only matters to Enterprise IT).- High
abstentionRatein one audience → you're asking past that population's role; rephrase or pick a more appropriate audience.
From comparison to action
# 1. compare across segments
POST /api/v1/audiences/compare { "question":"...", "audienceIds":["a","b","c"], "respondentsPerAudience":5 }
# 2. for a sharp divergence, drill in with a focus group on the audience that differs
POST /api/v1/tools/focus_group { "audienceId":"aud_enterprise_it", "topic":"...", "stimulus":"..." }
# 3. message-test the tailored variants
POST /api/v1/tools/compare_messages { "audienceId":"aud_enterprise_it", "variants":[...] }Compare audiences vs. compare studies
This endpoint asks a fresh question live across audiences and computes a qualitative contrast. If instead you've already run studies and want to meta-analyze one metric across them (pooled estimate, heterogeneity, between-study significance), use compare_studies — see Mining insights and the API explorer.