DEV Community

API Serpent
API Serpent

Posted on

Building an AI Citation Tracker in Python: Check if ChatGPT, Claude, Gemini, and Perplexity Recommend Your Brand

Building an AI Citation Tracker in Python

The overlap between Google top-10 rankings and
AI Overview citations dropped from 76% to 38%
in one year (Ahrefs, 2026).

This means ranking well on Google no longer
guarantees appearing in AI answers above your
result. You need to track both signals separately.

This tutorial shows how to build a tracker that
checks all four major AI engines simultaneously.

Why four engines?

Each uses a completely different search index:

Engine Index used Daily queries
ChatGPT Bing ~2.5B prompts
Claude Brave Search Enterprise-heavy
Gemini Google ~2B+
Perplexity Proprietary 780M/month

Empirical overlap between citations: below 20%.

Monitoring one engine captures roughly 1/5th
of the full AI recommendation landscape.

Setup

bash
pip install requests python-dotenv
Enter fullscreen mode Exit fullscreen mode
python
# .env
SERPENT_API_KEY=your_key_here
Enter fullscreen mode Exit fullscreen mode

The tracker

python
import os
import requests
from dataclasses import dataclass
from typing import Optional
from dotenv import load_dotenv

load_dotenv()

@dataclass
class Citation:
    engine: str
    url: str
    position: int
    cited_text: Optional[str]

@dataclass  
class VisibilityReport:
    keyword: str
    citations: list[Citation]
    scores: dict[str, int]
    combined_score: int

class AICitationTracker:
    BASE_URL = "https://apiserpent.com/api"
    ENGINES = ["chatgpt", "claude", "gemini", "perplexity"]

    def __init__(self):
        self.api_key = os.getenv("SERPENT_API_KEY")
        self.session = requests.Session()
        self.session.headers.update({"X-API-Key": self.api_key})

    def check_visibility(
        self, 
        keyword: str,
        engines: list = None
    ) -> VisibilityReport:
        """
        Check brand citations across AI engines.
        Returns structured visibility data.
        """
        engines = engines or self.ENGINES

        response = self.session.get(
            f"{self.BASE_URL}/ai/rank",
            params={
                "keyword": keyword,
                "engines": ",".join(engines)
            },
            timeout=30
        )
        response.raise_for_status()
        data = response.json()

        citations = []
        scores = {}

        for engine, result in data["results"].items():
            scores[engine] = result.get("visibility_score", 0)
            for cite in result.get("citations", []):
                citations.append(Citation(
                    engine=engine,
                    url=cite["url"],
                    position=cite["position"],
                    cited_text=cite.get("cited_text")
                ))

        return VisibilityReport(
            keyword=keyword,
            citations=citations,
            scores=scores,
            combined_score=data.get("combined_visibility_score", 0)
        )

    def track_keywords(self, keywords: list[str]) -> dict:
        """Track multiple keywords and return summary."""
        results = {}
        for kw in keywords:
            report = self.check_visibility(kw)
            results[kw] = {
                "combined_score": report.combined_score,
                "scores_by_engine": report.scores,
                "total_citations": len(report.citations),
                "engines_citing": list({c.engine for c in report.citations})
            }
        return results

# Usage
tracker = AICitationTracker()

# Single keyword check
report = tracker.check_visibility("best SERP API for developers")

print(f"Combined visibility score: {report.combined_score}/100\n")
for engine, score in report.scores.items():
    print(f"{engine}: {score}/100")
    engine_cites = [c for c in report.citations if c.engine == engine]
    for cite in engine_cites:
        print(f"  → pos {cite.position}: {cite.url}")

# Multiple keywords
keywords = [
    "best rank tracking API",
    "google SERP API Python",
    "cheapest search API"
]
summary = tracker.track_keywords(keywords)
for kw, data in summary.items():
    print(f"\n{kw}")
    print(f"  Score: {data['combined_score']}/100")
    print(f"  Cited by: {', '.join(data['engines_citing']) or 'none'}")
Enter fullscreen mode Exit fullscreen mode

Understanding the output

The visibility_score (0-100) per engine is a
composite of:

  • Citation presence (yes/no)
  • Position of citation vs competitors
  • Frequency across similar queries
  • Source authority of the cited URL

Track this weekly. A score dropping 10+ points
in a week means a competitor page overtook yours
in that engine's citations.

10 free searches to start

API key at apiserpent.com
no card required. Pay-as-you-go from $1.22/1K
(Gemini) to $11.11/1K (Perplexity) at default tier.

Drop questions below — happy to discuss
the architecture.

Top comments (0)