AnswerLine Start free

, ChatGPT · Brand Monitoring · Tutorials

ChatGPT visibility tracker: metrics, prompts and sampling

A ChatGPT visibility tracker runs a fixed set of buyer prompts through ChatGPT on a schedule, several times per prompt and market, and reduces each answer to a few facts: was the brand mentioned, was its site cited and where, did its products appear. Those rates, over time and against competitors, are the visibility metric. You can buy this as a platform (Peec AI, Otterly.ai, Profound, Scrunch, AthenaHQ, Semrush, Ahrefs Brand Radar) or build it on an answer API. The method comes first here, since it determines whether either approach gives numbers you can trust; then a Python pipeline on AnswerLine; then the off-the-shelf options.

Third-party facts are from the vendors’ own pages, checked 2026-09-17, with links. For the same measurement across engines rather than ChatGPT alone, see brand mentions per engine.

Options

Approach Engines What you can measure Geo Pricing model and entry price (checked 2026-09-17)
DataForSEO LLM Scraper + your own pipeline ChatGPT, Gemini sources, search_results, fan_out_queries, brand_entities, product items Location, language From $0.0012 per results page; $50 minimum top-up
DataForSEO LLM Mentions ChatGPT, Google AI Overview (prompt database) Mentions across 280M+ prompts, not your own prompt set Database $0.1 per request + $0.001 per row
Bright Data ChatGPT Scraper + your own pipeline ChatGPT, Perplexity, Gemini, Copilot, AI Mode Answer text, citations, links, recommendations Country $1.50 per 1K records; 5K records/month free
AnswerLine + the pipeline below ChatGPT, plus Gemini, Perplexity, Copilot, Grok, Google AI Mode and AI Overview Mentions, sources[] with position, citationPills[], entities[] (brands and products named) 213 countries on ChatGPT, Gemini and AI Mode, any country on Copilot, Perplexity and Grok; US states on Gemini Prepaid USD balance: $0.0006 per ChatGPT task at Starter; lower on higher tiers (pricing)
Peec AI 3 of ChatGPT, AI Mode, AI Overviews, Copilot, Perplexity, Gemini Visibility, position, sentiment, share of voice, sources Multi-country on Advanced Starter €85/month, 50 prompts, daily
Otterly.ai ChatGPT, AI Overviews, Perplexity, Copilot (+ add-ons) Platform reports; API on Standard and up Platform Lite $29/month, 15 prompts
Semrush AI Visibility Toolkit ChatGPT, Google AI, Gemini, Perplexity Prompt tracking, mentions Platform $99/month per domain, 25 prompts
Profound Trial: ChatGPT only; Enterprise up to 9 engines Platform reports; API on Enterprise Enterprise: multiple regions Free trial (10 prompts, run once); Enterprise custom
OpenAI API with web_search Your configured model, not the app Citations for your own call user_location $10 per 1K search calls + tokens (pricing)

Metrics

Define these before collecting anything. Each maps to one part of the ChatGPT answer.

Metric Definition AnswerLine response field
Mention rate Answers that name the brand ÷ completed answers text
Citation rate Answers whose sources include the brand’s domain ÷ completed answers sources[].url
Average citation position Mean best position of the domain, over answers where it is cited sources[].position
Product presence Answers that name one of the brand’s products ÷ completed answers entities[] with type product
Share of voice Brand mentions ÷ mentions of all brands in the defined competitor set text across brands
Cited competitors Domains ChatGPT cites for a topic, ranked by how often they appear sources[].url

To keep them comparable over time:

  1. Only completed answers go in the denominator. A failed collection is not an answer without you.
  2. Mentions and citations stay separate. A brand can be recommended without a link and linked without a recommendation. See mentions vs citations.
  3. The competitor set is fixed and versioned, because share of voice changes meaning whenever the set changes. The share of voice framework covers the arithmetic.

Building the prompt set

The prompt set decides what “visibility” means for you.

  1. Start from buyer questions rather than keywords. Sales calls, support tickets, site search and People Also Ask are better sources than a keyword list; People Also Ask for prompt research shows one method.
  2. Cover the funnel: category questions (“best payroll software for small business”), comparisons (“Gusto vs ADP”), problems (“how to run payroll for contractors in two states”), and brand questions (“is Acme Payroll good for restaurants”).
  3. Tag every prompt with topic, funnel stage and whether it names a brand. Unbranded prompts measure discovery and branded prompts measure reputation, so report them separately.
  4. Write prompts the way people type: short, conversational, sometimes with context (“for a 20-person agency”). Do not name your brand in unbranded prompts.
  5. Size by topic. Ten to twenty prompts per topic gives a rate that no single phrasing dominates.
  6. Keep a stable core. Changing prompts breaks trend lines, so add new prompts as a separate cohort and fold them in after a few weeks.

Prompt set design goes deeper on balance and size.

Sampling: how many runs you need

ChatGPT’s answers vary between runs of the same prompt, so a single daily run per prompt measures that noise as much as your visibility. Treat each answer as a sample and report rates over pooled samples.

