Ads in ChatGPT answers: how they work and the ads[] fields
ChatGPT answers now carry advertising. OpenAI began testing ads in ChatGPT in the US on February 9, 2026, for logged-in adult users on the Free and Go tiers, with Plus, Pro, Business, Enterprise and Education tiers excluded; it later announced pilots in Canada, Australia and New Zealand (update of March 26, 2026) and plans for the United Kingdom, Mexico, Brazil, Japan and South Korea (update of May 7, 2026) (OpenAI, “Testing ads in ChatGPT”, checked 2026-09-17). If you buy media or watch competitors, the ads attached to your category’s prompts are now worth monitoring. One limit first: result.ads[] exists in the API, but it comes back empty on the ChatGPT answers served today, so the steps below describe the field and the method, not data you can collect now. Below: requesting them with include.ads, what each field means, and turning the results into advertiser share-of-voice numbers.
What OpenAI says about how the ads work
The same OpenAI post, checked 2026-09-17, states that:
- ads are “clearly labeled as sponsored and visually separated from the organic answer”;
- ads do not influence the answers ChatGPT gives;
- during the test, ads are matched to the topic of the conversation, past chats and past interactions with ads, and when several advertisers match, the most relevant one is shown first;
- ads are not eligible to appear near sensitive or regulated topics such as health, mental health or politics.
Two consequences for measurement. First, because matching uses conversation and account context, one answer shows a single serving decision, not what every user sees; measure frequencies over many runs. Second, an empty ads[] on a health prompt is expected and says nothing about competitor spend.
Definitions
- Served ad: an ad included in ChatGPT’s ad response for an answer, whether or not it was displayed.
- Rendered ad: the served ad displayed as a visible card. At most one is displayed per response.
- Ad presence rate: the share of answers for a prompt set in which a given advertiser is served (or rendered, reported separately).
- Paid share of voice: an advertiser’s rendered ads divided by all rendered ads across the prompt set and period.
The request
Add include.ads to a ChatGPT request. Synchronously:
curl -X POST https://api.answerline.dev/v1/monitor/chatgpt \
-H "Authorization: Bearer $ANSWERLINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompt": "best project management tools for small agencies", "country": "US", "include": { "ads": true } }'
Or as an async task, which fits prompt sets:
{
"taskType": "CHATGPT",
"payload": {
"prompt": "best project management tools for small agencies",
"country": "US",
"include": { "ads": true }
},
"idempotencyKey": "ads-pm-small-agencies-us-2026-09-18"
}
The response fields
When include.ads is true, result.ads is an array with one entry per served ad. On the ChatGPT answers served today it comes back empty; the shape below is the contract:
{
"success": true,
"result": {
"text": "…",
"sources": [ { "position": 1, "url": "…", "label": "…", "description": "…" } ],
"ads": [
{
"brand": { "name": "…", "url": "…", "favicon": "…" },
"cards": [ { "title": "…", "body": "…", "url": "…", "image": "…" } ],
"visibility": "allowed",
"adsResponseIndex": 0,
"rendered": true
}
]
}
}
| Field | Meaning |
|---|---|
brand.name, brand.url |
the advertiser; brand.favicon is optional |
cards[] |
the ad’s carousel cards, each with title, body, url and optional image |
visibility |
OpenAI’s serving status: allowed means the ad was eligible to serve; hidden means OpenAI filtered it, for example by an integrity check |
adsResponseIndex |
the ad’s slot in OpenAI’s served ad response; served order, not what was shown |
rendered |
true if the ad was shown as a visible card; ads with rendered: false were served but not displayed |
Use rendered for exposure. Do not infer exposure from adsResponseIndex 0: the index reflects served order only.
Served-but-not-rendered entries show which advertisers were in contention for the prompt even when another brand’s card was displayed, which is useful for competitive intelligence as long as you label it as “served”, not “seen”.
An ad-tracking run
1. Build the prompt set
Take the commercial prompts from your organic prompt set: category (“best X for Y”), comparison (“X vs Y”) and purchase-intent (“where to buy X”) questions. Informational and sensitive prompts rarely matter for ads. Designing a prompt set covers the rest.
2. Choose markets deliberately
Run the markets where you advertise or plan to. ChatGPT answers from 213 countries, including pilot markets such as the US, Canada, Australia, the United Kingdom, Brazil and Japan. Do not read an empty result as “nobody advertises here”: ads[] comes back empty on the ChatGPT answers served today in every market.
3. Submit and collect
import collections
import datetime as dt
import os
import time
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['ANSWERLINE_API_KEY']}"}
PROMPTS = {
"pm-small-agencies": "best project management tools for small agencies",
"pm-vs": "Asana vs Monday for a marketing team",
"time-tracking": "which time tracking app should a consultancy buy",
}
MARKETS = ["US", "CA", "AU"]
day = dt.date.today().isoformat()
tasks = [
{
"taskType": "CHATGPT",
"payload": {"prompt": p, "country": c, "include": {"ads": True}},
"idempotencyKey": f"ads-{pid}-{c.lower()}-{day}",
}
for pid, p in PROMPTS.items()
for c in MARKETS
]
batch = requests.post(f"{API}/v1/async/task/batch", json=tasks, headers=HEADERS, timeout=60)
batch.raise_for_status()
ids = [r["task"]["id"] for r in batch.json()["results"] if r["success"]]
results = {}
while len(results) < len(ids):
for tid in ids:
if tid in results:
continue
data = requests.get(f"{API}/v1/async/task/{tid}", headers=HEADERS, timeout=30).json()
if data["task"]["status"] in ("COMPLETED", "FAILED"):
results[tid] = data
time.sleep(10)
For recurring runs, replace polling with a webhook on each task (webhooks).
4. Aggregate served and rendered ads
served = collections.Counter()
rendered = collections.Counter()
creatives = collections.defaultdict(set)
answers = 0
for data in results.values():
if data["task"]["status"] != "COMPLETED":
continue
answers += 1
for ad in data["response"]["result"].get("ads", []):
name = ad["brand"]["name"]
served[name] += 1
if ad["rendered"]:
rendered[name] += 1
for card in ad["cards"]:
creatives[name].add(card["title"])
total_rendered = sum(rendered.values())
for name, count in served.most_common():
print(
f"{name:30} served {count / answers:6.1%} "
f"rendered {rendered[name] / answers:6.1%} "
f"paid SoV {rendered[name] / total_rendered if total_rendered else 0:6.1%} "
f"creatives {len(creatives[name])}"
)
Store the raw task JSON with the date, prompt id and market. Card titles and bodies over time are a record of competitors’ messaging tests.
Worked example
These numbers are invented to show the calculation. 30 prompts in one market give 30 completed answers. Brand A is served in 12 and rendered in 7; brand B is served in 9 and rendered in 5; no other brand is rendered. Brand A’s served presence rate is 12 / 30 = 40%, its rendered presence rate 7 / 30 = 23.3%, and its paid share of voice 7 / (7 + 5) = 58.3%. Brand B is served in 9 answers but rendered in only 5, so in 4 answers it was in contention and another ad (or none) was displayed.
What to do with the data
- Competitor ad monitoring: which advertisers appear on your category prompts, in which markets, and how often they are rendered.
- Paid vs earned in one response: compare
ads[]with organicsources[]and brand mentions intextfrom the same answer. A competitor rendered in ads but absent from citations is buying coverage it has not earned organically; the reverse suggests you may not need to buy. - Creative tracking:
cards[].titleandcards[].bodyper brand over time show which messages advertisers test. - Market entry: run your category prompts in a pilot market before launching there and see who already advertises.
- Filtered ads: a share of
visibility: "hidden"entries for a brand shows ads OpenAI filtered; report them apart fromallowedones.
For the organic side of the same answers, see how AI engines choose citations and measuring brand mentions.
Ads on Google surfaces
Google’s surfaces carry ads too, and the Google endpoints return ads with their own shapes.
| Surface | Field | Key subfields |
|---|---|---|
| Google Search | result.ads[] |
position, blockPosition (top, bottom, middle, rhs), type (RESULT, SHOPPING_CARD), title, url, displayedUrl, domain, description, page, sitelinks[], and for shopping cards price, oldPrice, store, category, imageUrl |
| Google AI Overview | result.aioverview.ads[] |
position, title, url, type (TEXT or SHOPPING); domain and description on TEXT, price and store on SHOPPING |
| Google AI Mode | result.ads |
an object with title and ads[] (title, url, position, price, store, rating, reviews) |
AI Overview ads need include.aioverview on the Google request (+$0.00071 on the $0.00103 Google Search task). A combined view (ChatGPT ads, Google text ads and AI Overview ads for the same keyword) shows where a competitor concentrates spend. See Google AI Overviews monitoring, SERP feature tracking and the Google Search engine page.
Cost
A ChatGPT task is $0.0006 at the Starter tier, and include.ads and include.shopping are free. So a task with ads, or with ads and shopping, is $0.0006.
| Run | Tasks | Cost |
|---|---|---|
| 30 prompts x 1 market, tasks with ads | 30 | $0.018 |
| 90 prompts x 1 market, tasks with ads and shopping | 90 | $0.054 |
New accounts get 100 free requests on any engine, ChatGPT included, enough for 100 tasks with ads; after that you top up your balance. See pricing for tier prices and cost planning for budgeting.
Pitfalls
- Treating served as seen. Only
rendered: truewas displayed. Report served and rendered separately. - Reading
adsResponseIndexas rank on screen. It is served order. - One run per prompt. Ad selection depends on context, so single runs swing. Aggregate across runs and days.
- Mixing field shapes. ChatGPT
ads[]hasbrandandcards; Googleads[]hastitle,urlanddomain. Normalize to advertiser domain before combining. - Sensitive prompts. OpenAI says ads are not eligible near health, mental health or political topics; exclude them from paid share-of-voice denominators.
- Assuming a fixed market list. OpenAI has changed pilot markets over time; re-check its announcements before planning coverage.
Field definitions are in the API reference; engine details on the ChatGPT engine page.
Questions
Does ChatGPT show ads?
Yes. OpenAI began testing ads in ChatGPT in the US on February 9, 2026, for logged-in adults on the Free and Go tiers, and later announced pilots in more countries. Ads are labeled as sponsored and shown separately from the answer.
How do I get ChatGPT ads from the API?
Set include.ads to true on a ChatGPT request or CHATGPT task. The result then carries ads[], one entry per served ad, with brand, cards, visibility, adsResponseIndex and rendered. On the ChatGPT answers served today the array comes back empty.
What is the difference between a served ad and a rendered ad?
ChatGPT can serve several candidate ads for one response but displays at most one. Every served ad appears in ads[]; the one actually shown as a card has rendered set to true.
How much does ad tracking cost?
include.ads is free, so a ChatGPT request with it costs the base $0.0006 at the Starter tier. Shopping and rawResponse are free too.
Why is ads[] empty for my prompt?
Because ads[] comes back empty on the ChatGPT answers the API serves today, whatever the prompt and market. Separately, OpenAI says ads are not eligible near sensitive or regulated topics such as health, mental health or politics, and its ad pilots run in specific countries.
Can I track Google ads the same way?
Google Search responses carry their own ads[] with position, blockPosition, type, title, url, domain and description, and AI Overview ads appear under aioverview.ads[]. The field shapes differ from ChatGPT's, so parse them separately.