ChatGPT Ads conversion tracking: the pixel, the Conversions API, and attribution

Tarun Kapoor, founder of Context Hints, seated at a wooden desk with a soft city light behind him.Tarun Kapoor Updated July 24, 2026 21 min read

ChatGPT Ads conversion tracking runs on three mechanisms — the oaiq JavaScript pixel, the server-side Conversions API, and a no-JavaScript image tag — all reporting into a Pixel ID you provision in Ads Manager. Since conversion-optimized bidding went live in June 2026, this is no longer just reporting plumbing: the events you send are the training signal for the auction, so a broken implementation actively degrades delivery. This guide is the full implementation reference — code, all 13 events, deduplication, hashing rules, consent and CSP, platform-specific setup, QA, and an honest account of the attribution you will still be missing.

The 2-sentence answer

ChatGPT Ads conversion tracking works through three mechanisms — the oaiq JavaScript pixel for browser events, the Conversions API for server-side events, and a no-JavaScript image tag fallback — all reporting into a Pixel ID you provision in Ads Manager's conversions tab. Running the pixel and the Conversions API together, deduplicated by a shared event_id, is the configuration OpenAI's own documentation points toward and the only one that survives ad blockers, cookie restrictions, and multi-session buying journeys.

The short version

  • The pixel is oaiq, loaded from bzrcdn.openai.com, initialized with your Pixel ID. Events fire via oaiq("measure", "<event>", { … }).
  • The Conversions API endpoint is https://bzr.openai.com/v1/events?pid=<PIXEL-ID> — POST, bearer auth, up to 1,000 events per batch, and the whole batch fails if one event fails.
  • 13 standard events across four data shapes: contents, customer_action, plan_enrollment, and custom.
  • Money is in minor units. $129.99 is 12999. Always send currency with amount.
  • Deduplicate by reusing one identifier as the pixel's event_id and the API's id.
  • Hash email and external IDs with SHA-256, lowercase hex. Never send raw emails, IDs, or phone numbers. Geography, IP, and user agent go through raw.
  • Timestamps must be within the last 7 days and no more than 10 minutes in the future.
  • Only about 40% of ChatGPT-influenced conversions happen in the click session. Plan measurement accordingly.

Why conversion tracking is the bottleneck for ChatGPT Ads

Every new ad channel has a moment where measurement catches up with inventory, and until it does, sophisticated advertisers stay away. ChatGPT Ads had that moment in mid-2026, and the before-and-after is instructive.

In April 2026, an r/PPC thread asking what the platform was like drew a description of the reporting: "Super basic ads manager. Can use it to build campaigns, ad sets and ads. Reporting wise all it has is spend, impressions and clicks" (r/PPC, April 2026). A month later, a thread titled "ChatGPT ads are here, but can we measure their performance?" opened with a problem that will be familiar to anyone who has launched on a young channel:

A SaaS friend of mine said that they got a wave of demo requests after their first trial of these ads, but analytics only labelled most of it as direct traffic. There was no clear source or data they could use for attribution. — r/PPC, "ChatGPT ads are here, but can we measure their performance?", May 26, 2026

The responses split into two camps that are both still worth understanding. One camp diagnosed a setup problem — "Sounds like your friend had a setup issue. You can absolutely put whatever tracking query parameters you want (e.g. UTMs) on paid ad URLs for it. Or install their pixel / CAPI and measure within their universe based on attribution." The other camp argued the problem is structural: "ChatGPT attribution is gonna look more like podcast ads or word of mouth than Google Ads. People see a recommendation, leave, come back later through search or direct traffic, and analytics just labels it as 'direct'."

Both are right, and the distinction organizes this entire guide. The setup problem is solvable today — this article is largely about solving it. The structural problem is not fully solvable, and pretending otherwise leads to bad decisions. AdVenture Media's attribution analysis suggests only about 40% of conversions attributable to ChatGPT ads occur in the immediate post-click session, with the remaining 60% arriving hours, days, or weeks later through paths with no traceable link back to the original interaction.