For a rate p measured over n answers, the 95% margin of error is about 1.96 × √(p(1−p)/n):

Answers pooled (n) Margin at p = 10% Margin at p = 30% Margin at p = 50%
50 ±8.3 pts ±12.7 pts ±13.9 pts
200 ±4.2 pts ±6.4 pts ±6.9 pts
1,000 ±1.9 pts ±2.8 pts ±3.1 pts

In practice:

The sampling vs census post and AI answer volatility cover this in more depth.

A pipeline on AnswerLine

One Python file with three commands: submit queues today’s runs, collect fetches finished tasks and extracts brand signals, and report prints weekly rates. It uses requests and SQLite against the HTTP API, and polls for results to stay self-contained; at larger volume, use a webhook instead.

Inputs

prompts.csv:

id,topic,prompt
p001,category,best payroll software for small business
p002,category,easiest payroll software for restaurants
p003,comparison,Gusto vs ADP for a 20-person company

The script

import csv, datetime, os, re, sqlite3, sys
from urllib.parse import urlparse

import requests

API = "https://api.answerline.dev"
COUNTRIES = ["US"]
RUNS_PER_DAY = 3
BRANDS = {
    "acme": {"patterns": [r"\bacme payroll\b"], "domain": "acmepayroll.com"},
    "gusto": {"patterns": [r"\bgusto\b"], "domain": "gusto.com"},
    "adp": {"patterns": [r"\badp\b"], "domain": "adp.com"},
}

http = requests.Session()
http.headers["Authorization"] = f"Bearer {os.environ['API_KEY']}"
db = sqlite3.connect("chatgpt_visibility.db")
db.executescript("""
create table if not exists tasks (
  task_id text primary key, prompt_id text, topic text, country text, day text, run int, status text);
create table if not exists hits (
  task_id text, brand text, mentioned int, cited int, best_position real, in_products int,
  primary key (task_id, brand));
create table if not exists cited (task_id text, domain text);
""")


def submit():
    day = datetime.date.today().isoformat()
    with open("prompts.csv", newline="", encoding="utf-8") as f:
        prompts = list(csv.DictReader(f))
    meta, tasks = [], []
    for p in prompts:
        for country in COUNTRIES:
            for run in range(RUNS_PER_DAY):
                meta.append((p["id"], p["topic"], country, day, run))
                tasks.append({
                    "taskType": "CHATGPT",
                    "payload": {
                        "prompt": p["prompt"],
                        "country": country,
                        "include": {"shopping": True},
                    },
                    "idempotencyKey": f"{p['id']}-{country}-{day}-r{run}",
                })
    for start in range(0, len(tasks), 500):
        res = http.post(f"{API}/v1/async/task/batch", json=tasks[start:start + 500], timeout=60)
        res.raise_for_status()
        for item in res.json()["results"]:
            m = meta[start + item["index"]]
            if item["success"]:
                db.execute("insert or ignore into tasks values (?,?,?,?,?,?,?)",
                           (item["task"]["id"], *m, "QUEUED"))
            else:
                print("not queued:", m, item["error"]["code"])
    db.commit()


def on_domain(url, domain):
    host = (urlparse(url).hostname or "").lower()
    return host == domain or host.endswith("." + domain)


def product_text(result):
    names = [e.get("name", "") for e in result.get("entities", []) if e.get("type") == "product"]
    return " ".join(n for n in names if n).lower()


def extract(task_id, result):
    text = result.get("text", "").lower()
    products = product_text(result)
    for brand, cfg in BRANDS.items():
        mentioned = any(re.search(p, text) for p in cfg["patterns"])
        positions = [s["position"] for s in result.get("sources", [])
                     if on_domain(s.get("url", ""), cfg["domain"])]
        in_products = any(re.search(p, products) for p in cfg["patterns"])
        db.execute("insert or replace into hits values (?,?,?,?,?,?)",
                   (task_id, brand, mentioned, bool(positions),
                    min(positions) if positions else None, in_products))
    db.executemany("insert into cited values (?,?)",
                   [(task_id, (urlparse(s.get("url", "")).hostname or "").lower())
                    for s in result.get("sources", [])])


def collect():
    pending = db.execute("select task_id from tasks where status in ('QUEUED','PROCESSING')").fetchall()
    for (task_id,) in pending:
        res = http.get(f"{API}/v1/async/task/{task_id}", timeout=30)
        res.raise_for_status()
        body = res.json()
        status = body["task"]["status"]
        if status == "COMPLETED":
            extract(task_id, body["response"]["result"])
        db.execute("update tasks set status = ? where task_id = ?", (status, task_id))
        db.commit()


