ChatGPT Ads ROAS is the revenue of orders attributed to ChatGPT Ads divided by ChatGPT Ads spend, over the same period. It tells you how much revenue each dollar bought, not whether you made money. For that, subtract the purchase cost of what you sold and the ad spend, product by product, using your own order data.
ROAS, gross margin and net margin: three different questions
Ad platforms report ROAS because it needs nothing but revenue and spend. Merchants run on margin. The three figures answer different questions, and confusing them is the most expensive mistake in paid acquisition.
| Metric | Formula | Answers |
|---|---|---|
| ROAS | Ads revenue / ad spend | How much revenue did each unit of spend bring back? |
| Gross margin on ads sales | Ads revenue minus the purchase cost of the units sold | What did the sales leave after paying for the goods? |
| Net margin after ads | Gross margin minus ad spend | Did advertising this product make or lose money? |
A worked example with one product over 30 days:
- Ads revenue: 2,967 USD from 23 units at 129 USD.
- Purchase cost: 74 USD per unit, so 1,702 USD.
- Ad spend: 410 USD.
- ROAS: 2,967 / 410 = 7.2.
- Gross margin: 2,967 minus 1,702 = 1,265 USD.
- Net margin after ads: 1,265 minus 410 = 855 USD.
Now a second product with a very respectable-looking ROAS of 2.5: 1,000 USD of ads revenue for 400 USD of spend, but a purchase cost of 700 USD. Gross margin is 300 USD; net margin after ads is minus 100 USD. The ROAS looked fine; the product lost money every time it was advertised. Only the per-product net margin shows it.
Shipping, payment fees and returns lower the margin further. They are worth including in your own model if you have them per order; the minimum honest version is purchase cost plus ad spend.
Where each number comes from
ROAS and margin combine two data sources that never live in the same system.
Spend comes from OpenAI
The OpenAI Ads API insights reference exposes delivery metrics (impressions, clicks, spend, ctr, cpc, cpm) and conversion fields (conversions, click_through_conversions, view_through_conversions), aggregated at the ad_account, campaign, ad_group or ad level, by hour, day or month. Spend is reported in the ad account’s currency. It also exposes order_created_attributed_sales, defined as “Attributed purchase value across goal and non-goal order_created events”.
Insights can be segmented by product. In our own tests against the API, the product segment returned delivery data such as spend but no conversions. So OpenAI can tell you what each product cost you in ads; it cannot, at product level, tell you what each product sold.
Revenue and costs come from your store
Your order data holds what OpenAI does not: the paid amount of each order, the line items, the variant purchase cost (Shopify’s “Cost per item” or your own cost field), refunds and cancellations. Joining the two gives you per-product ads revenue, per-product spend, and therefore per-product margin.
The join key is the product. Spend is reported per feed item; orders carry variant identifiers in their line items. As long as your feed item_id maps back to a variant, the two meet.
Attributing each order to ads or organic
Before you can compute ROAS, you need to know which orders the ads produced. Two kinds of ChatGPT traffic reach your store: organic product results drawn from your feed, which cost nothing per click, and paid placements. OpenAI’s help center describes ads as separate from the answer and labeled as sponsored, and ad clicks land on your site with an oppref identifier, which the Measurement Pixel reference says the pixel captures from the landing page URL and stores in a first-party cookie.
A fixed rule on each order makes the split reproducible:
| Signal on the order | Counted as |
|---|---|
Landing URL contains oppref | ChatGPT Ads |
utm_source is chatgpt or openai with a paid utm_medium (cpc, ppc, paid, ads) | ChatGPT Ads |
utm_source is chatgpt or openai with any other medium | Organic ChatGPT |
| Referrer is chatgpt.com, chat.openai.com or openai.com | Organic ChatGPT |
| None of the above | Not ChatGPT |
ROAS uses only the ads rows. Putting organic revenue in the numerator inflates ROAS with sales the campaign did not buy, which is how budgets get raised on campaigns that do not pay. The full methodology, and why the order of the rules matters, is in ChatGPT organic vs ChatGPT Ads attribution.
Your number vs the Ads Manager’s number
Expect them to differ. They are not measuring the same thing, and knowing why prevents a lot of wasted debugging.
| Ads Manager | Your order-based count | |
|---|---|---|
| What is counted | Conversion events OpenAI received and attributed | Paid orders whose landing URL carried the ad identifier |
| Window | Click window of 7, 14 or 30 days (default 30 in the insights API), plus a one-day view-through window when available | The session that led to the order |
| Refunds and cancellations | A conversion already sent is not retracted by the Conversions API | Removed from revenue when the order is refunded or cancelled |
| Currency | Ad account currency | Store currency |
The Conversions API reference states that click-through attribution “uses the applicable configured click window” and that “View-through conversions use a fixed one-day window after an eligible ad impression”, with the click taking precedence when both apply. The insights API lets you request the click window (7, 14 or 30 days) and the view window (0 or 1 day) explicitly. A view-through conversion has no click, so no oppref on any URL: it can appear in the Ads Manager and never in an order-based count. Neither number is wrong; report which one you use, and do not mix them in one ratio.
The five traps that skew ChatGPT Ads ROAS
1. Counting the same sale twice
If the pixel and the Conversions API both send a purchase, OpenAI only deduplicates them when they share an identifier. The reference asks you to “reuse the same value as the API id and pixel event_id” under the same Pixel ID; OpenAI then processes “the first event it receives for a matching key and ignores later duplicates”. Without a shared ID, every order counts twice in the Ads Manager and ROAS doubles on paper. How to build that shared ID is covered in OAIQ pixel vs Conversions API.
2. Keeping refunded and cancelled orders in revenue
A conversion sent to OpenAI stays sent. If you compute ROAS on orders, remove the ones refunded or cancelled after the fact, otherwise the figure is flattering by exactly your refund rate. Store the order status and recompute; do not delete history.
3. Mixing currencies
Spend arrives in the ad account’s currency; revenue is in the store’s currency. Dividing euros of revenue by dollars of spend gives a number that looks like a ROAS and means nothing. Either convert with a stated rate on a stated date, or refuse to compute margin and say so. Convrail shows each amount in its own currency and leaves margin empty when the two differ, rather than inventing a conversion.
4. Treating an unknown cost as zero
A product without a purchase cost has an unknown margin, not a 100 percent margin. Treating the missing cost as zero turns every such product into a star and pushes budget toward products that may lose money. Leave the margin empty and fix the cost data.
5. Reading a single day
Spend is reported by day; orders land when they land, and a click today can convert days later within the click window. Daily ROAS swings for reasons that have nothing to do with performance. Read 7 or 30 day windows, and compare like with like.
Turning the numbers into decisions
The point of per-product margin is to move budget. A simple, defensible rule set:
- Rank products by net margin after ads, losses first.
- Pause or exclude products that lose money over a meaningful period, with a minimum loss threshold so noise does not trigger it.
- Keep products still being tested running; a product with little spend has not proven anything either way.
- Stop advertising what you cannot ship. An out-of-stock product with live ads is pure waste.
- Cap bids so a single campaign cannot overspend while you are not looking.
The OpenAI Ads API exposes the campaigns and ad groups these decisions act on, so they can be applied from a tool rather than by hand in the Ads Manager.
How Convrail computes it
Convrail does this join for you. It attributes every paid order to ChatGPT Ads or organic ChatGPT with the rule above and stores the evidence on the order; imports spend per product from your ChatGPT Ads account every day; and reads the variant purchase cost from your store. The Profit screen then shows, for each product over the last 30 days, ad spend, revenue, units sold, net margin and ROAS, with losing products at the top, out-of-stock products that are still advertised flagged, and missing costs or mismatched currencies stated instead of guessed.
From the same screen you can narrow your ads to the products that pay, pause a campaign or change a bid, and turn on daily guardrails (keep ads on products that do not lose money, never let a bid exceed your ceiling), with every change written to a log. See revenue attribution and the plans on the pricing page.
| Task | Do it yourself | With Convrail |
|---|---|---|
| Ads vs organic per order | Parse landing URLs and referrers, store the evidence | Fixed rule, evidence stored on every order |
| Spend per product | Call the insights API daily, segment by product, store it | Imported daily from your ChatGPT Ads account |
| Margin per product | Join spend, line items and purchase costs, handle currencies | Net margin and ROAS per product, losses first |
| Act on it | Pause, rebid and edit product sets in the Ads Manager | Advertise only what pays, guardrails, change log |
Summary
ROAS is a ratio of revenue to spend; profit is what remains after the goods and the ads are paid. Compute ROAS on ads-attributed orders only, net of refunds, in one currency, over a window long enough to mean something. Then go one step further and compute net margin per product, because that is the number that tells you which ads to keep.
Sources
Frequently asked questions
How is ChatGPT Ads ROAS calculated?
Revenue from orders attributed to ChatGPT Ads, divided by the ChatGPT Ads spend over the same period. Revenue from organic ChatGPT orders stays out of the numerator, because no ad spend produced it.
Is a ROAS of 3 profitable?
It depends on your margin. If the products you advertise cost you 60 percent of their price, a ROAS of 3 leaves 3 minus 1.8, then minus the 1 of spend, so 0.2 per dollar spent before shipping and fees. Compute net margin, not ROAS alone.
Why does my ROAS differ from the Ads Manager?
They count different things. OpenAI attributes conversion events it received within its click window (7, 14 or 30 days) and, when available, a one-day view-through window. Your own count matches paid orders to the ad click on their landing URL, and removes refunds and cancellations that a conversion already sent cannot reflect.
Can the Ads API give me revenue per product?
The insights endpoint can be segmented by product for delivery data such as spend, but in our tests against the API, conversions were not available at product level. Per-product revenue comes from your own orders, joined to the per-product spend.
Should I pause a product with a low ROAS?
Pause the products that lose money after purchase cost and ad spend, over a long enough period to be meaningful, and keep the ones still being tested running. A low ROAS on a high-margin product can still be profitable; a decent ROAS on a low-margin product can lose money.