Log in / Create free account🌐 ES☀️

Advanced training · Module 17 — ChatGPT Ads in depth — context hints, measurement, bidding and automation

The ChatGPT Ads API: automating campaigns and reports without falling into its traps

⏱️ 7 min read · 🤖 ChatGPT Ads 🥷 Scripts · updated on 2026-09-16

Ads Manager is fine for setting up a campaign; to run ten, pull reports every morning or hear about a rejection without watching the screen, you need the ChatGPT Ads API.

We measured almost all of that API's traps while setting up our own campaign: here they are, so you don't pay for them.

What it covers

Campaigns, ad groups, ads, images, custom audiences (currently not supported in campaigns targeting the European Economic Area or Switzerland), conversions, reports and bulk operations, on version v1. Note that sending conversions uses a different key (lesson 4).

Authentication: one key per account

Several accounts: there is no MCC

If you come from Google Ads, don't look for the MCC: it doesn't exist. Each advertiser has their own account, which an agency can't create on a client's behalf (the client creates it and invites the agency); one user can access several and switch between them, but with no combined view. Roles are per account: admin, member and viewer.

Through the API, running ten accounts means storing ten keys, and your "MCC" becomes a tool of your own that brings them together. At Ninja Scripts we are building exactly that into our panel.

Limits

The API's traps

Trap What happens What to do
Updating with PATCH or PUT Both return 405 Update with POST
Deleting There is no DELETE Archive what you retire (an archived audience never comes back)
Reading right after writing Eventual consistency: an archived ad read as "paused" instantly and "archived" after 10 seconds Wait and re-read before treating it as done
Listing Lists don't return the parent: the ad group list doesn't say which campaign each group belongs to Filter by the parent in the URL
Parameters that don't exist Silently ignored: our report with level and start_date would have returned the account total, with no per-ad breakdown Test every parameter with an invalid value: if it doesn't return 400, it doesn't exist
Simulating The management API has no simulation mode and no transactions; the Conversions API does offer validate_only, which validates without saving Simulate changes yourself; test conversions with validate_only
Amounts Bids and budgets are sent in micros (1 = 1,000,000; a $60 CPM is sent as $0.06 per impression = 60,000 micros); report spend arrives in the account currency Convert in one single place in your code
Uploading images Without a MIME type you get a 400 even with a valid PNG; minimum 640×640 Declare the file type
Context hints The list you send replaces the previous one Always send the complete list
Editing the creative Creates a new version and another review Only touch it when you mean to change it
Deduplicating pixel and server The event identifier is event_id in the pixel and id in the Conversions API Same value in both, with the same pixel and the same event name
An ad that won't serve Activating isn't enough: a pending account review, an exhausted spend cap or campaign problems block delivery Request include[]=serving_issues; ours said the account's brand review was in progress

Two nuances: an empty serving_issues list doesn't promise impressions, and bid_too_low on an ad group is guidance, not a block. And a 200 only tells you the API didn't complain, not that it did what you meant.

A per-ad report, requested properly

curl -G "https://api.ads.openai.com/v1/ad_account/insights" \
  -H "Authorization: Bearer $OPENAI_ADS_API_KEY" \
  --data-urlencode "aggregation_level=ad" \
  --data-urlencode "time_granularity=daily" \
  --data-urlencode 'time_ranges[]={"type":"date_range","since":"2026-09-01","until":"2026-09-15","timezone":"Europe/Madrid"}'

aggregation_level=ad asks for one row per ad; time_granularity=daily, one per day; and time_ranges[], the period with its time zone. These are the real names: with an invalid value, all three return 400. -G moves the data into the URL and --data-urlencode encodes it (the JSON needs that). The key comes from an environment variable, never from the command itself.

Before trusting the result, check that the rows carry both ad and day (a total without them gives away an ignored parameter); and the last few days can still change, because spend and conversions arrive late (lesson 5).

💡 Ninja trick: before using a new parameter, call it with an absurd value. If the API returns 400, the parameter exists and is validated; if it returns 200, it doesn't exist and is being ignored.

⚠️ Pitfall: if your tool checks your ads' URLs, those hits can land in your analytics as people arriving from the ad. It happened to us: we now filter them out by requiring each visit to be confirmed by a browser.

Good practice

What to remember

📎 Sources and further reading

⚠️ Free training with no support. Ninja Scripts support channels (email and Telegram) are only for the use of the scripts, not for Google Ads questions or questions about this training.

Pick up here

← BeforeOptimising ChatGPT Ads without search terms: experiments, a phased calendar and a weekly routineChatGPT Ads in depth — context hints, measurement, bidding and automationRelatedAutomating with judgement: rules, scripts and AI — what to delegate, what to supervise and what never to automateScripts I — what they are, what they solve and how to install the SuiteRelatedLead quality: scoring every contact and teaching Google the difference between a customer and a browserMeasurement II — offline conversions, values, GA4, attribution and lead qualityRelatedMeasuring ChatGPT Ads from day one: pixel, conversions and how to read the reportAdvertising in ChatGPT: first stepsRelatedChatGPT Ads step by step: account sign-up, brand review and your first campaignAdvertising in ChatGPT: first steps

See the full syllabus

🎓
You're reading the Ninja Academy in the open

Create your free account (no card) to save your progress, take the graded quizzes, earn the Basic level certificate and unlock part of the Intermediate and Advanced training.

Create my free account →
🥷

Subscriber feature

This option is part of the Ninja Scripts Suite subscription.

See the subscription →