Your customers are asking ChatGPT about products like yours right now. They're asking for recommendations, comparisons, and alternatives. If your brand isn't showing up — or worse, your competitor is — you're losing pipeline before a prospect ever visits your website.

But here's the problem: OpenAI doesn't give you a dashboard of who's mentioning your brand. There's no "brand mention" endpoint. No native analytics. No alert when ChatGPT recommends your competitor instead of you.

That doesn't mean you're powerless. With the OpenAI Responses API + web search tool, you can programmatically query ChatGPT, extract brand mentions, parse citations, and even gauge sentiment — all with a Python script under 100 lines.

In this tutorial, I'll walk you through exactly how to build your own ChatGPT brand mention tracker. We'll write clean, production-ready Python code, test it live, and discuss the trade-offs of the DIY approach versus using a dedicated tool like RankBits.

📖 What you'll learn:
  • How the OpenAI Responses API web search tool works
  • How to extract brand mentions, citations, and sentiment from ChatGPT responses
  • How to run multiple queries in batch and generate a structured report
  • The limitations of DIY tracking (and when to use a dedicated platform)

How ChatGPT Brand Mentions Work

When you ask ChatGPT a question like "What's the best AI visibility tool?", the model can either:

  1. Answer from training data — knowledge frozen at its cutoff date. This is where your brand reputation and SEO work over the years pays off.
  2. Search the web in real-time — pulling fresh results, citing sources, and forming an answer based on what's currently ranking and being discussed.

The second path is the one that matters for tracking. With the Responses API + web search tool, you can force ChatGPT to search the web and observe exactly what it finds — and whether your brand appears.

A mention isn't just a name-drop. It can be:

  • A recommendation: "For AI visibility tracking, tools like RankBits..."
  • A comparison: "RankBits offers X, while Semrush focuses on Y..."
  • A citation: "According to RankBits' research..." with a linked URL

Each type carries different weight. A recommendation is gold. A citation is trust. A passing mention is awareness. Your tracking should distinguish between them.

Prerequisites

You'll need:

  • Python 3.9+ installed
  • An OpenAI API key with access to GPT-5.6 models (get one here)
  • The openai Python package (pip install openai)

Set your API key as an environment variable:

export OPENAI_API_KEY="sk-your-key-here"
💰 Pricing breakdown: GPT-5.6 Luna costs $1.00/1M input tokens and $6.00/1M output tokens. The web search tool adds $10 per 1,000 calls plus search content tokens at the model's input rate. A typical brand-mention query uses ~2K input tokens + ~8K search content tokens + ~1K output tokens, which comes to roughly $0.03–$0.05 per query. Very affordable for occasional checks — but costs add up when you scale to multiple competitors and daily monitoring.

The Code: Building a Brand Mention Tracker

Here's the full script. It queries ChatGPT with web search enabled, extracts mentions and citations, applies a simple sentiment heuristic, and prints a structured report. Save it as brand_tracker.py.

"""
Brand Mention Tracker — Query ChatGPT via OpenAI Responses API with web search
and extract structured brand mentions, sentiment, and citations.
"""
import os
import json
import re
from dataclasses import dataclass, field, asdict
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])


@dataclass
class Citation:
    url: str
    title: str = ""


@dataclass
class MentionResult:
    query: str
    mentioned: bool
    sentiment: str  # "positive", "negative", or "neutral"
    citations: list[Citation] = field(default_factory=list)
    response_snippet: str = ""


# ── Sentiment helpers ──────────────────────────────────────────

POSITIVE_WORDS = [
    "best", "great", "excellent", "top", "leading", "powerful",
    "recommended", "good", "solid", "impressive", "standout",
    "strong", "reliable", "comprehensive", "innovative",
]

NEGATIVE_WORDS = [
    "bad", "poor", "worst", "avoid", "terrible", "limited",
    "expensive", "disappointing", "weak", "unreliable", "outdated",
    "lacking", "overpriced", "slow",
]


