TL;DR
- ChatGPT Ads run inside ChatGPT conversations as chat cards or product ads, managed through OpenAI's Ads Manager (currently in beta) or programmatically via the Advertiser API.
- Auth is refreshingly simple: one API key per ad account, created in Ads Manager under Settings → General → API Keys, sent as a Bearer token. No OAuth, no refresh tokens.
- The hierarchy is Campaign → Ad Group → Ad. Budgets and bids are in micros (1,000,000 micros = 1 unit of currency); insights spend comes back in plain currency units. Yes, both, in the same API.
- We integrated this API into Adstudio this week, tested everything below against a live ad account, and collected the gotchas so you don't have to.
OpenAI opened up advertising in ChatGPT, and with it an Advertiser API that will feel familiar if you've ever worked with the Meta or Google Ads APIs — except it's about a tenth of the surface area, which frankly is a feature. We just finished building a ChatGPT Ads integration for Adstudio, so this guide is written from the trenches: every request below was run against a real ad account this week.
ChatGPT Ads are sponsored placements that appear in ChatGPT conversations. Two creative formats exist today:
Targeting is contextual rather than demographic: ad groups carry context_hints (e.g. ["productivity", "team collaboration"]) that describe the conversations where your ad is relevant. Geotargeting by country, region, and DMA is also supported. There's no lookalike audience, no pixel-based retargeting — the conversation is the targeting signal.
You manage everything from the OpenAI Ads Manager (labeled Beta as of July 2026), or via the API described below.
sk-svcacct-.Two things worth knowing about these keys:
| Thing | Value |
|---|---|
| Base URL | https://api.ads.openai.com/v1 |
| Auth | Authorization: Bearer $OPENAI_ADS_API_KEY |
| Hierarchy | Campaign → Ad Group → Ad |
| Content type | JSON (uploads also accept multipart/form-data) |
| Rate limits | 600 req/min per endpoint, 1,200 req/min overall |
| Updates | POST to the object URL (not PATCH/PUT) |
Verify your key works before anything else:
curl "https://api.ads.openai.com/v1/ad_account" \
-H "Authorization: Bearer $OPENAI_ADS_API_KEY"
You get back your account's ID, name, currency, timezone, and review status. Note the endpoint is /ad_account — not /account, which returns a 404. We learned this the hard way (more in the gotchas section).
Upload an image by URL (or multipart) and keep the returned file_id:
curl -X POST "https://api.ads.openai.com/v1/upload" \
-H "Authorization: Bearer $OPENAI_ADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image_url": "https://example.com/assets/launch-card.png"}'
# → {"file_id": "file_901"}
curl -X POST "https://api.ads.openai.com/v1/campaigns" \
-H "Authorization: Bearer $OPENAI_ADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Spring launch",
"status": "active",
"budget": { "lifetime_spend_limit_micros": 25000000 }
}'
That budget is 25000000 micros = 25.00 in your account currency (minimum is 1,000,000 = one unit). Omit start_time and the campaign starts delivering as soon as it's allowed to; omit location targeting and it can serve in all available locations.
curl -X POST "https://api.ads.openai.com/v1/ad_groups" \
-H "Authorization: Bearer $OPENAI_ADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"campaign_id": "cmpn_101",
"name": "US English",
"status": "active",
"context_hints": ["productivity", "team collaboration"],
"bidding_config": {
"billing_event_type": "impression",
"max_bid_micros": 60000
}
}'
The context_hints array is the interesting part — this is where you tell OpenAI which conversation contexts your ads belong in. Billing is per impression, and your max bid is again in micros (60000 micros = 0.06 per impression here).
curl -X POST "https://api.ads.openai.com/v1/ads" \
-H "Authorization: Bearer $OPENAI_ADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ad_group_id": "adgrp_301",
"name": "Planner launch card",
"status": "active",
"creative": {
"type": "chat_card",
"title": "Try the new workspace planner",
"body": "Coordinate tasks, docs, and meetings in one place.",
"target_url": "https://example.com/planner",
"file_id": "file_901"
}
}'
The response includes "review_status": "in_review" — every ad passes review before serving (typically minutes, per OpenAI). For an ad to actually show, the ad and its parent ad group and campaign all have to be active, and the review has to pass.
Insights live under each scope — GET /ad_account/insights, /campaigns/{id}/insights, /ad_groups/{id}/insights, /ads/{id}/insights. The account-level endpoint plus aggregation_level is the workhorse: one call returns a row per campaign (or ad group, or ad):
curl -G "https://api.ads.openai.com/v1/ad_account/insights" \
-H "Authorization: Bearer $OPENAI_ADS_API_KEY" \
--data-urlencode "aggregation_level=campaign" \
--data-urlencode "time_granularity=none" \
--data-urlencode 'time_ranges[]={"type":"date_range","since":"2026-07-01","until":"2026-07-14"}'
Metrics available: impressions, clicks, spend, ctr, cpc, cpm. You can segment by product, country, or device, filter, sort, and bucket by hour/day/month. Two details that cost us time:
time_ranges must use the array bracket syntax (time_ranges[]=...) with a JSON-encoded range object as the value. Passing it as a plain parameter returns a 400.spend comes back in currency units (e.g. 42.75), even though every budget and bid you sent in was in micros. Don't divide by a million twice.We integrated this API into Adstudio's cross-platform dashboard this week. The docs are good, but here's what actually bit us:
/ad_account, not /account. Our connect flow validated keys against GET /account and got Invalid URL errors from the live API. If you're getting 404s with a valid key, check your paths against the API reference — don't guess them from other ad platforms' conventions.POST /campaigns/{id} with partial fields updates the campaign. There is no PATCH. And if you update budget, you must send the complete budget object.POST /campaigns/{id}/archive is irreversible. Use pause unless you're certain.currency_code from GET /ad_account instead of assuming.after, has_more, last_id), max 500 per page for campaigns. Same object: "list" envelope OpenAI uses in its platform API.If you're running one account with a handful of campaigns, Ads Manager is enough. The API earns its keep when ChatGPT Ads is one of several platforms you're running — because it's another dashboard, another currency/timezone context, and another place to check every morning.
That's the case we built for: Adstudio connects your ChatGPT Ads account with a single API key (Settings → Integrations → ChatGPT Ads) and shows campaigns, ad groups, and ads with their metrics next to your Google, Meta, and TikTok data — same tables, same date ranges, one view. The step-by-step connection guide is in our docs.
Does ChatGPT actually show ads? Yes — OpenAI serves sponsored placements in ChatGPT conversations, managed through the Ads Manager (in beta). Ads render as chat cards or product ads and are marked as sponsored.
How much do ChatGPT Ads cost?
There's no rate card; it's an auction. You're billed per impression against the max_bid_micros you set at the ad group level, bounded by campaign lifetime budgets. Minimum campaign budget is one unit of your currency (1,000,000 micros).
Can I manage ChatGPT Ads entirely through the API? Almost — campaigns, ad groups, ads, creatives, product feeds, geotargeting, conversion setup, and reporting are all in the API. Account creation and billing setup happen in Ads Manager.
Is there an official SDK? Not yet — it's a straightforward REST API with an OpenAPI spec you can download and generate a client from. Any HTTP client works.
How do I target specific audiences?
Contextual, not demographic: context_hints on ad groups describe relevant conversation topics, and campaign targeting supports country/region/DMA locations. There are no interest audiences or retargeting lists.
Can I see ChatGPT Ads performance alongside Google and Meta? Not natively — Ads Manager only shows OpenAI data. That's what cross-platform tools are for; Adstudio does this with a one-time API key connection.