ChatGPT Ads Insights API: non-goal conversion reporting
Non-goal conversion reporting is a ChatGPT Ads Insights API capability that returns attributed conversion events beyond a campaign's selected goal — for example, attributed purchases for a campaign optimising toward leads or checkouts — without changing its conversion goal or optimisation settings. You call POST https://api.ads.openai.com/v1/conversions/insights with include: ["attributed_events"]. This guide explains every request parameter OpenAI has published, what the response contains, how attribution windows and time basis change the numbers, how to handle the 2,000-row 413 limit, and the reporting use cases that make it worth wiring up — with working curl examples.
The short version
- What it does: reports attributed conversion events other than a campaign's goal — e.g. purchases on a campaign optimising for leads — with no change to goal or optimisation.
- Endpoint:
POST /v1/conversions/insightsonapi.ads.openai.com, Bearer token auth,include: ["attributed_events"]. - Knobs:
attribution_window_days7 / 14 / 30 (default 30),view_through_attribution_window_days0 / 1 (default 1),attribution_time_basisad_event_time(default) orconversion_time. - Caveat:
conversion_timebasis has limited coverage of non-goal events. Use the default for non-goal work. - Limits: HTTP 413 if a report exceeds 2,000 summary rows, 2,000 event rows, or 2,000 breakdown entries for one event. Split by campaign or date and retry.
- Why it matters: you can optimise where the signal is dense and still report where the money is — purchases, revenue and ROAS.
What is non-goal conversion reporting in ChatGPT Ads?
Non-goal conversion reporting lets you retrieve attributed conversion events that are not the campaign's selected conversion goal. If a campaign optimises toward a lead or checkout event, you can still see attributed purchases — counts and values — through the public Insights API. The campaign's goal and optimisation settings do not change.
Before this, a ChatGPT Ads campaign's conversion reporting centred on its own goal. That forced an uncomfortable choice. Optimise for purchases and you might starve the algorithm of signal in a young, low-volume channel. Optimise for an upstream event with more volume and you lose sight of the thing the business actually cares about. Advertisers ended up stitching purchase data back in from their own analytics, with all the attribution mismatches that implies.
Non-goal reporting closes that gap inside OpenAI's own attribution. The same attribution engine that credits your goal event also credits your other recognised events, and you can pull those numbers per campaign, per day, with the windows you choose. For the underlying tracking this depends on, see ChatGPT Ads conversion tracking.
Why would you optimise for one event and report on another?
Because the best optimisation signal and the best business metric are often different events. Upstream events such as leads or checkouts happen more often, so bidding learns faster. Purchases and revenue are what finance cares about. Non-goal reporting lets you bid on the dense signal and still judge the campaign on the valuable one.
This is standard practice on mature platforms, and it matters more on a young one. Volume on ChatGPT is still modest for most advertisers; a campaign that needs a steady flow of conversions to learn may simply not see enough purchases in its early weeks. The conversion campaigns guide covers how oCPC learns; here is how the reporting side now fits:
| Business | Optimise toward (goal) | Report via non-goal events | What you learn |
|---|---|---|---|
| Ecommerce, young account | checkout_started | order_created count and value | True ROAS without starving learning |
| Lead gen with online sales | lead_created | Purchases or subscriptions | Which campaigns produce buyers, not just leads |
| Subscription app / SaaS | Trial or signup event | subscription_created | Paid conversion rate by campaign |
| Retailer with feed campaigns | Upstream commerce event | Purchases per campaign | Which product groups earn their spend |
| Any advertiser with custom events | Primary goal | Custom events (by exact name) | Secondary actions like demo bookings or high-value page views |
The event names above are the standard events used elsewhere in our tracking guides; your own recognised events are whatever you have configured. The point is the pattern, not the specific names.
How does the /v1/conversions/insights endpoint work?
Send an authenticated POST to https://api.ads.openai.com/v1/conversions/insights with a JSON body naming the aggregation level, time granularity, time ranges, entity IDs and attribution settings, and set include to ["attributed_events"]. The response returns summary conversion fields plus an attributed_events array, one item per entity and event.
Authentication uses a Bearer token in the Authorization header, conventionally held in an environment variable such as OPENAI_ADS_API_KEY. Never hard-code the key in scripts or commit it to version control. Here is the minimal request that returns all recognised attributed events for one campaign across one week:
curl -X POST https://api.ads.openai.com/v1/conversions/insights \
-H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"aggregation_level": "campaign",
"time_granularity": "none",
"time_ranges": [
"{\"type\":\"unix_range\",\"start\":\"1788235200\",\"end\":\"1788840000\"}"
],
"entity_ids": ["cmpn_123"],
"attribution_time_basis": "ad_event_time",
"attribution_window_days": 30,
"view_through_attribution_window_days": 1,
"include": ["attributed_events"]
}'
Two details catch people out. First, each time_ranges item is a JSON-encoded string describing a unix_range, not a nested object — note the escaped quotes. Second, start and end are Unix timestamps in seconds, supplied as strings. In the example, 1788235200 to 1788840000 is a seven-day span.
What do the request parameters do?
The body controls four things: what you are grouping by (aggregation level and entity IDs), over what time (time ranges and granularity), how attribution is counted (windows and time basis), and what extra detail comes back (include and event names). The table lists every parameter OpenAI documents for this endpoint.
| Parameter | Accepted values | Default | What it controls |
|---|---|---|---|
aggregation_level | "campaign" (as documented in the example) | — | The entity level rows are grouped by |
time_granularity | "daily" or "none" | — | One row per day, or one row for the whole range |
time_ranges | Array of JSON strings, each a unix_range with start and end | — | Reporting period(s) |
entity_ids | Array of IDs, e.g. ["cmpn_123"] | — | Which campaigns to report on |
attribution_time_basis | "ad_event_time" or "conversion_time" | ad_event_time | Whether conversions are dated to the ad interaction or the conversion |
attribution_window_days | 7, 14, 30 | 30 | How long after an ad interaction a conversion can be credited |
view_through_attribution_window_days | 0 or 1 | 1 | View-through window; 0 excludes view-through attribution |
include | ["attributed_events"] | — | Adds the per-event breakdown, including non-goal events |
event_names | Exact standard or custom event names | All recognised events | Filters the breakdown to specific events |
Which events count as “recognised”?
event_names filters by exact name, and only recognised events are reported. OpenAI defines recognised events as those configured in your conversion event settings, or those that appear in your published received-event history. In practice: if an event is firing correctly and visible in your event history, or you have set it up in conversion event settings, you can report on it. If a name is misspelt or the event never arrived, you will get nothing back for it — check spelling and casing before assuming the campaign drove zero. The event quality guide covers how to confirm events are arriving cleanly.
How do attribution windows change the numbers?
A longer click window credits conversions that happen longer after the ad interaction, so 30-day numbers are always equal to or higher than 7-day numbers for the same period. Setting the view-through window to 0 removes conversions credited to ad views without clicks. Pick windows that match your buying cycle and keep them fixed across reports.
| Setting | Use when | Trade-off |
|---|---|---|
| 30-day click, 1-day view (default) | Considered purchases, B2B, high-ticket items | Numbers mature slowly; recent days look low until the window closes |
| 14-day click | Mid-consideration retail | A middle ground; less lag than 30 days |
| 7-day click | Impulse or low-ticket purchases; fast decision cycles | Undercounts slower buyers |
| View-through 0 | Conservative reporting, finance-facing ROAS, comparing against click-only analytics | Drops any credit for ads seen but not clicked |
| View-through 1 | Understanding full ad influence | Includes conversions some stakeholders will question |
My rule: report the default (30-day click, 1-day view) as the headline because it matches Ads Manager's defaults, and keep a click-only (view-through 0) series alongside it for finance. When the two diverge a lot, that tells you how much of the story relies on view-through credit. Whatever you choose, never change windows mid-series — it creates a step change that looks like performance but is just accounting.
The summary fields split the goal total into click_through_conversions and view_through_conversions, and each attributed event item carries the same split. That makes it simple to produce both views from one request with view_through_attribution_window_days: 1: add both for the full number, use only the click-through figure for the conservative one.
What is attribution_time_basis, and which should you use?
attribution_time_basis decides which date a conversion is reported on. The default, ad_event_time, dates it to the ad interaction that earned the credit; conversion_time dates it to when the conversion happened. OpenAI notes that conversion_time has limited coverage of non-goal events, so use the default for non-goal reporting.
The difference matters most for daily reporting. Under ad_event_time, a purchase made on Friday from an ad clicked on Monday shows up on Monday, which ties conversions to the spend that caused them — the right view for judging campaign efficiency. Under conversion_time, it shows up on Friday, which is closer to how your ecommerce platform or finance system records revenue.
- Use
ad_event_time(default) for ROAS by campaign, for non-goal event reporting, and for any analysis that compares spend with results on the same day. - Consider
conversion_timeonly for goal events when reconciling with a revenue ledger — and be aware that non-goal events may be incomplete on that basis.
A related consequence of ad_event_time: recent days keep growing as late conversions are credited back to them. Yesterday's ROAS is not final. Either report on periods old enough for your window to have closed, or label recent periods as provisional. We cover this reporting-lag trap in measuring ChatGPT Ads ROI.
What does the response contain?
The response carries summary fields for the goal — conversions, click_through_conversions and view_through_conversions — and, with include set, an attributed_events array. Each item names the entity, event name and kind, attributed count, click and view split, and value amount, value count and currency, plus a breakdown by conversion event setting.
The shape below is illustrative: field names are OpenAI's, the values and simplified nesting are mine. Check the reference documentation for the exact structure your integration receives.
{
"account_currency": "USD",
"order_created_attributed_sales": 18420.50,
"order_created_attributed_sales_currency": "USD",
"conversions": 212,
"click_through_conversions": 188,
"view_through_conversions": 24,
"attributed_events": [
{
"entity_id": "cmpn_123",
"event_name": "order_created",
"event_kind": "standard",
"attributed_event_count": 141,
"click_through_conversions": 129,
"view_through_conversions": 12,
"attributed_event_value_amount": 18420.50,
"attributed_event_value_count": 139,
"attributed_event_value_currency": "USD",
"conversion_event_setting_breakdowns": [ ... ]
}
]
}
| Field | Meaning |
|---|---|
conversions | Goal conversions, counting clicks and views within the chosen windows |
click_through_conversions / view_through_conversions | The goal total split by interaction type |
account_currency | The ad account's currency |
order_created_attributed_sales (+ _currency) | Attributed sales value for the order-created event |
attributed_events[].entity_id | The campaign the event is attributed to |
event_name / event_kind | The event's exact name and whether it is standard or custom |
attributed_event_count | Number of attributed occurrences of that event |
click_through_conversions / view_through_conversions (per event) | The event's count split by interaction type |
attributed_event_value_amount | Total attributed value for the event |
attributed_event_value_count | How many of those events carried a value |
attributed_event_value_currency | Currency of the value |
conversion_event_setting_breakdowns | The event broken down by conversion event setting |
Read the value count, not just the value
attributed_event_value_count is the quiet hero of the response. If a campaign shows 141 attributed purchases but a value count of 90, then 51 purchases arrived without a value — and your revenue figure is understated by roughly a third. Always compare count and value count before trusting a ROAS number. A gap usually means some purchase events are sent without an amount, often from one checkout path or one integration. Fixing it is a tracking job, covered in conversion tracking and advanced matching.
Done for you: conversion tracking implementation
Non-goal reporting is only as good as the events behind it. Our tracking setup service installs the pixel and Conversions API, makes sure purchase events carry values, and wires the Insights API into your reporting — from $3,999.
How do you filter to one event, such as purchases?
Add event_names with the exact event name. This request pulls daily attributed purchases for two campaigns over a week, on a 7-day click window with view-through excluded — a conservative, finance-friendly view:
curl -X POST https://api.ads.openai.com/v1/conversions/insights \
-H "Authorization: Bearer ${OPENAI_ADS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"aggregation_level": "campaign",
"time_granularity": "daily",
"time_ranges": [
"{\"type\":\"unix_range\",\"start\":\"1788235200\",\"end\":\"1788840000\"}"
],
"entity_ids": ["cmpn_123", "cmpn_456"],
"attribution_window_days": 7,
"view_through_attribution_window_days": 0,
"include": ["attributed_events"],
"event_names": ["order_created"]
}'
Filtering keeps responses small, which matters for the row limits below, and makes downstream processing simpler: every item in attributed_events is the event you asked for. When you need several events, list them all in one request rather than calling once per event, and split by campaign or date instead if the response grows too large.
What are the 413 limits, and how do you avoid them?
The endpoint returns HTTP 413 when a report would exceed 2,000 summary rows, 2,000 event rows, or 2,000 conversion-event-setting breakdown entries for a single event. The fix is to split the request — fewer campaigns per call, shorter date ranges, time_granularity: "none" instead of daily, or an event_names filter — and combine the results yourself.
Rows multiply quickly. Daily granularity across 90 days for 30 campaigns is 2,700 summary rows before a single event is counted, which already crosses the limit. Rough arithmetic before calling saves a lot of retry logic:
| Driver | Effect on row count | How to reduce |
|---|---|---|
| Number of campaigns | Multiplies every row type | Batch campaigns into groups |
| Daily vs. none granularity | Daily multiplies by the number of days | Use none for period totals |
| Date range length | Multiplies daily rows | Request in weekly or monthly chunks |
| Number of events | Multiplies event rows | Filter with event_names |
| Conversion event settings per event | Drives breakdown entries | Split by campaign so each event's breakdown stays small |
A simple, robust pattern is to catch 413 and bisect — halve the campaign list, or if you are down to one campaign, halve the date range — then retry each half. This Python sketch does exactly that:
import json, os, requests
URL = "https://api.ads.openai.com/v1/conversions/insights"
HEADERS = {"Authorization": f"Bearer {os.environ['OPENAI_ADS_API_KEY']}"}
def unix_range(start, end):
# time_ranges items are JSON-encoded strings, not objects
return json.dumps({"type": "unix_range", "start": str(start), "end": str(end)})
def fetch(campaign_ids, start, end, events=None, window=30, vta=1):
body = {
"aggregation_level": "campaign",
"time_granularity": "daily",
"time_ranges": [unix_range(start, end)],
"entity_ids": campaign_ids,
"attribution_window_days": window,
"view_through_attribution_window_days": vta,
"include": ["attributed_events"],
}
if events:
body["event_names"] = events
r = requests.post(URL, headers=HEADERS, json=body, timeout=60)
if r.status_code == 413:
# too many rows: split the work and try again
if len(campaign_ids) > 1:
mid = len(campaign_ids) // 2
return fetch(campaign_ids[:mid], start, end, events, window, vta) + \
fetch(campaign_ids[mid:], start, end, events, window, vta)
mid = (start + end) // 2
return fetch(campaign_ids, start, mid, events, window, vta) + \
fetch(campaign_ids, mid, end, events, window, vta)
r.raise_for_status()
return [r.json()]
Use daily boundaries when splitting date ranges in production so that daily rows do not straddle two requests. The sketch is deliberately minimal — add retries for transient errors and logging before putting it on a schedule.
How do you calculate true ROAS with non-goal events?
Pull spend per campaign from the Insights endpoints, pull attributed purchase value per campaign from /v1/conversions/insights with the same date range, windows and time basis, and divide value by spend. Check that value count matches purchase count, keep currencies consistent, and report recent days as provisional until the attribution window has closed.
- Get spend. Use
GET /v1/campaigns/{id}/insightsorGET /v1/ad_account/insightswithfieldsincludingspend,impressions,clicks. These endpoints usetime_rangesas date ranges withsince,untilandtimezone. - Get attributed purchases. Call
/v1/conversions/insightswithevent_namesset to your purchase event andinclude: ["attributed_events"]. - Align the periods. The two endpoints express time differently — Unix ranges on one, dated ranges with a timezone on the other. Convert carefully using the ad account's time zone so both cover the same days.
- Join on campaign ID and compute
attributed_event_value_amount / spend. - Sanity-check value coverage by comparing
attributed_event_value_countwithattributed_event_count. - Check currency. Compare
attributed_event_value_currencywithaccount_currencybefore dividing. - Mark provisional periods that are still inside the attribution window.
For campaigns whose goal is already purchases, the standard Insights fields order_created_attributed_sales and order_created_roas give you this directly. Non-goal reporting is what lets you compute the same figure for campaigns optimising toward something else — which, in young accounts, is often most of them.
A worked example
A retailer runs two campaigns. Campaign A optimises for purchases; Campaign B optimises for checkouts because purchase volume was too thin for it to learn. Before non-goal reporting, B's dashboard showed cost per checkout only, and the team argued about whether those checkouts turned into orders. Now one request returns B's attributed purchases and their value. If B spends 4,000 in a month and non-goal reporting shows 9,600 in attributed purchase value, B is running at 2.4x on the same attribution basis as A — a like-for-like comparison without touching either campaign's settings. (Illustrative numbers.)
What is the fastest way to get a first non-goal report?
Pick one campaign that optimises toward an upstream event, find its campaign ID in Ads Manager, and run the minimal curl request above for the last full week with default windows. Read the attributed_events array: if your purchase event appears with a count and value, you have your first non-goal ROAS figure.
- Create or locate an API key for the Ads API and store it as
OPENAI_ADS_API_KEYin your shell or secrets manager. - Choose a campaign with at least a few weeks of delivery, so there is something to attribute.
- Pick a closed period. A week that ended more than 30 days ago gives final numbers on the default window; a recent week is fine for a first look, but label it provisional.
- Convert the dates to Unix seconds in the ad account's time zone.
- Run the request without
event_namesfirst, so you can see every recognised event the campaign is credited with. - Note the exact event names returned, then add
event_namesfor the ones you will report on regularly. - Pull spend for the same period from the campaign insights endpoint and compute ROAS.
What are the best use cases for non-goal reporting?
Five use cases pay back fastest: true ROAS on upstream-optimised campaigns, lead-to-sale quality by campaign, deciding when to switch a campaign's goal, reporting custom secondary actions, and giving finance a conservative click-only view. Each uses the same endpoint with different filters and windows.
1. True ROAS on upstream-optimised campaigns
Covered above. It is the most common reason to wire this up and the one most stakeholders will ask for first.
2. Lead quality by campaign
Lead-gen advertisers with any downstream online conversion — a purchase, a paid subscription, a booked call recorded as a custom event — can now see which campaigns produce leads that go on to convert. Two campaigns with the same cost per lead can differ enormously in cost per customer. That difference used to require CRM joins; for events that reach OpenAI's attribution, it now comes back from the API.
3. Deciding when to switch goals
A campaign optimised for checkouts that now shows steady attributed purchases may be ready to move its goal to purchases. Non-goal reporting gives you the purchase volume history to make that call on evidence rather than guesswork. Changing the goal is a campaign setting change with its own learning implications; reading the numbers first costs nothing.
4. Custom secondary actions
If you send custom events — say, a pricing-page view or a demo request — you can report them by exact name alongside the goal. That is useful for B2B advertisers whose real conversion happens offline weeks later; mid-funnel custom events show which campaigns are moving people forward. See our small business guide for lighter-weight versions of this setup.
5. A finance-safe view
Set view_through_attribution_window_days to 0 and a short click window for a conservative report. It will never match your analytics exactly, but it removes the most common objection — “you're counting people who never clicked” — and gives finance a floor to plan against.
How does this relate to the other Insights endpoints?
The Insights API also has GET endpoints for the ad account, campaigns, ad groups and ads, which return delivery and performance fields such as impressions, clicks, spend, CTR, conversions, attributed sales and ROAS, with segments by country, device, platform and product. The conversions insights endpoint adds the non-goal event breakdown those do not provide.
| Entity insights (GET) | Conversions insights (POST) | |
|---|---|---|
| Endpoints | /v1/ad_account/insights, /v1/campaigns/{id}/insights, /v1/ad_groups/{id}/insights, /v1/ads/{id}/insights | /v1/conversions/insights |
| Best for | Spend, delivery, CTR, goal conversions, segments | Attributed events beyond the goal; per-event counts and values |
| Time ranges | Date ranges with since, until, timezone | JSON-string unix_range with start, end |
| Segments | country, device, platform, product | Campaign-level aggregation (as documented) |
| Notable fields | impressions, clicks, spend, ctr, conversions, order_created_attributed_sales, order_created_roas, product.feed_id, product.item_id, product.title | attributed_events with count, click/view split, value amount, value count, currency, setting breakdowns |
| Other parameters | aggregation_level, time_granularity, fields[], segments[], filters[], sort[] | Attribution windows, time basis, include, event_names |
Two details worth knowing from the same documentation. ctr is returned as a ratio — 0.04 means 4% — so do not multiply by 100 twice in your dashboard. And delivery data is retained for 365 days at non-hourly granularity, so if you want longer history, store the results yourself on a schedule. For product-level analysis of feed campaigns, pair the product segment with bulk-built feed campaigns and the carousel reporting described in the product feed guide.
How should you build a reporting pipeline around it?
Run a scheduled job that pulls entity insights for spend and conversions insights for attributed events, stores raw responses, and rebuilds the last 30 days on every run so late-attributed conversions are captured. Join on campaign ID and date, keep window settings fixed, and label periods still inside the attribution window as provisional.
- Schedule daily. Once a day is enough for most advertisers.
- Re-pull a rolling window. Under
ad_event_time, past days change as conversions arrive. Re-request at least the length of your click window each run and overwrite. - Store raw JSON before transforming. If you change logic later, you can reprocess without re-requesting.
- Normalise to one table: date, campaign ID, spend, goal conversions, and one column per non-goal event count and value.
- Record the settings (windows, time basis) in the table or its metadata so nobody mixes series.
- Archive beyond 365 days if you need longer history than the API retains.
- Feed it into your BI tool or a measurement partner. Our measurement partners guide covers the third-party options.
Non-goal reports are only as good as the pixel and Conversions API events underneath them. Our ChatGPT Ads tracking setup installs and validates both, and a ChatGPT Ads audit uses this report to judge each campaign on the purchases it actually drove.
Why won't the numbers match Google Analytics or Shopify?
Because they measure different things. OpenAI attributes conversions to ChatGPT ad interactions using its own windows, including view-through; your analytics platform uses its own attribution model, often last-click, and may never see the ChatGPT click at all if parameters are stripped. Expect a gap, explain it, and track its trend rather than chasing an exact match.
- View-through: analytics tools rarely credit views. Compare against the click-only (view-through 0) series.
- Windows: align OpenAI's click window as closely as you can to your analytics lookback.
- Time basis:
ad_event_timedates conversions to the click; analytics dates them to the session or order. - Tracking gaps: events missing values or identifiers reduce OpenAI's matched conversions. URL parameters and advanced matching help both sides.
- Deduplication: if the pixel and Conversions API both send events without proper deduplication, counts inflate. Our tracking guide covers
event_iddeduplication.
What mistakes should you avoid?
- Using
conversion_timefor non-goal events. OpenAI notes limited coverage; stay on the default. - Mixing window settings across periods. Fix them and record them.
- Treating recent days as final. They grow as conversions are credited back.
- Ignoring value count. Revenue is understated if some events lack values.
- Misspelling event names. Filters match exact names; wrong names return nothing.
- Sending one giant request. Plan for 413 and split by campaign or date.
- Passing time ranges as objects. Each
time_rangesitem is a JSON string. - Changing a campaign's goal to get reporting. You no longer need to — that is the whole point of the feature.
- Exposing the API key. Keep
OPENAI_ADS_API_KEYin a secrets manager or environment variable.
For the bigger picture on what ChatGPT Ads measurement can and cannot tell you, start with how ChatGPT ads work and our news log for each new reporting capability as it ships.
Frequently asked questions
What is non-goal conversion reporting in ChatGPT Ads?
A capability of the public Insights API that returns attributed conversion events beyond a campaign's selected goal — for example, purchases on a campaign optimising for leads — without changing the conversion goal or optimisation settings.
Which endpoint returns non-goal conversions?
POST https://api.ads.openai.com/v1/conversions/insights, authenticated with a Bearer token, with include set to ["attributed_events"].
Does non-goal reporting change how my campaign optimises?
No. OpenAI states you can retrieve these events without changing the campaign's conversion goal or optimisation settings. It is reporting only.
What attribution windows can I use?
attribution_window_days accepts 7, 14 or 30 (default 30). view_through_attribution_window_days accepts 0 or 1 (default 1); 0 excludes view-through attribution.
What is attribution_time_basis?
It sets the date conversions are reported on: ad_event_time (default) dates them to the ad interaction, conversion_time to when the conversion happened. conversion_time has limited coverage of non-goal events.
How do I report only purchases?
Add event_names with the exact name of your purchase event. Only recognised events — configured in conversion event settings or present in your published received-event history — are returned.
Can I report custom events?
Yes. event_names accepts standard or custom event names by exact match, and each item in attributed_events includes an event_kind field.
Why am I getting HTTP 413?
The report exceeds 2,000 summary rows, 2,000 event rows, or 2,000 conversion-event-setting breakdown entries for one event. Split the request by campaign or date, use time_granularity: "none", or filter events.
What format do time_ranges use?
Each item is a JSON-encoded string describing a unix_range with string start and end Unix timestamps in seconds. Note it is a string containing JSON, not a nested object.
How do I calculate ROAS for a campaign optimising for checkouts?
Pull spend from the entity insights endpoints and attributed purchase value from /v1/conversions/insights for the same period and settings, then divide value by spend. Check value count and currency first.
What does attributed_event_value_count mean?
How many of the attributed events carried a value. If it is lower than attributed_event_count, some events arrived without values and revenue is understated.
Why don't the numbers match my analytics?
Different attribution models, windows, view-through credit and time basis. Compare against a click-only series and track the gap's trend rather than expecting an exact match.
How long is ChatGPT Ads reporting data retained?
OpenAI's reporting docs state delivery data is retained for 365 days at non-hourly granularity. Store results yourself if you need longer history.
Do I need the API, or is this in the Ads Manager interface?
OpenAI announced non-goal conversion reporting through the public Insights API. Plan on using the API for it.
Sources and further reading
- OpenAI for Developers — Ads API reporting documentation (conversions insights endpoint, parameters, response fields, 413 limits, entity insights endpoints; retrieved 1 October 2026).
- OpenAI — “What's new in Ads Manager” weekly note, week of 25 September 2026 (non-goal conversion reporting).
- OpenAI Help Center — Conversion measurement and Measure results.
- OpenAI Help Center — Conversion-optimized campaigns and Understand and improve event quality.
Want purchases and ROAS on every campaign?
30 minutes with Tarun. We will review your event setup and sketch the Insights API reporting that gets true ROAS onto every campaign, whatever it optimises for.
Book a discovery call