def _detect_sentiment(text: str, brand: str) -> str:
    """Crude lexicon-based sentiment around the first brand mention."""
    idx = text.lower().find(brand.lower())
    if idx == -1:
        return "neutral"

    window = text[max(0, idx - 300):idx + 300].lower()
    pos = sum(1 for w in POSITIVE_WORDS if re.search(rf"\b{w}\b", window))
    neg = sum(1 for w in NEGATIVE_WORDS if re.search(rf"\b{w}\b", window))

    if pos > neg:
        return "positive"
    elif neg > pos:
        return "negative"
    return "neutral"


# ── Core function ──────────────────────────────────────────────

def check_brand_mention(
    brand: str,
    query: str,
    model: str = "gpt-5.6-luna",
    search_context_size: str = "medium",
) -> MentionResult:
    """Ask ChatGPT (with live web search) and extract brand mentions."""

    resp = client.responses.create(
        model=model,
        tools=[{
            "type": "web_search",
            "search_context_size": search_context_size,
        }],
        input=query,
    )

    output_text = resp.output_text

    # ── extract citations from annotations ──
    citations: list[Citation] = []
    for item in resp.output:
        if item.type == "message":
            for block in item.content:
                for ann in getattr(block, "annotations", []) or []:
                    if hasattr(ann, "url"):
                        citations.append(
                            Citation(
                                url=ann.url,
                                title=getattr(ann, "title", ""),
                            )
                        )

    # Deduplicate by URL
    seen = set()
    unique_citations = []
    for c in citations:
        if c.url not in seen:
            seen.add(c.url)
            unique_citations.append(c)

    mentioned = brand.lower() in output_text.lower()
    sentiment = _detect_sentiment(output_text, brand) if mentioned else "neutral"

    return MentionResult(
        query=query,
        mentioned=mentioned,
        sentiment=sentiment,
        citations=unique_citations,
        response_snippet=(
            output_text[:500] + ("..." if len(output_text) > 500 else "")
        ),
    )


# ── Batch runner ───────────────────────────────────────────────

def run_queries(
    brand: str,
    queries: list[str],
    model: str = "gpt-5.6-luna",
) -> list[MentionResult]:
    """Run multiple queries and return structured results."""
    return [check_brand_mention(brand, q, model=model) for q in queries]


# ── Report ─────────────────────────────────────────────────────

def print_report(brand: str, results: list[MentionResult]) -> None:
    print(f"\n{'=' * 65}")
    print(f"  BRAND MENTION REPORT: {brand}")
    print(f"{'=' * 65}")

    for i, r in enumerate(results, 1):
        icon = "✅" if r.mentioned else "❌"
        print(f"\n[{i}] {icon} {r.query}")
        print(f"    Sentiment : {r.sentiment}")
        print(f"    Citations : {len(r.citations)}")
        for c in r.citations[:3]:
            print(f"      • {c.title or c.url}")
        print(f"    Snippet   : {r.response_snippet[:120]}...")

    total = sum(1 for r in results if r.mentioned)
    print(f"\n{'─' * 65}")
    print(f"  Mentioned in {total}/{len(results)} queries")
    print(f"  Total unique citations: {sum(len(r.citations) for r in results)}")
    print(f"{'─' * 65}\n")


# ── CLI ────────────────────────────────────────────────────────

if __name__ == "__main__":
    BRAND = "YourBrand"
    QUERIES = [
        "What are the best [your category] tools in 2026?",
        "What tools can track brand mentions in ChatGPT and other AI engines?",
        f"Is {BRAND} a good tool for [your use case]?",
    ]

    results = run_queries(BRAND, QUERIES)
    print_report(BRAND, results)

    # Also dump JSON for programmatic use
    print(json.dumps([asdict(r) for r in results], indent=2))

How It Works

1. The Responses API + Web Search

Instead of the older Chat Completions API, we use the Responses API with the web_search tool. This tells ChatGPT: "don't just answer from training data — go search the web and cite your sources."

resp = client.responses.create(
    model="gpt-5.6-luna",
    tools=[{"type": "web_search", "search_context_size": "medium"}],
    input=query,
)

