Skip to content
ceaksan

OpenAI Ads Measurement Architecture: How the Pixel and Conversions API Work Together

OpenAI Ads conversion measurement rests on two layers: the browser JavaScript pixel and the server-side Conversions API. This post explains what each layer captures, why both are needed, and how they reconcile the same conversion without double-counting, conceptually, without diving into setup steps.

Jun 7, 2026 5 min read
TL;DR

OpenAI Ads conversion measurement rests on two layers: a JavaScript pixel that runs in the browser and a Conversions API that sends events from the server. The pixel captures on-site events after the click; the Conversions API carries offline and server-originated conversions without being affected by ad blockers and consent loss. OpenAI describes the Conversions API as a more reliable source than the pixel alone and recommends using both together. When the same conversion is sent from both sides, it is deduplicated through a shared event identifier and the same Pixel ID, so double-counting is avoided.

OpenAI Ads conversion measurement rests on two layers: a JavaScript pixel that runs in the browser and a Conversions API that sends events from the server123. The two share the same conversion taxonomy but operate in different places, with different strengths. This post does not cover setup steps; it offers a conceptual map of what each layer captures, why both are needed, and how they reconcile the same conversion without counting it twice.

The technical side of setup is the subject of separate posts: pixel installation, server-side sending with the Conversions API, and the dedup mechanics. The goal here is to settle the mental model before diving into those parts.

Two Layers, One Conversion

A conversion, say an order, can be reported to OpenAI through two separate paths:

  • JavaScript pixel: The oaiq SDK that runs in the browser. It loads the core code when the page loads and sends the event through a measure call when the conversion happens2.
  • Conversions API: A server-to-server endpoint that sends events directly to OpenAI. It runs only on the server side3.

Both use the same standard event names (page_viewed, contents_viewed, items_added, order_created, subscription_created, and so on) and share the same data shapes1. So they speak the same conversion language; the difference is where and under what conditions the event is sent.

What Each Layer Captures

The two layers have different strengths. The pixel sees the live interaction in the browser; the Conversions API carries everything the browser cannot see or cannot reliably send.

DimensionJavaScript pixelConversions API
Where it runsBrowser (client-side)Server (server-side)
Typical usePage and content views, cart, orderOffline/CRM conversions, cases the pixel cannot fire
oppref identifierAuto-captures from the landing URL, stores in the __oppref cookieNot auto-captured; the identifier is carried manually
Ad blocker impactCan be blockedUnaffected
ReliabilityLowerDescribed by OpenAI as more reliable

A practical advantage of the pixel is that it auto-captures OpenAI’s privacy-preserving identifier, oppref, from the landing page URL and stores it in a first-party cookie; this identifier is carried on later page views2. On the Conversions API side, this identifier does not arrive automatically; when sending from the server, you capture and pass it yourself3.

Why Both Layers: The Measurement Gap

Browser-based measurement alone systematically loses a portion of conversions. When ad blockers, consent refusal, and the cookie restrictions of browsers like Safari/iOS stack up, client-side measurement turns into a sample, not a count. This is not a problem specific to OpenAI Ads; I covered in detail why client-side tracking falls short on its own and how consent decisions affect measurement earlier.

The Conversions API exists precisely to close this gap. Events sent from the server are outside ad blockers’ field of view, do not need third-party cookies, and the server decides what data is sent and when. OpenAI describes the Conversions API as a more reliable source than the pixel and recommends using it when possible3. When the two layers work together, measurement moves closer to a count than to an incomplete sample.

How the Two Layers Reconcile Without Overlap

When the two layers send the same conversion, a natural risk arises: double-counting. OpenAI solves this with a deduplication (dedup) mechanism. If the same conversion comes from both the pixel and the Conversions API, the same value is used on both sides: event_id on the pixel side, id on the server side, and the same Pixel ID on both23. For custom events, the match is made via custom_event_name rather than the event name.

The simplest form of the same order sent from both layers looks like this. In the browser with the pixel:

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

On the server with the Conversions API, with the same identifier:

curl -X POST "https://bzr.openai.com/v1/events?pid=<PIXEL-ID>" \
  -H "Authorization: Bearer <API-KEY>" \
  -H "Content-Type: application/json" \
  --data '{
    "events": [{
      "id": "order_12345",
      "type": "order_created",
      "timestamp_ms": 1773892800000,
      "action_source": "web",
      "source_url": "https://shop.example.com/checkout/confirmation",
      "data": { "type": "contents", "amount": 2599, "currency": "USD" }
    }]
  }'

The bridge is a single value: event_id on the pixel equals id on the server (order_12345), and the same Pixel ID is used on both. From this match, OpenAI understands the two records are one conversion. That is why the identifier must be derived from the same stable value (an order number, for example) on both sides. Full setup and common mistakes are the subject of separate posts; the example here only shows how the match is set up.

Set up correctly, there is no double-counting; set up wrong, reported conversions inflate and ROAS breaks. The one critical point is that the identifier is genuinely stable and consistent across both sides.