What changed is that OpenAI shipped real measurement infrastructure. As of the May 5 self-serve beta, a pixel and conversion events existed. By June 5, conversion optimization went live, which made accurate tracking not merely a reporting nicety but the input that drives bidding — see our companion guide on ChatGPT Ads conversion campaigns for how that bidding works. Get tracking wrong now and you are not just mis-measuring; you are actively training the auction on bad data.

Two thin filaments of blue light — one from a translucent glass browser pane, one from a glass server block — converging into a single luminous node on a white field, representing pixel and server-side conversion signals merging.
Two signal paths, one destination. The browser pixel and the Conversions API are complements, not alternatives.

Three ways to send a conversion — and when to use each

OpenAI provides three ingestion methods. They are not equivalent, and the right architecture for most advertisers uses at least two of them.

JavaScript pixelConversions APIImage tag
Runs whereBrowserYour serverBrowser (HTML only)
Captures opprefAutomaticallyYou must pass itNo — pass manually
Blocked by ad blockersYesNoSometimes
Can measure interactionsYes (clicks, submits)Yes (any backend event)No — page load only
BatchingAutomaticUp to 1,000 eventsOne event per request
Offline / CRM eventsNoYesNo
App eventsNoYes (mobile_app)No
Personal dataHashed onlyHashed onlyNever

The recommended architecture: pixel for browser-observable events (page views, add-to-cart, checkout starts) plus Conversions API for anything financially meaningful (orders, qualified leads, subscription starts, refunds you want excluded), deduplicated on a shared identifier. The image tag is a fallback for environments where you genuinely cannot run JavaScript — email landing pages, some CMS templates, AMP-like constraints — not a primary method.

Why server-side matters more here than on other channels

ChatGPT Ads sends traffic to your site from an app context, and a meaningful share of that traffic arrives on mobile browsers with aggressive tracking prevention. A browser-only setup loses those conversions silently — they simply never appear. Because oCPC uses conversions as its training signal, missing conversions do not just under-report performance; they teach the optimizer that a converting audience segment does not convert.

The JavaScript pixel: full implementation

The pixel loads an SDK called oaiq. Place the snippet in your <head>, replacing the placeholder with the Pixel ID from Ads Manager's conversions tab.

<script>
  (function (w, d, s, u) {
    if (w.oaiq) return;
    var q = function () { q.q.push(arguments); };
    q.q = [];
    w.oaiq = q;
    var js = d.createElement(s);
    js.async = true;
    js.src = u;
    var f = d.getElementsByTagName(s)[0];
    f.parentNode.insertBefore(js, f);
  })(window, document, "script", "https://bzrcdn.openai.com/sdk/oaiq.min.js");

  oaiq("init", { pixelId: "<YOUR-PIXEL-ID>" });
</script>

The pattern is a standard command queue: calls made before the SDK finishes loading are buffered and replayed. That means you can call oaiq("measure", …) immediately after init without waiting for the script.

Initialization options

Add debug: true during implementation to get console output confirming events fire:

oaiq("init", {
  pixelId: "<YOUR-PIXEL-ID>",
  debug: true
});

You can also pass user data at init time to improve match rates. Email and external IDs must be pre-hashed with SHA-256; geographic fields are sent raw:

oaiq("init", {
  pixelId: "<YOUR-PIXEL-ID>",
  user: {
    email_sha256: "<SHA256_HASH>",
    external_id_sha256: "<SHA256_HASH>",
    country: "US",
    city: "San Francisco",
    zip_code: "94107"
  }
});

What the SDK handles for you

Four things happen automatically, and knowing them prevents a lot of redundant engineering:

The __oppref cookie is the linchpin of the whole attribution model, and it is the value your server-side implementation will need. If you are building Conversions API calls, read that cookie and pass it as obref in the user object — this is how server events get stitched to the original ad click.