def report():
    rows = db.execute("""
      select strftime('%Y-%W', t.day) as week, t.topic, h.brand,
             count(*) as answers,
             round(100.0 * avg(h.mentioned), 1) as mention_pct,
             round(100.0 * avg(h.cited), 1) as citation_pct,
             round(avg(h.best_position), 2) as avg_position,
             round(100.0 * avg(h.in_products), 1) as product_pct
      from hits h join tasks t using (task_id)
      where t.status = 'COMPLETED'
      group by week, t.topic, h.brand
      order by week, t.topic, mention_pct desc""").fetchall()
    for row in rows:
        print(*row, sep="\t")


if __name__ == "__main__":
    {"submit": submit, "collect": collect, "report": report}[sys.argv[1]]()

Run python tracker.py submit once a day, python tracker.py collect every few minutes until nothing is pending, and python tracker.py report for the table. Test it on a few prompts before scheduling the full set.

Design choices

Extending it

  1. More markets: ChatGPT answers from 213 countries. Add the ones you sell in to COUNTRIES; the country is already part of the key.
  2. Webhooks: add "webhook": {"url": ...} to each task and move extract into the handler, reading response.result. Verify signatures first.
  3. Other engines: set taskType to GEMINI and remove the ChatGPT-only include flags, which other engines reject. The text and sources[] fields used here exist on Gemini responses too. Perplexity, Copilot, Grok and AI Mode work the same way (PERPLEXITY, COPILOT, GROK, AIMODE); AI Mode’s shopping fields differ from ChatGPT’s.
  4. Cited-domain analysis: group cited.domain by frequency per topic to find the sites ChatGPT cites most; see how AI engines choose citations.
  5. Mention detection: regex on brand names misses misspellings and catches homonyms. Keep an alias list per brand and review a sample of matches each month.
  6. Alerts: compare this week’s rates with the previous four weeks and alert when a change exceeds the margin of error in the sampling table.

Reading the weekly report

report prints one row per week, topic and brand. Read it in this order:

  1. Answer count. A week with far fewer completed answers than planned has wider margins; check the sampling table before comparing it with other weeks.
  2. Mention rate against citation rate. High mentions with low citations means ChatGPT knows the brand but cites other sites. Low mentions with some citations means your pages are retrieved but the answer recommends others. The cited table shows which domains were cited instead of yours.
  3. Average position, only when citation rate is stable. Position over a handful of cited answers swings widely.
  4. Product presence in commercial topics. A brand can lead the text and still have none of its products named.
  5. The answers behind any change. The script stores only extracted signals and cited domains; add a table for text and full source URLs so a moved number can be explained from the answers.

Cost of this pipeline

100 prompts, three runs a day, one market, 30 days: 9,000 ChatGPT answers a month.

Option Billing (checked 2026-09-17) Monthly
DataForSEO LLM Scraper, standard queue $0.0012 per results page $10.80, collection only; $50 minimum top-up
Bright Data, pay as you go $1.50 per 1K records $13.50, collection only
AnswerLine $0.0006 per task at Starter $5.40, collection only, a 30-day spend in the Starter tier; see /pricing
Otterly.ai Standard $189/month, 100 prompts, daily, four engines Platform, not per answer
Peec AI Pro €205/month, 150 prompts, three models, daily Platform, not per answer

API and scraper lines cover collection, and your pipeline covers the rest. Platform lines include reports but fix run frequency and prompt allowance. AI monitoring cost planning shows how cadence and runs per prompt change the total.

Off-the-shelf ChatGPT trackers

These platforms include ChatGPT (entry details checked 2026-09-17):

Before choosing, ask how many runs per prompt the platform takes and how it defines visibility; the sampling table applies to platforms too. The AI visibility tools comparison covers each in more detail.

Pitfalls

The method behind mention measurement is in measuring brand mentions in ChatGPT, and the ChatGPT response fields are on the ChatGPT engine page.

Questions

How do you track brand visibility in ChatGPT?

Run a fixed set of prompts your buyers would ask through ChatGPT on a schedule, several times each, per market. For every answer record whether your brand is mentioned, whether your domain is cited and at what position, and whether the answer names your products, then report those rates over time against competitors.

What should I ask a tracking platform before subscribing?

How many runs per prompt it takes and how it defines visibility, how many prompts and markets the plan covers, whether it measures the ChatGPT app rather than a model API, and whether you can export the underlying answers rather than only its charts.

Which metrics matter for ChatGPT visibility?

Mention rate, citation rate, average citation position, product presence (the products the answer names), and share of voice against a defined competitor set. Keep mentions and citations separate: being named and being linked are different outcomes.

Can I use the OpenAI API to track ChatGPT visibility?

It measures something else. The OpenAI API answers with the model and settings you choose; ChatGPT users get the app's answer, with its own search and citations. Visibility tracking should use the app's answers.

What does the pipeline in this post cost to run on AnswerLine?

At the Starter tier each ChatGPT task with include.shopping costs $0.0006; the flag is free. 100 prompts run three times a day in one market is 9,000 tasks and about $5.40 a month; tier prices are on the pricing page.

Try it on your own prompts

100 free requests on any engine, no card. One POST returns the answer, sources and citations as JSON.

Keep reading