Skip to content

Repository files navigation

BestSearch

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.


What is BestSearch?

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?

The Problem

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.


How a Scan Works

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.

Scoring

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.


Dashboard

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 — Tracking Snippet

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.


Tech Stack

  • Next.js 16 (App Router, Turbopack)
  • Supabase (Auth + Postgres + RLS)
  • AI engines: Perplexity, OpenAI (gpt-4o-mini, plus chatgpt_search via the Responses API web-search tool), Gemini (gemini-1.5-flash), Claude (claude-haiku-4-5)
  • Tailwind CSS v4
  • Stripe (subscriptions + webhook)
  • Recharts (analytics)

Database

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

API Routes

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.

Plans & Quotas

  • 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.


Environment Variables

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:3000

Getting Started

npm install
cp .env.example .env.local   # fill in values
npm run dev
  1. Create a Supabase project, copy the URL + anon key into .env.local.
  2. Run the SQL files in supabase/migrations/ in order, in the Supabase SQL editor.
  3. In Supabase Auth → URL Configuration, add http://localhost:3000/auth/callback to Redirect URLs.
  4. (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).
  5. Open http://localhost:3000Get Started/dashboard.
  6. Read /docs for the full engine-by-engine breakdown.

Status

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)

Roadmap

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.

Vision

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.

Releases

Packages

Contributors

Languages