Firing an event

The signature is oaiq("measure", "<event_name>", <data>, <options>):

oaiq("measure", "order_created", {
  type: "contents",
  amount: 2599,           // $25.99 in minor units
  currency: "USD",
  contents: [{
    id: "sku_123",
    name: "Product name",
    content_type: "product",
    quantity: 1
  }]
}, {
  event_id: "order_12345"  // for deduplication
});
Monetary values are integers in ISO 4217 minor units $129.99 is 12999, not 129.99. This is the single most common implementation bug, and it fails silently — your revenue reporting is simply off by 100×. Always include currency whenever you send amount.

The event taxonomy and data shapes

There are 13 standard events. Each requires a specific type in its data object, and sending the wrong shape causes rejection.

EventData typeMeaning
order_createdcontentsA purchase is completed
checkout_startedcontentsA user starts checkout
items_addedcontentsItems added to a cart, bundle, or selection
contents_viewedcontentsA product, listing, article, or content unit is viewed
page_viewedcontentsA user lands on or views an important page
lead_createdcustomer_actionA lead form is submitted or contact requested
registration_completedcustomer_actionAn account or event registration finishes
appointment_scheduledcustomer_actionA meeting, demo, or consultation is booked
app_installedcustomer_actionAn app is installed (API only)
app_openedcustomer_actionAn app is opened (API only)
trial_startedplan_enrollmentA free trial starts
subscription_createdplan_enrollmentA paid subscription starts
customcustomAnything outside the standard taxonomy

The four data shapes

contents — commerce events carrying an item list:

{
  type: "contents",
  amount: 2599,
  currency: "USD",
  contents: [
    { id: "sku_123", name: "Product name", content_type: "product", quantity: 1 }
  ]
}

customer_action — leads and registrations, where amount is optional:

{
  type: "customer_action",
  amount: 5000,      // optional — e.g. estimated lead value
  currency: "USD"    // optional
}

plan_enrollment — trials and subscriptions:

{
  type: "plan_enrollment",
  plan_id: "pro_monthly",
  amount: 2000,
  currency: "USD"
}

custom — for anything the taxonomy does not cover:

oaiq("measure", "custom",
  { type: "custom" },
  { custom_event_name: "quote_requested" }
);

Custom event names must be 1–64 characters, alphanumeric plus underscores and dashes, starting and ending with a letter or number.

Custom events cannot drive bidding

Custom events are fine for measurement, but they cannot be used as an oCPC optimization goal. If a custom event represents your real business outcome, fire a standard event alongside it — lead_created next to quote_requested, for instance — so you have something to optimize toward. Discovering this after building your whole taxonomy on custom events is an expensive rebuild.

The Conversions API: server-side tracking

The Conversions API sends events from your infrastructure directly to OpenAI, bypassing the browser entirely. This is how you capture conversions that ad blockers eat, that happen after the session ends, or that occur in systems the browser never sees — a CRM stage change, a subscription renewal, an offline sale.

Endpoint and authentication

POST https://bzr.openai.com/v1/events?pid=<PIXEL-ID>

Authorization: Bearer <API-KEY>
Content-Type: application/json

Both the Pixel ID and API key are provisioned through Ads Manager's conversions tab. The request body takes an events array and an optional validation flag:

{
  "validate_only": true,
  "events": [ /* … */ ]
}

Use validate_only: true during implementation. It exercises the full validation path without recording data — the safest way to confirm your payloads are correct before sending live conversions.

Batch constraints

Up to 1,000 events per batch, and critically: the entire batch fails if one event fails. This is an all-or-nothing contract, and it has real architectural consequences. A single malformed event in a batch of 900 discards all 900. Validate each event before assembling the batch, and consider smaller batches so a poison record has a smaller blast radius.

Event object fields