The search_context_size parameter controls how much search-result context the model sees before answering:

  • low — fast, cheap, minimal context. Good for quick yes/no checks.
  • medium — balanced default. Works well for most brand tracking.
  • high — deeper research, more citations. Use for competitive analysis.

2. Extracting Citations

The response object contains url_citation annotations that tell you exactly which URLs the model consulted. We parse these from the output items:

for item in resp.output:
    if item.type == "message":
        for block in item.content:
            for ann in getattr(block, "annotations", []) or []:
                if hasattr(ann, "url"):
                    citations.append(Citation(url=ann.url, title=ann.title))

This gives you not just whether your brand was mentioned, but which sources ChatGPT used to form its answer — invaluable for understanding what content drives AI visibility.

3. Sentiment Detection

We use a simple lexicon-based approach: scan the 300 characters around the first brand mention for positive and negative words. It's not as sophisticated as a fine-tuned classifier, but it's fast, free, and catches obvious sentiment signals.

window = text[max(0, idx - 300):idx + 300].lower()
pos = sum(1 for w in POSITIVE_WORDS if re.search(rf"\b{w}\b", window))
neg = sum(1 for w in NEGATIVE_WORDS if re.search(rf"\b{w}\b", window))
💡 Pro tip: For production use, swap this out for a proper sentiment model like cardiffnlp/twitter-roberta-base-sentiment-latest on Hugging Face, or use OpenAI's structured outputs to get a JSON sentiment field directly from the model.

Running the Tracker

Customize the BRAND and QUERIES variables at the bottom of the script, then run:

python brand_tracker.py

Here's real output from testing this script with RankBits as the target brand:

=================================================================
  BRAND MENTION REPORT: RankBits
=================================================================

[1] ❌ What are the best AI visibility tracking tools in 2026?
    Sentiment : neutral
    Citations : 7
      • AI Visibility Toolkit Pricing | Semrush
      • Ahrefs Brand Radar: See ANY brand's AI visibility
      • Best AI visibility tools in 2026 (compared) | MentionsAPI

[2] ❌ What tools can track brand mentions in ChatGPT?
    Sentiment : neutral
    Citations : 9
      • Free AI Visibility Checker | Ahrefs
      • Which AI searches does OtterlyAI support?
      • AI Search Visibility Platform | DeepSmith

[3] ✅ Is RankBits a good tool for tracking AI brand visibility?
    Sentiment : neutral
    Citations : 4
      • CodingFleet AI Visibility Report - RankBits
      • How to Get Cited by Gemini in 2026 — RankBits
      • Profound vs Peec AI: Which Tool Is Right for You?

─────────────────────────────────────────────────────────────────
  Mentioned in 1/3 queries
  Total unique citations: 20
─────────────────────────────────────────────────────────────────

The script also outputs a JSON array for programmatic consumption — pipe it into your monitoring dashboards, Slack alerts, or time-series database.

The Limitations of DIY Tracking

This script works. But before you go building a full monitoring system on it, understand what you're signing up for:

1. ChatGPT Is Only One Engine

Your customers are asking 13+ AI engines: ChatGPT, Claude, Gemini, Perplexity, Google AI Overviews, Bing Copilot, DeepSeek, Grok, and more. Each requires its own API integration — and some (like Claude and Google AI Overviews) don't even offer a comparable web-search API. You'd need Playwright-based scraping for those, which is fragile and constantly breaking.

2. AI Responses Are Non-Deterministic

Run the same query twice, ten minutes apart. You'll get different answers, different citations, different sentiment. A single snapshot tells you almost nothing — you need trends over time with regular, scheduled checks.

3. Prompt Design Is an Art

The queries you choose dramatically affect results. "Best AI visibility tools" returns different brands than "What should I use to track AI mentions?" You need 30–50 well-researched prompts that mirror how your actual customers ask questions — not just the ones you hope will mention your brand.

4. Competitor Tracking Multiplies Everything