Data Flow and the Standard Event Taxonomy

The common language the two layers share is the standard event taxonomy. Every event has a type field and a data shape appropriate to that type: content and order events use contents, lead and registration events use customer_action, and subscription and trial events use plan_enrollment1. Monetary values are sent as integers in ISO 4217 minor units; for example, $129.99 is sent as 129991.

Using this taxonomy correctly requires both knowing which event to send when and sending the value in the right form. Which case each event maps to, and the common mistakes in sending value, belong to the application posts, not the conceptual map.

The Identity and Privacy Layer

Both layers can carry optional identity fields to match a conversion to a user. These fields are sent hashed, not raw: email and external ID are sent with SHA-256, after being lowercased and trimmed; raw email, raw external ID, and phone numbers are never sent3. Geographic data, IP, and user-agent can be sent raw.

This is a deliberate privacy design: it aims to preserve match quality while not sharing personal data in the clear. Hashing, the oppref identifier, opt_out, and the GDPR/KVKK framing will be covered in depth in a separate post.

Same principle, new channel

The OpenAI Ads Conversions API rests on the same logic as Meta CAPI and the GA4 Measurement Protocol: a server-side measurement layer that is more reliable and independent of browser restrictions. For a team with a server-side tracking setup, OpenAI Ads measurement is not a new paradigm but another application of a familiar pattern.

Where to Start

Once the conceptual model is settled, implementation follows. A practical starting order: first set up the browser side and capture core conversions with the pixel, then add the Conversions API and send critical conversions from the server too, closing the measurement gap, and finally reconcile the two layers with dedup. OpenAI’s own advice is to always use the pixel in the browser and to call the server API from the server, not from page code2.

Setting up the two layers separately is a technical job; but setting them up without knowing which layer solves what leads to measurement that is incomplete or double-counting. This map explains why the next steps come in that order.

Footnotes

  1. Supported events (OpenAI Developers) — the standard event taxonomy, data shapes, and the minor units rule (“12999 for $129.99 with currency: \"USD\"”). 2 3 4
  2. JavaScript Pixel (OpenAI Developers) — oaiq SDK install (bzrcdn.openai.com), measure calls, oppref auto-capture from the landing URL and the __oppref cookie, browser/server dedup via event_id, “Always use the pixel on the browser. Do not call the server conversions API directly from page code.” 2 3 4 5
  3. Conversions API (OpenAI Developers) — the server-side endpoint (bzr.openai.com/v1/events?pid=), dedup (API id = pixel event_id + same Pixel ID, custom_event_name for custom events), identity matching (email_sha256/external_id_sha256, never raw email/external id/phone; geo/IP/user-agent raw), “The Conversions API is a more reliable tracking source than the pixel alone.” 2 3 4 5 6
Key Takeaways
  • 01 OpenAI Ads measurement is two layers: the browser pixel (oaiq) and the server-side Conversions API. Both share the same conversion taxonomy.
  • 02 The pixel auto-captures client-side events and the oppref identifier; the Conversions API carries offline/CRM events and cases the pixel cannot fire, unaffected by ad blockers.
  • 03 OpenAI describes the Conversions API as more reliable than the pixel; using both together approaches a census, while browser-only measurement stays a sample.
  • 04 If both sides send the same conversion, dedup is done via a shared event_id/id + the same Pixel ID; for custom events, custom_event_name matches.
  • 05 This architecture rests on the same principle as Meta CAPI and the GA4 Measurement Protocol; for a team with a server-side tracking setup, it is a familiar pattern.
Frequently Asked Questions (FAQ)
+ Why does OpenAI Ads measurement consist of two layers?

Browser-based measurement alone loses a portion of conversions to ad blockers, consent refusal, and browser cookie restrictions. The JavaScript pixel captures client-side events; the Conversions API compensates for those losses by sending events from the server and carries offline conversions. OpenAI describes the Conversions API as a more reliable source than the pixel alone and recommends using both together.

+ What is the difference between the pixel and the Conversions API?

The pixel runs in the browser, captures on-page events (page_viewed, contents_viewed, order_created, etc.), and auto-captures the oppref identifier from the landing page URL. The Conversions API runs server-to-server, sends offline and CRM events and cases the pixel cannot fire, and is unaffected by ad blockers and third-party cookie restrictions.

+ Will the same conversion be counted twice?

If both the pixel and the Conversions API send the same conversion, it is deduplicated using a shared event identifier (event_id on the pixel side, id on the server side) and the same Pixel ID. For custom events, the match is made via custom_event_name. Set up correctly, there is no double-counting; the details of the dedup mechanics are the subject of a separate post.

+ Is this architecture similar to Meta CAPI or GA4?

Yes. The OpenAI Ads Conversions API rests on the same logic as Meta CAPI and the GA4 Measurement Protocol: a server-side measurement layer that is more reliable and independent of browser restrictions. For a team with a server-side tracking setup, OpenAI Ads measurement is not a new paradigm but another application of a familiar pattern.