FieldRequiredNotes
idYesUnique identifier; used with type for deduplication
typeYesStandard event name or custom
timestamp_msYesMilliseconds; within last 7 days, ≤10 min in future
dataYesMust match the event's required shape
custom_event_nameConditionalRequired when type is custom
source_urlConditionalRequired when action_source is web
action_sourceConditionalweb, mobile_app, offline, physical_store, phone_call, email, other
opprefNoNot auto-captured server-side — you must pass it
userNoIdentity matching object
opt_outNotrue excludes from future personalization

A complete server-side purchase event:

{
  "events": [{
    "id": "order_12345",
    "type": "order_created",
    "timestamp_ms": 1785000000000,
    "action_source": "web",
    "source_url": "https://example.com/checkout/complete",
    "user": {
      "obref": "<value of __oppref cookie>",
      "email_sha256": "<64-char lowercase hex>",
      "country": "US",
      "ip_address": "203.0.113.42",
      "user_agent": "Mozilla/5.0 …"
    },
    "data": {
      "type": "contents",
      "amount": 12999,
      "currency": "USD",
      "contents": [
        { "id": "sku_123", "name": "Product name", "content_type": "product", "quantity": 1 }
      ]
    }
  }]
}
The 7-day timestamp window is a hard constraint Events older than 7 days are rejected. If you import conversions from a CRM on a weekly or monthly batch cycle, that cadence will silently drop data. Move to a daily job, at minimum, for anything you intend to count.

App events

App lifecycle events are Conversions API only and require action_source: "mobile_app":

{
  "type": "app_installed",
  "action_source": "mobile_app",
  "data": { "type": "customer_action" }
}

Note that during the self-serve beta the Ads Manager data-source form offered Web, iOS, and Android types with only Web selectable — iOS and Android were marked "Coming soon." An advertiser documenting the beta called this "the single biggest limitation for anyone planning to run ChatGPT ads for an app." Verify current availability in your own account before planning an app-install campaign.

Deduplication: making pixel and API agree

If you run both the pixel and the Conversions API — which you should — the same purchase will be reported twice unless you deduplicate. The mechanism is simple and unforgiving: use the same identifier as the pixel's event_id and the API's id.

// Browser
oaiq("measure", "order_created",
  { type: "contents", amount: 12999, currency: "USD" },
  { event_id: "order_12345" }
);

// Server — same identifier in the `id` field
{ "id": "order_12345", "type": "order_created", … }

OpenAI's documentation states it directly: "If you send the same conversion from the pixel and the Conversions API, reuse the same value as the API id and pixel event_id." For custom events, custom_event_name must also match on both sides.

Two blue glass tokens bearing identical engraved identifiers travelling along separate paths and merging into one token at a glass junction on a white field, representing event deduplication.
One order, two transmission paths, one shared identifier. Without the shared ID, you count the same revenue twice.

Choosing the identifier

The identifier must be stable across both systems and unique per conversion. Your order ID or transaction ID is almost always the right choice, because both the browser (on the confirmation page) and your backend (in the order record) know it.

What does not work: timestamps, random values generated independently on each side, session IDs, or anything the server cannot reconstruct. If the browser generates a UUID that the server never learns, the two events will never match and you will double-count every order.

For lead forms, use the CRM record ID if the browser can see it after submission; otherwise a hash of email plus form ID plus a coarse timestamp is a workable deterministic fallback — as long as both sides compute it identically.

Identity matching and hashing rules

The user object improves attribution by giving OpenAI more ways to connect a conversion back to an ad interaction. The rules about what gets hashed and what does not are strict, and getting them backwards means either rejected events or a privacy incident.

FieldFormatHashed?
obrefFirst-party cookie value, unmodifiedNo
email_sha256SHA-256, lowercase 64-char hexYes
external_id_sha256SHA-256, lowercase 64-char hexYes
countryISO 3166-1 code, e.g. USNo
cityMax 128 chars, lowercased, trimmedNo
zip_codeMax 32 chars; letters, numbers, spaces, hyphensNo
ip_addressIPv4 or IPv6No
user_agentNon-empty browser stringNo