Knowing your own mention rate is only half the picture. You need share of voice: what percentage of responses mention you vs. your top 5 competitors? That means running every query and checking for every competitor. A 3-brand × 30-query matrix is 90 API calls per check. At ~$0.03–$0.05 per call, that's roughly $3–$5 per check — or about $90–$150/month for daily monitoring. Still reasonable, but that's just ChatGPT. Add the other 12 engines and the cost multiplies fast.

5. Sentiment Is Hard

Our lexicon-based sentiment is a decent start, but it'll miss nuanced cases. "RankBits is cheap" could mean affordable (positive) or low-quality (negative). "RankBits is interesting" could go either way. Proper sentiment requires an LLM-based judge, which adds cost and complexity.

6. You're Building Infrastructure, Not Insights

Scheduling, retries, rate-limit handling, data storage, dashboards, alerting, PDF reports — you'll spend more time on plumbing than on understanding your AI visibility. That's engineering time not spent on the content and PR work that actually improves your visibility.

Capability DIY Script RankBits
ChatGPT tracking
13+ AI engines ❌ (build each manually) ✅ (built-in)
Scheduled recurring checks ❌ (cron + infra) ✅ (set-and-forget)
Competitor share of voice ❌ (manual matrix) ✅ (automatic)
Sentiment analysis ⚠️ (basic lexicon) ✅ (LLM-based)
Citation source tracking
PDF reports ❌ (build yourself) ✅ (one-click export)
Trend dashboards ❌ (build yourself) ✅ (built-in)
Cost per month (30 queries, 3 competitors, daily) ~$90–$150 (ChatGPT only) $49–$99 (all 13+ engines)

When Should You DIY?

The DIY approach makes sense if:

  • You only care about ChatGPT (not the other 12 engines)
  • You're checking 1–2 queries occasionally, not 30+ daily
  • You have engineering bandwidth to maintain the pipeline
  • You're prototyping before committing to a paid tool

For everyone else — marketers, SEO teams, agency owners, SaaS founders — a purpose-built platform saves time, money, and missed insights.

The Shortcut: RankBits Does This Automatically

RankBits is an AI visibility tracker built for exactly this problem. You enter your brand URL, and it:

  1. Generates realistic AI-search prompts — the kind your customers actually type
  2. Queries 13+ AI engines in parallel — ChatGPT, Claude, Gemini, Perplexity, Google AI, Bing Copilot, DeepSeek, Grok, and more
  3. Measures mentions, citations, sentiment, and share of voice — against your competitors
  4. Tracks trends over time — with scheduled, recurring scans so you see improvement, not just snapshots
  5. Gives you dashboards, reports, and alerts — without writing a single line of code

And unlike the DIY approach, RankBits handles the engines that don't offer an API — including Claude, Google AI Overviews, and Bing Copilot — using robust scraping infrastructure that stays up to date as platforms change.

🚀 See How Your Brand Appears in AI Search

Stop guessing. Get a free AI visibility scan across ChatGPT, Claude, Gemini, Perplexity, and 9+ other engines — complete with competitor comparison and citation analysis.

Get Your Free AI Visibility Report →

Full Source Code

The complete, runnable script is below. Copy it, set your OPENAI_API_KEY, customize the brand and queries, and you'll have a working ChatGPT mention tracker in under 5 minutes.

"""
Brand Mention Tracker — Query ChatGPT via OpenAI Responses API with web search
and extract structured brand mentions, sentiment, and citations.

Requirements:
    pip install openai

Usage:
    export OPENAI_API_KEY="sk-..."
    python brand_tracker.py
"""
import os
import json
import re
from dataclasses import dataclass, field, asdict
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])


@dataclass
class Citation:
    url: str
    title: str = ""


@dataclass
class MentionResult:
    query: str
    mentioned: bool
    sentiment: str
    citations: list[Citation] = field(default_factory=list)
    response_snippet: str = ""


POSITIVE_WORDS = [
    "best", "great", "excellent", "top", "leading", "powerful",
    "recommended", "good", "solid", "impressive", "standout",
    "strong", "reliable", "comprehensive", "innovative",
]

NEGATIVE_WORDS = [
    "bad", "poor", "worst", "avoid", "terrible", "limited",
    "expensive", "disappointing", "weak", "unreliable", "outdated",
    "lacking", "overpriced", "slow",
]


