GEO Optimizer
v4.10.1
☰
Audit
Compare
Analyze
Docs
More
Research
Roadmap
Manifesto
GitHub
Analyze Competitive Narratives
See how AI search engines describe your competitors vs your brand
Competitor URLs (comma-separated)
Enter up to 5 competitors
Analyze
Analyzing... this may take up to 90 seconds.
Executive Summary
Competitor Narratives
Competitive Gaps
Category
Competitor
Gap Type
Missing From Your Content
Suggestion
fetch('/api/stats').then(function(r){return r.json()}).then(function(d){var ns=document.getElementById('nav-stars');if(ns&&d.github_stars)ns.textContent='★ '+d.github_stars}).catch(function(){}); const $=id=>document.getElementById(id); const $in=$tag=>$('input[type="text"]')&&$.prototype.addEventListener.call(document.querySelector($tag),arguments[1],arguments[2]); $('btn').addEventListener('click',run); $('competitorUrls').addEventListener('keypress',e=>{if(e.key==='Enter')run()}); function isValidUrl(str){ if(/^https?:\/\/.+\..+/.test(str))return true; if(/^[^\s]+\.[a-z]{2,}(\/.*)?$/i.test(str))return true; return false; } async function run(){ const targetUrl=$('targetUrl').value.trim(); const competitorText=$('competitorUrls').value.trim(); if(!targetUrl)return; if(!competitorText)return; if(!isValidUrl(targetUrl)){$('error').textContent='Please enter a valid URL for your website';$('error').style.display='block';return} if(!/^https?:\/\//i.test(targetUrl)){$('targetUrl').value='https://'+targetUrl;targetUrl=$('targetUrl').value} const competitors=competitorText.split(',').map(u=>u.trim()).filter(u=>u&&isValidUrl(u)); if(competitors.length===0){$('error').textContent='Please enter at least one valid competitor URL';$('error').style.display='block';return} $('btn').disabled=true;$('spinner').classList.add('active');$('error').style.display='none';$('results').style.display='none'; try{ const response=await fetch('/api/analyze-competitors',{ method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({target_url:targetUrl,competitor_urls:competitors}) }); if(!response.ok){throw new Error('Analysis failed: '+response.statusText);} const data=await response.json(); render(data); $('results').style.display='block'; if(data.summary){$('summaryText').textContent=data.summary;} }catch(e){$('error').textContent=e.message;$('error').style.display='block'} finally{$('btn').disabled=false;$('spinner').classList.remove('active')} } function render(data){ const competitorList=$('competitorList'); competitorList.textContent=''; data.competitors.forEach(function(c){ const card=document.createElement('div'); card.className='narrative-card'; card.innerHTML='
'+ '
'+(c.brand_name||c.url)+'
Score '+(c.score||'N/A')+'
'+ '
'+ '
Adjectives:
'+ c.dominant_adjectives.map(a=>'
'+a+'
').join('')+ '
'+ '
Frames:
'+ c.key_frames.map(f=>'
'+f+'
').join('')+ '
'+ (c.positioning?'
\"'+c.positioning+'\"
':'')+ (c.credibility_signals.length?'
Credibility Signals
'+ c.credibility_signals.join('
')+'
':'')+ (c.content_gaps.length?'
Content Gaps
'+ c.content_gaps.map(g=>'
'+g+'
').join('')+'
':''); competitorList.appendChild(card); }); // Render gaps table if available if(data.competitive_gaps&&data.competitive_gaps.length>0){ const gapTableBody=$('gapTableBody'); gapTableBody.textContent=''; $('gapSection').style.display='block'; data.competitive_gaps.forEach(function(g){ const tr=document.createElement('tr'); tr.innerHTML='
'+g.category+'
'+ '
'+new URL(g.competitor_url).hostname+'
'+ '
'+g.gap_type+'
'+ '
'+g.target_missing+'
'+ '
'+g.suggested_content+'
'; gapTableBody.appendChild(tr); }); } } document.getElementById('nav-ham').addEventListener('click',function(){ document.querySelector('.nav-links').classList.toggle('open'); this.textContent=this.textContent==='☰'?'✕':'☰'; }); document.querySelectorAll('.nav-dropdown-toggle').forEach(function(btn){ btn.addEventListener('click',function(e){ e.stopPropagation(); var dd=this.closest('.nav-dropdown'); dd.classList.toggle('open'); this.setAttribute('aria-expanded',dd.classList.contains('open')); }); }); document.addEventListener('click',function(){ document.querySelectorAll('.nav-dropdown.open').forEach(function(dd){ dd.classList.remove('open'); dd.querySelector('.nav-dropdown-toggle').setAttribute('aria-expanded','false'); }); });