The governing rule from OpenAI's documentation: send raw values for geographic, IP, and user-agent fields; hash email and external IDs; and never send raw emails, IDs, or phone numbers.

Normalizing before hashing

Hashing is only useful if both sides derive the same hash from the same person, which means normalization must be deterministic. Before hashing an email:

  1. Trim leading and trailing whitespace.
  2. Lowercase the entire address.
  3. Hash with SHA-256.
  4. Render as lowercase hexadecimal — 64 characters.
# Node
crypto.createHash("sha256")
      .update(email.trim().toLowerCase())
      .digest("hex");

# Python
hashlib.sha256(email.strip().lower().encode()).hexdigest()

A frequent and subtle error: hashing an already-hashed value. If your CRM stores hashed emails, do not hash again. Another: uppercase hex output. The spec asks for lowercase, and a mismatch here means the identity simply never matches.

The image tag fallback

For environments where JavaScript cannot run, OpenAI provides an image-tag method that fires a 1×1 pixel on page load.

<img
  src="https://bzr.openai.com/v1/sdk/events?pid=<PIXEL-ID>&event=page_viewed&data[type]=contents"
  width="1" height="1" style="display:none" alt=""
/>

Required parameters are pid, event, and data[type]. Optional ones include event_id, oppref, custom_event_name, and event-specific data[<field>] values. A successful request returns 200 image/gif, which you can confirm in your browser's network panel.

The limitations are significant and worth stating plainly:

That last point is the practical killer. Without oppref, attribution back to the ad click is materially weaker. Treat the image tag as a genuine last resort — wrapped in <noscript> as a fallback beneath a working pixel, rather than as your primary method.

Consent

The SDK exposes a consent command. The correct pattern for a consent-gated jurisdiction is to disable tracking, initialize, and enable only after the user grants permission:

oaiq("consent", false);                          // disable
oaiq("init", { pixelId: "<YOUR-PIXEL-ID>" });
oaiq("consent", true);                           // enable after consent

Server-side, the opt_out field on an event — set to true — excludes that event from future personalization while still allowing it to be counted. These are different controls for different purposes; do not substitute one for the other.

Content Security Policy

If you run a CSP, three directives need entries or the pixel fails silently:

DirectiveSourcePurpose
script-srchttps://bzrcdn.openai.comLoad the SDK
connect-srchttps://bzr.openai.comSend events
img-srchttps://bzr.openai.comFallback delivery

A missing connect-src entry is a classic cause of "the pixel is installed but no conversions appear" — the script loads fine, and every event is then blocked by the browser at send time. Check the console for CSP violations before assuming a configuration problem in Ads Manager.

Implementing through Google Tag Manager

Most teams will deploy this through a tag manager rather than editing templates. There is no official OpenAI template at time of writing, so you are building custom HTML tags.

  1. Base tag. Create a Custom HTML tag containing the loader snippet and oaiq("init", …). Fire it on All Pages, with tag firing priority set high so it initializes before event tags.
  2. Set "Once per page" in advanced settings to prevent double-initialization on sites with history-change triggers.
  3. Event tags. One Custom HTML tag per conversion event, each calling oaiq("measure", …) with values pulled from data layer variables.
  4. Pass the dedup ID. Push your order ID into the data layer at purchase and read it into event_id. If it is absent, deduplication fails — so add a data layer check rather than sending an undefined value.
  5. Convert currency to minor units in the variable, not in the tag. A custom JavaScript variable returning Math.round(price * 100) keeps the conversion in one auditable place.
  6. Consent Mode. If you use GTM consent mode, gate the tag on your analytics or marketing consent type and call oaiq("consent", true) on consent update.

