GEO Optimizer

Audit your website's visibility to ChatGPT, Perplexity, Claude & Gemini

stars 📦 /mo 🔍 audits
Connecting...
0 / 100
Trend
Baseline snapshot saved for this URL.

    Why this tool exists

    AI search is already deciding which websites get cited and which don't. The signals are documented, the research is published — but most of the web has no idea. GEO Optimizer exists to make AI visibility auditable, transparent, and accessible to every developer, not just teams with enterprise budgets.

    Auditability Scientific Foundation Open Source Precision
    Read the full manifesto →
    /* ─── Constants ─── */ const BANDS = {excellent:'#22c55e',good:'#06b6d4',foundation:'#eab308',critical:'#ef4444'}; const CATEGORIES = [ {key:'robots',label:'Robots.txt',max:18}, {key:'llms',label:'llms.txt',max:18}, {key:'schema',label:'Schema JSON-LD',max:16}, {key:'meta',label:'Meta Tags',max:14}, {key:'content',label:'Content',max:12}, {key:'signals',label:'Signals',max:6}, {key:'ai_discovery',label:'AI Discovery',max:6}, {key:'brand_entity',label:'Brand & Entity',max:10}, ]; const SPIN_MSGS = ['Checking robots.txt...','Analyzing llms.txt...','Parsing schema...','Evaluating content...','Scoring citability...','Compiling results...']; /* ─── Version + Stats ─── */ (async function init(){ try{ const h = await fetch('/health'); if(h.ok){const d=await h.json();var vb=document.getElementById('nav-ver');if(vb)vb.textContent='v'+d.version;} }catch(e){} try{ const s = await fetch('/api/stats'); if(s.ok){ const d=await s.json(); const fmt=n=>n>=1000?(n/1000).toFixed(1)+'k':String(n); document.getElementById('stars').textContent=fmt(d.github_stars); document.getElementById('downloads').textContent=fmt(d.pypi_downloads_month); document.getElementById('audits').textContent=fmt(d.audits_run); var ns=document.getElementById('nav-stars');if(ns&&d.github_stars)ns.textContent='★ '+d.github_stars; } }catch(e){} })(); /* ─── Audit ─── */ const $=id=>document.getElementById(id); let spinInterval; function startSpin(){ $('spinner').classList.add('active'); let i=0; $('spin-text').textContent=SPIN_MSGS[0]; spinInterval=setInterval(()=>{i=(i+1)%SPIN_MSGS.length;$('spin-text').textContent=SPIN_MSGS[i]},2200); } function stopSpin(){$('spinner').classList.remove('active');clearInterval(spinInterval)} function isValidUrl(str){ /* Accetta URL con protocollo, oppure dominio semplice (es. example.com) */ if(/^https?:\/\/.+\..+/.test(str))return true; /* Dominio senza protocollo: almeno un punto, niente spazi */ if(/^[^\s]+\.[a-z]{2,}(\/.*)?$/i.test(str))return true; return false; } async function runAudit(){ const input=$('url-input'); const hint=$('url-hint'); let url=input.value.trim(); if(!url){hint.style.display='none';return} /* Validazione lato client */ if(!isValidUrl(url)){ hint.textContent='Please enter a valid URL (e.g. https://example.com)'; hint.style.display='block'; input.focus(); return; } hint.style.display='none'; /* Aggiungi https:// se manca il protocollo */ if(!/^https?:\/\//i.test(url))url='https://'+url; input.value=url; $('btn').disabled=true;$('error').style.display='none';$('result').style.display='none'; startSpin(); try{ const res=await fetch('/api/audit?url='+encodeURIComponent(url)); const data=await res.json(); if(!res.ok)throw new Error(data.detail||'Audit failed'); renderResult(data,url); }catch(e){ $('error').textContent=e.message;$('error').style.display='block'; }finally{stopSpin();$('btn').disabled=false} } function renderResult(data,url){ const color=BANDS[data.band]||'#888'; const score=data.score||0; /* Gauge */ const circ=314.16; const offset=circ-(score/100*circ); const fill=$('gauge-fill'); fill.style.stroke=color; fill.style.strokeDashoffset=String(offset); $('gauge-score').textContent=score; $('gauge-score').style.fill=color; const tag=$('band-tag'); tag.textContent=data.band?data.band.toUpperCase():'—'; tag.style.background=color+'1a';tag.style.color=color; /* Breakdown */ const bd=$('breakdown');bd.textContent=''; const br=data.score_breakdown||{}; for(const cat of CATEGORIES){ const val=br[cat.key]||0; const pct=Math.min(val/cat.max*100,100); const row=document.createElement('div');row.className='bar-row'; const lbl=document.createElement('span');lbl.className='bar-label';lbl.textContent=cat.label; const track=document.createElement('div');track.className='bar-track'; const barFill=document.createElement('div');barFill.className='bar-fill'; barFill.style.width='0%';barFill.style.background=color; track.appendChild(barFill); const valSpan=document.createElement('span');valSpan.className='bar-val'; valSpan.textContent=val+'/'+cat.max; row.appendChild(lbl);row.appendChild(track);row.appendChild(valSpan); bd.appendChild(row); requestAnimationFrame(()=>{requestAnimationFrame(()=>{barFill.style.width=pct+'%'})}); } /* Citability */ const cit=data.citability; if(cit&&cit.total_score!==undefined){ $('cit-wrap').style.display='flex'; const cs=$('cit-score');cs.textContent=cit.total_score+'/100'; const cc=cit.total_score>=86?BANDS.excellent:cit.total_score>=68?BANDS.good:cit.total_score>=36?BANDS.foundation:BANDS.critical; cs.style.color=cc; $('cit-grade').textContent=cit.grade?cit.grade.toUpperCase():''; $('cit-grade').style.color=cc; }else{$('cit-wrap').style.display='none'} /* Recommendations */ const recs=data.recommendations||[]; $('recs-count').textContent='('+recs.length+')'; const rl=$('recs-list');rl.textContent='';rl.classList.remove('open'); $('recs-toggle').classList.remove('open'); for(const r of recs){const li=document.createElement('li');li.textContent=r;rl.appendChild(li)} /* Trend */ const history=data.history; const trendBox=$('trend-box'); if(history && history.total_snapshots){ trendBox.style.display='block'; const scoreDelta=history.score_delta; const latestBand=(history.latest_band||'critical').toUpperCase(); let summary=`Current baseline: ${history.latest_score}/100 (${latestBand}).`; if(history.total_snapshots >= 2 && scoreDelta !== null && scoreDelta !== undefined){ if(scoreDelta > 0) summary=`Improved from ${history.previous_score} to ${history.latest_score} since the previous snapshot.`; else if(scoreDelta < 0) summary=`Dropped from ${history.previous_score} to ${history.latest_score} since the previous snapshot.`; else summary=`Stable at ${history.latest_score}/100 since the previous snapshot.`; } $('trend-summary').textContent=summary; const deltaNode=$('trend-delta'); if(scoreDelta === null || scoreDelta === undefined){ deltaNode.textContent='BASE'; deltaNode.style.color='var(--text-dim)'; }else{ deltaNode.textContent=(scoreDelta > 0 ? '+' : '') + scoreDelta; deltaNode.style.color=scoreDelta >= 0 ? BANDS.good : BANDS.critical; } const trendList=$('trend-list');trendList.textContent=''; (history.entries||[]).slice(0,5).forEach(item=>{ const row=document.createElement('div');row.className='trend-row'; const date=document.createElement('span');date.className='trend-date';date.textContent=(item.timestamp||'').slice(0,10); const score=document.createElement('span');score.className='trend-score'; const delta=(item.delta === null || item.delta === undefined) ? '—' : ((item.delta > 0 ? '+' : '') + item.delta); score.textContent=`${item.score}/100 · ${delta}`; const bar=document.createElement('div');bar.className='trend-bar'; const fill=document.createElement('span');fill.className='trend-fill'; fill.style.width=Math.max(4, Math.min(100, item.score||0))+'%'; bar.appendChild(fill); row.appendChild(date);row.appendChild(score);row.appendChild(bar); trendList.appendChild(row); }); }else{ trendBox.style.display='none'; } /* Actions */ const acts=$('actions');acts.textContent=''; if(data.report_url){ const a=document.createElement('a');a.className='action-link';a.href=data.report_url; a.target='_blank';a.rel='noopener';a.textContent='HTML Report';acts.appendChild(a); } const pdf=document.createElement('a');pdf.className='action-link'; pdf.href='/api/audit/pdf?url='+encodeURIComponent(url); pdf.textContent='Download PDF';acts.appendChild(pdf); $('result').style.display='block'; } /* ─── Event listeners ─── */ $('btn').addEventListener('click',runAudit); $('url-input').addEventListener('keypress',e=>{if(e.key==='Enter')runAudit()}); $('recs-toggle').addEventListener('click',function(){ this.classList.toggle('open');$('recs-list').classList.toggle('open'); }); $('copy-btn').addEventListener('click',function(){ navigator.clipboard.writeText('pip install geo-optimizer-skill').then(()=>{this.textContent='Done!'}); setTimeout(()=>{this.textContent='\u{1F4CB}'},1500); }); 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'); }); });