This guide uses the API client from the Guides index. Copy it to your project first.
What You’ll Build
A competitive analysis showing:- Competitor ranking: Share of Voice comparison
- Gap analysis: Where you’re winning and losing
- Trend tracking: Monitor competitor momentum
Get Competitive Landscape
async function getCompetitiveLandscape(client, brandId, period = 30) {
const data = await client.getCompetitors(brandId, {
period,
limit: 20,
sort: 'shareOfVoice',
order: 'desc',
});
const yourBrand = data.competitors.find(c => c.relationship === 'SELF');
const competitors = data.competitors.filter(c => c.relationship !== 'SELF');
if (!yourBrand) {
throw new Error('Your brand not found in competitor data');
}
return {
yourPosition: {
name: yourBrand.name,
shareOfVoice: yourBrand.shareOfVoice,
mentions: yourBrand.totalMentions,
avgPosition: yourBrand.avgPosition,
sentiment: yourBrand.avgSentiment,
rank: data.competitors.findIndex(c => c.id === yourBrand.id) + 1,
},
competitors: competitors.map(c => ({
id: c.id,
name: c.name,
shareOfVoice: c.shareOfVoice,
mentions: c.totalMentions,
avgPosition: c.avgPosition,
sentiment: c.avgSentiment,
gap: (c.shareOfVoice - yourBrand.shareOfVoice).toFixed(2),
status: c.shareOfVoice > yourBrand.shareOfVoice ? 'ahead' : 'behind',
})),
summary: {
totalCompetitors: competitors.length,
competitorsAhead: competitors.filter(c => c.shareOfVoice > yourBrand.shareOfVoice).length,
competitorsBehind: competitors.filter(c => c.shareOfVoice < yourBrand.shareOfVoice).length,
avgGap: (competitors.reduce((sum, c) => sum + c.shareOfVoice, 0) / competitors.length - yourBrand.shareOfVoice).toFixed(2),
},
};
}
from typing import List, Optional
def get_competitive_landscape(client, brand_id: str, period: int = 30) -> dict:
"""Analyze competitive positioning and gaps."""
data = client.get_competitors(brand_id, period=period, limit=20, sort='shareOfVoice')
your_brand = next((c for c in data['competitors'] if c['relationship'] == 'SELF'), None)
competitors = [c for c in data['competitors'] if c['relationship'] != 'SELF']
if not your_brand:
raise ValueError('Your brand not found in competitor data')
your_sov = your_brand['shareOfVoice']
return {
'your_position': {
'name': your_brand['name'],
'share_of_voice': your_sov,
'mentions': your_brand['totalMentions'],
'avg_position': your_brand['avgPosition'],
'sentiment': your_brand['avgSentiment'],
'rank': next(i for i, c in enumerate(data['competitors']) if c['id'] == your_brand['id']) + 1,
},
'competitors': [
{
'id': c['id'],
'name': c['name'],
'share_of_voice': c['shareOfVoice'],
'mentions': c['totalMentions'],
'avg_position': c['avgPosition'],
'sentiment': c['avgSentiment'],
'gap': round(c['shareOfVoice'] - your_sov, 2),
'status': 'ahead' if c['shareOfVoice'] > your_sov else 'behind',
}
for c in competitors
],
'summary': {
'total_competitors': len(competitors),
'competitors_ahead': sum(1 for c in competitors if c['shareOfVoice'] > your_sov),
'competitors_behind': sum(1 for c in competitors if c['shareOfVoice'] < your_sov),
'avg_gap': round(sum(c['shareOfVoice'] for c in competitors) / len(competitors) - your_sov, 2) if competitors else 0,
},
}
Track Competitor Evolution
Monitor how a specific competitor’s metrics change over time.async function trackCompetitorEvolution(client, brandId, competitorId, period = 30) {
const evolution = await client.getCompetitorEvolution(brandId, competitorId, { period });
const dataPoints = evolution.evolution;
if (dataPoints.length < 2) {
return { trend: 'insufficient_data', dataPoints };
}
const first = dataPoints[0];
const last = dataPoints[dataPoints.length - 1];
const sovChange = last.shareOfVoice - first.shareOfVoice;
const mentionChange = last.mentions - first.mentions;
return {
competitor: evolution.competitor,
period: { start: first.date, end: last.date },
metrics: {
sovStart: first.shareOfVoice,
sovEnd: last.shareOfVoice,
sovChange: sovChange.toFixed(2),
sovTrend: sovChange > 0.5 ? 'growing' : sovChange < -0.5 ? 'declining' : 'stable',
mentionStart: first.mentions,
mentionEnd: last.mentions,
mentionChange,
},
dataPoints,
};
}
def track_competitor_evolution(client, brand_id: str, competitor_id: str, period: int = 30) -> dict:
"""Track competitor metrics over time."""
evolution = client.get_competitor_evolution(brand_id, competitor_id, period=period)
data_points = evolution['evolution']
if len(data_points) < 2:
return {'trend': 'insufficient_data', 'data_points': data_points}
first = data_points[0]
last = data_points[-1]
sov_change = last['shareOfVoice'] - first['shareOfVoice']
mention_change = last['mentions'] - first['mentions']
def get_trend(change: float) -> str:
if change > 0.5:
return 'growing'
if change < -0.5:
return 'declining'
return 'stable'
return {
'competitor': evolution['competitor'],
'period': {'start': first['date'], 'end': last['date']},
'metrics': {
'sov_start': first['shareOfVoice'],
'sov_end': last['shareOfVoice'],
'sov_change': round(sov_change, 2),
'sov_trend': get_trend(sov_change),
'mention_start': first['mentions'],
'mention_end': last['mentions'],
'mention_change': mention_change,
},
'data_points': data_points,
}
Usage
const client = new QwairyClient(process.env.QWAIRY_API_TOKEN);
// Get landscape
const landscape = await getCompetitiveLandscape(client, 'your-brand-id', 30);
console.log(`\nYour Position: #${landscape.yourPosition.rank}`);
console.log(`Share of Voice: ${landscape.yourPosition.shareOfVoice}%`);
console.log(`\nCompetitors ahead: ${landscape.summary.competitorsAhead}`);
console.log(`Competitors behind: ${landscape.summary.competitorsBehind}`);
console.log('\nCompetitor Ranking:');
console.log('─'.repeat(60));
for (const c of landscape.competitors.slice(0, 5)) {
const indicator = c.status === 'ahead' ? '↑' : '↓';
console.log(`${c.name.padEnd(25)} ${c.shareOfVoice.toFixed(1)}% SOV ${indicator} ${Math.abs(c.gap)}% gap`);
}
// Track top competitor
const topCompetitor = landscape.competitors[0];
if (topCompetitor) {
const evolution = await trackCompetitorEvolution(client, 'your-brand-id', topCompetitor.id, 30);
console.log(`\n${topCompetitor.name} trend: ${evolution.metrics.sovTrend} (${evolution.metrics.sovChange}%)`);
}
client = QwairyClient()
# Get landscape
landscape = get_competitive_landscape(client, 'your-brand-id', period=30)
print(f"\nYour Position: #{landscape['your_position']['rank']}")
print(f"Share of Voice: {landscape['your_position']['share_of_voice']}%")
print(f"\nCompetitors ahead: {landscape['summary']['competitors_ahead']}")
print(f"Competitors behind: {landscape['summary']['competitors_behind']}")
print('\nCompetitor Ranking:')
print('─' * 60)
for c in landscape['competitors'][:5]:
indicator = '↑' if c['status'] == 'ahead' else '↓'
print(f"{c['name']:<25} {c['share_of_voice']:.1f}% SOV {indicator} {abs(c['gap'])}% gap")
# Track top competitor
if landscape['competitors']:
top = landscape['competitors'][0]
evolution = track_competitor_evolution(client, 'your-brand-id', top['id'], period=30)
print(f"\n{top['name']} trend: {evolution['metrics']['sov_trend']} ({evolution['metrics']['sov_change']}%)")
Example Output
Console:Your Position: #3
Share of Voice: 8.13%
Competitors ahead: 2
Competitors behind: 7
Competitor Ranking:
────────────────────────────────────────────────────────────
Competitor A 12.5% SOV ↑ 4.37% gap
Competitor B 9.8% SOV ↑ 1.67% gap
Competitor C 6.2% SOV ↓ 1.93% gap
Competitor D 5.1% SOV ↓ 3.03% gap
Competitor E 4.8% SOV ↓ 3.33% gap
Competitor A trend: stable (+0.3%)
{
"yourPosition": {
"name": "My Brand",
"shareOfVoice": 8.13,
"mentions": 104,
"avgPosition": 2.1,
"sentiment": 78.1,
"rank": 3
},
"competitors": [
{
"id": "comp1",
"name": "Competitor A",
"shareOfVoice": 12.5,
"mentions": 156,
"avgPosition": 1.8,
"sentiment": 72.3,
"gap": "4.37",
"status": "ahead"
}
],
"summary": {
"totalCompetitors": 9,
"competitorsAhead": 2,
"competitorsBehind": 7,
"avgGap": "-1.23"
}
}
Key Metrics
| Metric | Description | Action |
|---|---|---|
gap | Difference in Share of Voice | Positive = competitor ahead |
avgPosition | Average rank in AI responses | Lower = better (1 is first) |
status | Relative position | ahead or behind your brand |
sovTrend | Direction over time | growing, declining, stable |
Next Steps
- Build a custom dashboard to visualize these insights
- Set up weekly reports to track changes
- Export data for deeper analysis in BI tools