For the server-side leg, the Conversions API call belongs in your backend — the order-confirmation handler, or a queue consumer subscribed to order events — not in a GTM server container tag unless you have already standardized on server-side GTM. The reason is the 7-day timestamp window and all-or-nothing batching: those require retry logic and dead-lettering that a tag manager is a poor place to implement.

Platform-specific implementation notes

The snippets above are platform-agnostic. Here is where each major stack puts them, and the gotcha that most often bites on each.

Shopify

Add the base pixel to theme.liquid in the <head>, or as a Custom Pixel under Settings → Customer events if you want it sandboxed and consent-aware. Fire order_created from the order status page using the checkout object.

The gotcha: Shopify prices are decimal strings ("129.99") while OpenAI wants integer minor units (12999). Convert explicitly and round — floating-point multiplication on prices produces values like 12998.999999, which will be rejected or silently truncated. Also note that themes render the order status page outside your normal template chain, so a pixel in theme.liquid alone will not fire on purchase. Ecommerce sellers should also read our product feed guide, since feed campaigns are a separate program with separate eligibility.

WordPress and WooCommerce

Inject the base tag via wp_head in your child theme's functions.php — not the parent theme, which updates will overwrite. Hook order_created to woocommerce_thankyou, and prefer the server-side Conversions API for the purchase event, firing from the same hook. WooCommerce order objects give you the order ID for event_id and the total for amount.

The gotcha: caching plugins routinely strip or defer inline scripts. If conversions stop after a performance-plugin update, check whether the pixel is being deferred past the point where your event calls execute.

Webflow and other no-code builders

Base tag goes in Project Settings → Custom Code → Head Code. Event calls go in per-page custom code or on form-submit handlers. Because most no-code platforms give you no server, the Conversions API is not available without an intermediary — a serverless function or an automation platform receiving a form webhook and posting to bzr.openai.com.

The gotcha: native form success states often do not trigger a page load, so a page-load-based event will never fire. Bind to the form's success event explicitly.

HubSpot, Salesforce, and CRM-driven funnels

This is where server-side tracking stops being optional. The valuable event is not the form submission — it is the moment a lead becomes qualified, which happens days later inside the CRM, in response to human judgement the browser never observes.

The pattern: capture the __oppref cookie value in a hidden form field at submission, store it on the CRM record, and when the record reaches a qualifying stage, fire a Conversions API event using that stored value as obref. This is what lets you optimize toward qualified pipeline rather than raw form fills.

The gotcha: the 7-day timestamp window. If a lead qualifies on day 10, you cannot backdate the event to the original submission — the timestamp will be rejected. Send it with the qualification timestamp instead, and accept that the event's time reflects when the outcome was confirmed rather than when the click happened.

Hidden field pattern

Capturing __oppref for later CRM use:

document.querySelector('[name="oai_ref"]').value =
  document.cookie.match(/__oppref=([^;]+)/)?.[1] || '';

The attribution gap nobody warns you about

Even with flawless implementation, ChatGPT Ads will under-report. This is not a bug in your setup; it is a property of how people use a conversational assistant.