def _detect_sentiment(text: str, brand: str) -> str:
    idx = text.lower().find(brand.lower())
    if idx == -1:
        return "neutral"
    window = text[max(0, idx - 300):idx + 300].lower()
    pos = sum(1 for w in POSITIVE_WORDS if re.search(rf"\b{w}\b", window))
    neg = sum(1 for w in NEGATIVE_WORDS if re.search(rf"\b{w}\b", window))
    if pos > neg:
        return "positive"
    elif neg > pos:
        return "negative"
    return "neutral"


def check_brand_mention(
    brand: str,
    query: str,
    model: str = "gpt-5.6-luna",
    search_context_size: str = "medium",
) -> MentionResult:
    resp = client.responses.create(
        model=model,
        tools=[{
            "type": "web_search",
            "search_context_size": search_context_size,
        }],
        input=query,
    )
    output_text = resp.output_text

    citations: list[Citation] = []
    for item in resp.output:
        if item.type == "message":
            for block in item.content:
                for ann in getattr(block, "annotations", []) or []:
                    if hasattr(ann, "url"):
                        citations.append(
                            Citation(url=ann.url, title=getattr(ann, "title", ""))
                        )

    seen = set()
    unique_citations = [c for c in citations if not (c.url in seen or seen.add(c.url))]

    mentioned = brand.lower() in output_text.lower()
    sentiment = _detect_sentiment(output_text, brand) if mentioned else "neutral"

    return MentionResult(
        query=query,
        mentioned=mentioned,
        sentiment=sentiment,
        citations=unique_citations,
        response_snippet=(
            output_text[:500] + ("..." if len(output_text) > 500 else "")
        ),
    )


def run_queries(
    brand: str, queries: list[str], model: str = "gpt-5.6-luna"
) -> list[MentionResult]:
    return [check_brand_mention(brand, q, model=model) for q in queries]


def print_report(brand: str, results: list[MentionResult]) -> None:
    print(f"\n{'=' * 65}")
    print(f"  BRAND MENTION REPORT: {brand}")
    print(f"{'=' * 65}")
    for i, r in enumerate(results, 1):
        icon = "✅" if r.mentioned else "❌"
        print(f"\n[{i}] {icon} {r.query}")
        print(f"    Sentiment : {r.sentiment}")
        print(f"    Citations : {len(r.citations)}")
        for c in r.citations[:3]:
            print(f"      • {c.title or c.url}")
        print(f"    Snippet   : {r.response_snippet[:120]}...")
    total = sum(1 for r in results if r.mentioned)
    print(f"\n{'─' * 65}")
    print(f"  Mentioned in {total}/{len(results)} queries")
    print(f"  Total unique citations: {sum(len(r.citations) for r in results)}")
    print(f"{'─' * 65}\n")


if __name__ == "__main__":
    BRAND = "YourBrand"
    QUERIES = [
        "What are the best [your category] tools in 2026?",
        "What tools can track brand mentions in ChatGPT and other AI engines?",
        f"Is {BRAND} a good tool for [your use case]?",
    ]
    results = run_queries(BRAND, QUERIES)
    print_report(BRAND, results)
    print(json.dumps([asdict(r) for r in results], indent=2))

What's Next?

If you run this script and discover your brand is invisible in ChatGPT, don't panic. AI visibility is still a new frontier, and most brands haven't started optimizing for it. Here's your action plan:

  1. Run a baseline scan — use the script above or RankBits' free scan to see where you stand across all engines
  2. Identify your gap queries — which high-intent questions are your competitors winning?
  3. Create content that answers those questions — publish authoritative, well-structured pages that AI models want to cite
  4. Track weekly — AI responses shift constantly. A single snapshot is useless; trends are everything
  5. Expand beyond ChatGPT — Claude, Perplexity, and Google AI Overviews each have unique audiences and citation patterns

The brands that win in AI search won't be the ones with the biggest ad budgets. They'll be the ones who showed up first and measured consistently.