Skip to main content
Analyze your competitive landscape in AI search results with gap analysis and trend tracking.
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),
    },
  };
}

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,
  };
}

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}%)`);
}

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%)
JSON:
{
  "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

MetricDescriptionAction
gapDifference in Share of VoicePositive = competitor ahead
avgPositionAverage rank in AI responsesLower = better (1 is first)
statusRelative positionahead or behind your brand
sovTrendDirection over timegrowing, declining, stable

Next Steps