Early advertiser analysis indicates roughly 40% of ChatGPT-ad-attributable conversions happen in the immediate post-click session, leaving about 60% to arrive later through channels with no traceable link back (AdVenture Media's attribution analysis). The r/PPC thread quoted at the top of this guide captured the pattern precisely: users "see something in ChatGPT, come back later through Google, type the brand directly, switch devices, send links around, etc so it ends up looking like random direct traffic."

A translucent blue glass bridge on a white field with a visible gap near its far end, a thin beam of light arcing across the gap to reach the other side — representing conversions that complete outside the tracked session.
The tracked path captures roughly 40% of the outcome. Measurement design has to account for the rest.

There is a further structural constraint: OpenAI provides aggregated performance data only. Advertisers see views and clicks; they do not see chats or user-level details. One r/PPC commenter drew the right operational conclusion from this — measure it "like an emerging intent channel, not a mature paid channel," using a dedicated landing page, strict UTMs, a CRM source field, hidden form fields, self-reported attribution, and downstream quality metrics. Their closing observation is the one to keep: "If it shows as direct, that doesn't mean it failed… it means the tracking path is leaky."

A measurement stack that survives the gap

The QA checklist

Run this before launching a conversion campaign, and again after any deploy that touches checkout, forms, or consent.

Failure modes that silently corrupt your data

Conversion tracking rarely fails loudly. These are the failures that produce plausible-looking but wrong numbers — the dangerous kind, because they inform bidding.

FailureSymptomFix
Missing event_idConversions roughly doubleAdd shared ID to both paths
Amount in major unitsRevenue 100× too highSend integer minor units
CSP missing connect-srcPixel loads, zero eventsAllow bzr.openai.com
Batch import older than 7 daysSilent event lossMove to a daily job
One bad event in a batchAll 1,000 rejectedValidate per-event; smaller batches
Uppercase or double hashingMatch rates collapseLowercase hex, hash exactly once
Custom event as oCPC goalEvent not selectableFire a standard event alongside
Consent blocks everythingConversions drop sharply after a banner changeVerify both consent paths; add server-side
Checkout redesign drops tagGradual unexplained declineWeekly automated verification

The last row deserves emphasis. OpenAI's optimization guidance says to "keep conversion tracking healthy," noting that "incomplete or misconfigured tracking can make reporting and optimization less effective." In practice, tracking decays. A template change, a consent-tool update, a new checkout flow — each can sever the pipe without a single error appearing in Ads Manager. Schedule verification; do not rely on noticing.

How AI search monitoring improves the whole strategy

Conversion tracking answers "did this click pay off?" It cannot answer the two questions that matter more in an AI-mediated market: which conversations is my brand present in at all, and what is being said about me when I am not paying? That is the job of an AI search monitoring platform, and connecting the two datasets is where the real strategic advantage sits.

1. It substitutes for the search-term report you do not get

ChatGPT Ads has no search-term report and no placement report — a limitation advertisers complain about persistently. Monitoring which prompts return your brand, and which return competitors, is the nearest available equivalent. Those prompts are direct evidence for writing better context hints, which are the only targeting lever the platform gives you.

2. It separates a visibility problem from a conversion problem

When a campaign underperforms, there are two distinct diagnoses with opposite remedies. If you are absent from the relevant conversations, that is a visibility and content problem. If you are present and people are not converting, that is a landing-page and offer problem. Conversion data alone cannot distinguish them; conversion data plus visibility data can.

3. It stops you paying for what you already earn

If ChatGPT already recommends you organically for a given class of question, aggressive bidding there partly buys traffic you would have received anyway. Reallocating that budget toward contexts where a competitor is named and you are not is one of the few unambiguous efficiency wins available in this channel.

4. It turns conversion evidence into an SEO and AEO roadmap

This is the compounding one. Your conversion data tells you which claims, offers, and audiences actually produce revenue. Your visibility data tells you which of those high-converting contexts you have no organic presence in. The intersection is a ranked content brief backed by paid proof of demand — a far stronger prioritization signal than keyword volume, and one built specifically for a world where the "search result" is a generated answer. The structural work that follows — clear comparison pages, well-marked-up entities, retrievable factual content — is what earns citations, and citations are what make the next ad click cheaper and more likely to convert.

The practical loop: monitor the prompts that matter to your category, run oCPC campaigns with clean tracking against them, read cost per conversion by context, then feed both the winners and the absences into your content roadmap. Paid buys presence today; the content earns it permanently. Our guides on appearing in ChatGPT answers, visibility metrics, and buyer-evaluation visibility cover that side of the discipline.

The strategic summary

Conversion tracking makes ChatGPT Ads accountable. AI search monitoring makes it strategic. One tells you what a click was worth; the other tells you which conversations you are missing entirely — and in a channel with no search-term report, that second dataset is not a luxury, it is the missing half of the reporting.

ChatGPT Ads conversion tracking: frequently asked questions

How do I set up ChatGPT Ads conversion tracking?

Provision a Pixel ID in Ads Manager's conversions tab, add the oaiq loader snippet to your site's <head>, call oaiq("init", { pixelId: … }), then fire events with oaiq("measure", "<event>", { … }). For anything transactional, add the server-side Conversions API and deduplicate with a shared event_id.

What is the ChatGPT Ads pixel called?

The SDK is oaiq, loaded from https://bzrcdn.openai.com/sdk/oaiq.min.js. Events are sent to https://bzr.openai.com. Both hosts need to be allowed in your Content Security Policy.

What is the ChatGPT Ads Conversions API endpoint?

POST https://bzr.openai.com/v1/events?pid=<PIXEL-ID> with a bearer token and JSON body. It accepts up to 1,000 events per batch, and the entire batch fails if a single event fails validation.

Do I need both the pixel and the Conversions API?

For anything with real revenue attached, yes. The browser pixel loses events to ad blockers and tracking prevention; the server API cannot see browser-only interactions. Running both with matched event_id and id values gives the most complete picture without double counting.

How does deduplication work between the pixel and the API?

Use the same value as the pixel's event_id and the API's id for the same conversion — an order or transaction ID is usually ideal. For custom events, custom_event_name must match on both sides as well.

What is oppref and the __oppref cookie?

oppref is OpenAI's privacy-preserving attribution identifier. The JavaScript pixel captures it from the landing page URL automatically and stores it in a first-party cookie named __oppref. Server-side calls must pass it explicitly as obref inside the user object.

How should I format amounts and currency?

Monetary values are integers in ISO 4217 minor units — $129.99 is 12999, not 129.99. Always include currency whenever you send an amount. Sending major units is the most common implementation bug and it fails silently.

Which fields must be hashed?

Hash email and external IDs with SHA-256 as lowercase 64-character hex, derived from trimmed and lowercased input. Send country, city, ZIP, IP address, and user agent as raw values. Never send raw emails, IDs, or phone numbers.

Can I import historical conversions?

Only recent ones. Event timestamps must fall within the last 7 days and no more than 10 minutes in the future. Weekly or monthly CRM batch imports will silently drop data — move to a daily job at minimum.

Why does ChatGPT Ads under-report my conversions?

Partly implementation, partly structure. Published analysis suggests only around 40% of ChatGPT-attributable conversions happen in the click session; the rest arrive later via direct or branded search. Pair platform tracking with UTMs, self-reported attribution, CRM outcomes, and holdout tests.

Can I use a custom event to optimize a conversion campaign?

No. Custom events can be measured but cannot be an oCPC optimization goal. If a custom event represents your real outcome, fire a standard event such as lead_created alongside it so the campaign has something eligible to optimize toward.

Does ChatGPT Ads support app install tracking?

The Conversions API defines app_installed and app_opened with action_source: "mobile_app". During the self-serve beta, however, Ads Manager data sources offered Web, iOS and Android with only Web selectable. Confirm current availability in your own account before planning app campaigns.

Sources and further reading

Want your conversion tracking audited before you scale spend?

30 minutes with Tarun. We will walk your pixel and Conversions API setup, check deduplication and hashing, and find the events that are silently failing before they distort your bidding.

Book a discovery call
Tarun Kapoor, founder of Context Hints, seated at a wooden desk with a soft city light behind him.
Tarun Kapoor
Founder & CEO, Context Hints

Twelve years of media buying across GroupM, WPP, Ogilvy & Mather, and Neil Patel Digital. Has personally owned media for Nestlé, Sage, Qualcomm, Aetna, Weight Watchers, Chubb and Novotel.