AI Search Visibility & Analytics — measure how your brand performs inside AI-generated answers across Perplexity, ChatGPT, Gemini, and Claude, and see which AI crawlers are actually visiting your site.
BestSearch is an AI visibility intelligence platform. As people research using Perplexity, ChatGPT, and Gemini instead of Google, brands need a layer beyond traditional SEO to answer:
- Are we appearing in AI answers?
- How often are we recommended, and where do we rank versus competitors?
- Which sources does AI cite in our category — and are we in them?
- Is our AI visibility improving over time?
- Which AI crawlers (training, search-indexing, live user queries) are actually hitting our site?
Search is shifting: Google → AI answers. Most brands have no visibility into:
- Whether they're mentioned in AI responses at all
- Where they rank against competitors, per engine
- Which domains AI models trust and cite
- Whether that visibility is trending up or down
- Whether AI bots are even crawling their site, and what kind
BestSearch solves this with scans, scoring, citation tracking, and a dashboard built around each of those questions.
| Step | What happens |
|---|---|
| 1. Input | You enter a query — e.g. "best veg restaurant in Punjabi Bagh Delhi". |
| 2. Expand | BestSearch generates up to 15 intent-based query variations (commercial, comparison, local, review, list). |
| 3. Scan | Each variation runs across the selected AI engines — Perplexity, GPT-4o mini, ChatGPT (search), Gemini, Claude. |
| 4. Extract | Entities are parsed from each response (JSON-first, falling back to numbered/bullet lists). |
| 5. Score | We compute per-engine leaderboards plus a Global score and a Consensus score across engines, then persist and display them. |
Each entity gets a rank weight (rank 1 = 10 pts, rank 10 = 1 pt).
- Global = Σ rank_weight + per-engine bonus (Perplexity +2, OpenAI/Gemini +1).
- Consensus = mentions × engines_count × avg(rank_weight) — rewards entities that every engine agrees on.
- Volatility = top-3 rank variance across the last 5 scans of the same query →
Stable | Moderate | High volatility.
Full walkthrough, including how each AI engine actually decides who to recommend, lives at /docs once the app is running.
The dashboard is organized around a persistent sidebar:
| Section | What it shows |
|---|---|
| Home | Aggregate stats — visibility score, scan count, pending actions, plan. |
| Overview | Run a scan; global / per-engine / consensus leaderboards; trend + engine-comparison charts; keyword intelligence; "why am I missing?" diagnosis; citation gaps for the current scan. |
| Actions | Every GEO recommendation you've tracked across your scan history — filterable by brand, mark done. |
| Sources | Domains AI models cite in your category, aggregated across your entire scan history (not just the last scan). |
| Web Analytics | Privacy-first human visitor analytics for a site you embed the tracking snippet on. |
| LLM Analytics | Which AI bots — training, search-indexing, or live user-action — are crawling your site, with a full bot reference table. |
| Reddit Marketing | Search Reddit for threads relevant to your brand, score which subreddits are worth engaging with, and draft (never auto-post) suggested replies. Read-only by design — see md/reddit-marketing/engine.md. |
Web/LLM Analytics are powered by a small first-party script, not a third-party pixel:
<script async src="https://your-domain.com/track.js" data-website-id="YOUR_WEBSITE_ID"></script>Get your website_id from the Web Analytics or LLM Analytics tab (GET /api/sites auto-provisions one per user). Every pageview posts to POST /api/collect, which classifies the visitor's User-Agent server-side — human traffic feeds Web Analytics, known AI/SEO crawlers (GPTBot, ClaudeBot, PerplexityBot, Googlebot, etc. — see lib/botDetection.ts) feed LLM Analytics.
- Next.js 16 (App Router, Turbopack)
- Supabase (Auth + Postgres + RLS)
- AI engines: Perplexity, OpenAI (
gpt-4o-mini, pluschatgpt_searchvia the Responses API web-search tool), Gemini (gemini-1.5-flash), Claude (claude-haiku-4-5) - Tailwind CSS v4
- Stripe (subscriptions + webhook)
- Recharts (analytics)
Migrations live in supabase/migrations/, run in order via the Supabase SQL editor.
| Migration | Adds |
|---|---|
001_scans_table.sql |
Initial brand/category schema (superseded by 002). |
002_query_leaderboard_schema.sql |
Current core schema: scans (query-keyed) + scan_results (per-engine leaderboard + raw responses). |
003_subscriptions.sql |
subscriptions — Stripe plan state. |
004_user_settings.sql |
user_settings — Slack webhook, email digest opt-in, target brand. |
005_tracked_actions.sql |
tracked_actions — GEO recommendations marked done, powers the Actions tab. |
006_scan_citations.sql |
Adds citations to scan_results so Sources can be rebuilt from history. |
007_web_llm_analytics.sql |
sites + site_events — tracking-snippet ingestion for Web/LLM Analytics. |
Key tables:
| Table | Columns |
|---|---|
scans |
id, user_id, query, created_at, volatility_index, stability_label |
scan_results |
id, scan_id, engine, leaderboard (jsonb), raw_responses (jsonb), citations (jsonb), top_score, total_entities |
tracked_actions |
id, user_id, target_brand, engine, action_text, marked_done, done_at, rank_before, rank_after |
sites |
id, user_id, name, website_id, domain |
site_events |
id, site_id, session_id, path, referrer, country, device, is_bot, bot_name, bot_category, utm_* |
subscriptions |
user_id, plan, status, stripe_customer_id, stripe_subscription_id, current_period_end |
| Route | Purpose |
|---|---|
POST /api/scan |
Run a scan. Enforces free-tier quota (3 scans/month) for signed-in users when Stripe is configured. |
GET /api/scan/[id] |
Rebuild and return a stored scan's full ScanResponse, including citation intelligence. |
GET /api/scans |
List the current user's scans (?query= filter, ?limit=). |
GET/POST/PATCH/DELETE /api/actions[/id] |
Tracked GEO recommendations. |
GET /api/sources |
Domains cited across the user's scan history. |
GET /api/sites · PUT /api/sites |
Get-or-create / rename the user's tracked site. |
POST /api/collect |
Public tracking-snippet ingestion endpoint (service-role, CORS-open). |
GET /api/analytics/web |
Aggregated human-visitor analytics for the user's site. |
GET /api/analytics/llm |
Aggregated bot-traffic analytics for the user's site. |
GET /api/subscription |
Current user's plan + remaining free quota. |
POST /api/stripe/checkout |
Create a Checkout Session. Body: { plan: "starter" | "pro" }. |
POST /api/stripe/webhook |
Stripe events → upsert subscriptions via service-role client. |
GET /auth/callback |
Supabase OAuth/email-confirm callback. |
- Free — 3 scans / month, single or multi-engine.
- Starter / Pro — unlimited scans, multi-engine. Wired via Stripe Checkout.
- Enterprise — contact sales (mailto link).
Quota enforcement and subscription state degrade gracefully when Stripe / Supabase env vars are missing — the app still runs for local development, and the dashboard does not currently require login.
See .env.example. Copy to .env.local and fill in.
# Required
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
# Server-only — bypasses RLS for the Stripe webhook and the /api/collect
# tracking-snippet ingestion endpoint
SUPABASE_SERVICE_ROLE_KEY=
# At least one engine
PERPLEXITY_API_KEY=
OPENAI_API_KEY=
GEMINI_API_KEY=
ANTHROPIC_API_KEY=
# Optional: Stripe billing
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_STARTER=
STRIPE_PRICE_PRO=
NEXT_PUBLIC_APP_URL=http://localhost:3000npm install
cp .env.example .env.local # fill in values
npm run dev- Create a Supabase project, copy the URL + anon key into
.env.local. - Run the SQL files in
supabase/migrations/in order, in the Supabase SQL editor. - In Supabase Auth → URL Configuration, add
http://localhost:3000/auth/callbackto Redirect URLs. - (Optional) Create products in Stripe, set
STRIPE_PRICE_STARTER/STRIPE_PRICE_PRO, and point a webhook at/api/stripe/webhook(stripe listen --forward-to localhost:3000/api/stripe/webhook). - Open http://localhost:3000 → Get Started →
/dashboard. - Read
/docsfor the full engine-by-engine breakdown.
| Area | Status |
|---|---|
| Multi-engine scanner (Perplexity, OpenAI, ChatGPT search, Gemini, Claude) | ✅ Done |
| Query expansion + entity extraction | ✅ Done |
| Global + Consensus scoring, volatility | ✅ Done |
| Citation intelligence, persisted per scan | ✅ Done |
| Dashboard: Home, Overview, Actions, Sources | ✅ Done |
| Web Analytics + LLM Analytics (tracking snippet, bot classification) | ✅ Done |
/docs — living product + engine documentation |
✅ Done |
| Stripe checkout + webhook + quota enforcement | ✅ Done |
| Reddit Marketing (search, community fit, reply drafts) | ✅ Done — v1 |
| Authority-weighted scoring, sentiment, competitor tracking | 🔜 Roadmap |
| Weekly reports, Slack alerts, CSV export | ✅ Shipped (Slack alerts, CSV export, weekly digest) |
| Phase | Scope |
|---|---|
| 1 – MVP | Auth, multi-engine scan, scoring, dashboard, Stripe. |
| 2 – Intelligence (current) | Citation persistence, Actions/Sources history, Web + LLM analytics, docs. |
| 3 – Growth | Reddit reply persistence + rate limiting, authority-weighted scoring, sentiment, competitor tracking. |
BestSearch aims to become the "Ahrefs for AI Search": the default analytics layer for measuring and optimizing brand visibility in AI answers.
BestSearch — AI Influence Intelligence for modern marketers.