querying.ai scrapes the consumer ChatGPT app in logged-out sessions and records which model wrote each answer. On 9 October 2026 the default switched from gpt-5-6 to gpt-6, and our then-current parser recorded inline-citation rates of 91.2% and 22.8%. Those historical readings omit widget citations and require parser-aware comparison. You can read the current default with one request and no API key. To see what the switch means for your own prompts, collect them as CHATGPT tasks and check model, citationPills and sources in each answer.
What changed when gpt-6 became the default?
The public ChatGPT Model Tracker sends the same question set to logged-out ChatGPT every day and updates every hour. This table is its reading of 12 October 2026, 21:00 UTC, comparing every answer written by each default model.
| Component | gpt-5-6 (tracked 4 Sep to 9 Oct) | gpt-6 (since 9 Oct) |
|---|---|---|
| Answers with inline citations | 91.2% | 22.8% |
| Distinct sources cited inline per answer | 2.93 | 0.71 |
| Sources listed in the panel per answer | 16.74 | 18.35 |
| Answers with shopping cards | 24.8% | 3.9% |
| Answers with entity cards | 63.7% | 81.7% |
| Searches per answer | 1.35 | 1.19 |
These are historical readings from a parser that missed widget citations. They describe our stored results at the time. Establishing a model effect requires replaying retained originals with the same parser and comparing matching collection conditions.
How do I read the current model without an API key?
The tracker's data is open JSON. One request returns it:
curl https://api.querying.ai/v1/chatgpt-modelResponses are cached for 5 minutes, and each IP can send 240 requests per minute. Save this code as chatgpt_model.py to print the current default and the before-and-after table. It uses Python's standard library only.
#!/usr/bin/env python3
"""Print which model answers logged-out ChatGPT now and what changed at the last switch. No API key."""
import json
import sys
from urllib.request import urlopen
URL = "https://api.querying.ai/v1/chatgpt-model"
SHARES = ("citationPills", "entities", "shopping", "inlineProducts", "map")
AVERAGES = ("inlineSourcesPerAnswer", "sourcesPerAnswer", "searchesPerAnswer")
def main():
with urlopen(URL, timeout=30) as response:
board = json.load(response)["data"]
current = board["current"]
print(f"Default model: {current['model']} ({current['share']}% of the last 24 hours' answers)")
print(f"Default since: {current['since']}")
eras = board["eras"]
if len(eras) < 2:
return 0
before, after = eras[-2], eras[-1]
print(f"\n{'surface':<24}{before['model']:>10}{after['model']:>10}")
for name in SHARES + AVERAGES:
old, new = before["surfaces"].get(name), after["surfaces"].get(name)
unit = "%" if name in SHARES else ""
print(f"{name:<24}{str(old) + unit:>10}{str(new) + unit:>10}")
return 0
if __name__ == "__main__":
sys.exit(main())Our run on 13 October 2026 printed:
Default model: gpt-6 (99.8% of the last 24 hours' answers)
Default since: 2026-10-09T07:00:00.000Z
surface gpt-5-6 gpt-6
citationPills 91.2% 22.8%
entities 63.7% 81.7%
shopping 24.8% 3.9%
inlineProducts 20.4% 4.1%
map 1.7% 0.5%
inlineSourcesPerAnswer 2.93 0.71
sourcesPerAnswer 16.74 18.35
searchesPerAnswer 1.35 1.19current: the default model, its share of the last 24 hours' answers and the time it took over.switches: every change of default, withat,fromandto.eras: one entry per default model with its averages insurfaces. Rates are percentages of answers, and the threePerAnswerfields are averages.daily: the samesurfacesper day, with each model's share of that day's answers inshares.
Where does a ChatGPT answer cite my site?
A CHATGPT task returns the model that wrote the answer in model. It returns two kinds of citation. citationPills are the source chips inside the answer text, and each one has the cited url and domain. sources is the list in the sources panel. When an answer has no chips, citationPills is not in the response.
The tracker counts an answer as having inline citations when citationPills has at least one entry, and it counts inline sources as distinct chip URLs. The script below counts your answers the same way, so you can compare your prompts with the tracker.
How do I check my own prompts?
Save this code as check_chatgpt_citations.py. It submits each prompt as a CHATGPT task, polls until the task is done, and records the model, the inline citations, the panel sources and where your domain appears. Set --max-in-flight to your plan's concurrency. The script waits and retries on 429 and 503.
#!/usr/bin/env python3
"""Ask ChatGPT your own prompts and check which model answered and where your site is cited."""
import argparse
import json
import os
import sys
import time
from urllib.error import HTTPError
from urllib.parse import quote, urlsplit
from urllib.request import Request, urlopen
BASE_URL = "https://api.querying.ai"
def call(method, path, api_key, payload=None):
data = None if payload is None else json.dumps(payload).encode("utf-8")
request = Request(BASE_URL + path, data=data, method=method, headers={
"Authorization": "Bearer " + api_key, "Content-Type": "application/json"})
while True:
try:
with urlopen(request, timeout=30) as response:
return json.load(response)
except HTTPError as error:
if error.code not in (429, 503): # 429: plan concurrency; 503: queue busy
raise RuntimeError(f"{method} {path} returned HTTP {error.code}") from error
wait = error.headers.get("Retry-After", "")
time.sleep(int(wait) if wait.isdigit() else 5)
def on_domain(url, domain):
host = (urlsplit(url if "//" in url else "//" + url).hostname or "").casefold().rstrip(".")
return host == domain or host.endswith("." + domain)
def read_answer(prompt, task_id, response, domain):
"""One row per answer, counted the way the public tracker counts."""
pills = response.get("citationPills") or []
sources = response.get("sources") or []
inline_urls = {pill["url"] for pill in pills if isinstance(pill.get("url"), str)}
row = {"task_id": task_id, "prompt": prompt, "model": response.get("model") or "unknown",
"inline_citations": bool(pills), "inline_sources": len(inline_urls),
"panel_sources": len(sources)}
if domain:
inline = any(on_domain(url, domain) for url in inline_urls)
listed = any(on_domain(s.get("url", ""), domain) for s in sources if isinstance(s, dict))
row["your_site"] = "inline" if inline else "panel only" if listed else "not cited"
return row
def collect(api_key, prompts, country, domain, max_in_flight):
waiting, running, rows = list(prompts), {}, []
while waiting or running:
while waiting and len(running) < max_in_flight:
body = {"taskType": "CHATGPT", "payload": {"prompt": waiting[0], "country": country}}
task = call("POST", "/v1/async/task", api_key, body)["task"]
running[task["id"]] = waiting.pop(0)
for task_id, prompt in list(running.items()):
result = call("GET", "/v1/async/task/" + quote(task_id, safe=""), api_key)
status = result["task"]["status"]
if status in ("QUEUED", "PROCESSING"):
continue
del running[task_id]
if status == "COMPLETED":
rows.append(read_answer(prompt, task_id, result["response"], domain))
else:
rows.append({"task_id": task_id, "prompt": prompt, "status": status})
print(f"{len(rows)}/{len(prompts)} {status}: {prompt}", file=sys.stderr)
if running:
time.sleep(4)
return rows
def summarize(rows):
by_model = {}
for row in rows:
if "model" in row:
by_model.setdefault(row["model"], []).append(row)
summary = {}
for model, group in by_model.items():
n = len(group)
summary[model] = {
"answers": n,
"inline_citation_share": round(100 * sum(r["inline_citations"] for r in group) / n, 1),
"inline_sources_per_answer": round(sum(r["inline_sources"] for r in group) / n, 2),
"panel_sources_per_answer": round(sum(r["panel_sources"] for r in group) / n, 2),
}
if "your_site" in group[0]:
for place in ("inline", "panel only", "not cited"):
summary[model]["your_site_" + place.replace(" ", "_")] = sum(r["your_site"] == place for r in group)
return summary
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("prompts", help="Text file with one prompt per line")
parser.add_argument("--domain", help="Your site, for example example.com; subdomains count too")
parser.add_argument("--country", default="US")
parser.add_argument("--max-in-flight", type=int, default=2, help="Match your plan's concurrency")
args = parser.parse_args()
api_key = os.environ.get("QUERYING_API_KEY")
if not api_key:
print("Set QUERYING_API_KEY first", file=sys.stderr)
return 1
with open(args.prompts, encoding="utf-8") as handle:
prompts = [line.strip() for line in handle if line.strip()]
domain = args.domain.casefold().rstrip(".") if args.domain else None
rows = collect(api_key, prompts, args.country, domain, args.max_in_flight)
print(json.dumps({"answers": rows, "by_model": summarize(rows)}, indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())Put one prompt per line in prompts.txt, store your key as QUERYING_API_KEY, and pass your site:
export QUERYING_API_KEY='<your-key>'
python3 check_chatgpt_citations.py prompts.txt --domain example.com --country US > citations.jsonWe ran 10 English shopping and how-to prompts with country set to US on 13 October 2026, such as "best CRM for small business" and "best noise cancelling headphones". Each CHATGPT task cost 2 credits, so the set cost 20 credits. For --domain we used techradar.com, the site listed most often in the panels of this set.
| Measure | Result |
|---|---|
| Answers written by gpt-6 | 10 of 10 |
| Answers with inline citations | 0 of 10 |
| Sources listed in the panel per answer | 23.4 (17 to 28) |
| Answers listing techradar.com in the panel | 6 of 10 |
| Answers citing techradar.com inline | 0 of 10 |
The historical 22.8% and this ten-answer run used the earlier parser. Missing citationPills can reflect missed widget citations. Use retained originals to distinguish a parser omission from a source that was only listed.
What do the output fields mean?
answers: one row per prompt. A task that did not complete keeps itstask_idandstatusso you can fetch it later.model: the model that wrote the answer, orunknownwhen the response has nomodel.inline_citationsandinline_sources: whether the answer has any chip, and how many distinct URLs the chips cite.panel_sources: the number of entries in the sources panel.your_site:inlinewhen a chip cites your domain or a subdomain,panel onlywhen only the panel lists it, andnot citedotherwise.by_model: the same counts per model, so answers from two models never share one rate during a switch.
To repeat the check after the next switch, keep the prompt file, country and date with each result. To count brand names in the answer text across ChatGPT, Perplexity, Gemini and Google AI Overviews, see How do I count brand mentions and citations in scraped answers?.
Frequently asked questions
Is this OpenAI's model API?
No. querying.ai reads the consumer ChatGPT app in logged-out sessions, the answers people see without signing in. The model name comes from that answer, not from an API you call.
How fresh is the tracker?
It updates every hour. An hour or a day with too few answers shows no rate, and the API response is cached for 5 minutes.
Why does the checker split results by model?
During a switch, two models answer the same prompts for a while. One combined rate would mix their citation styles.
Do I need an API key for both scripts?
No. chatgpt_model.py reads the public tracker without a key. check_chatgpt_citations.py runs tasks on your prompts and needs a key from your dashboard.
Create an account and API key at https://querying.ai/en to run your first querying.ai request.