Why a conversion did not count
Read the XD-reason header (or the pixel's debug=1 JSON) to see why a pixel or server-to-server conversion was not counted, what each reason code means, and why repeated test conversions vanish.
A conversion pixel or postback that “did nothing” almost always says why on its response. This page lists the reason codes the ad server returns, how to read them, and why repeated test conversions can vanish. To set up the tracking in the first place, see Get the pixel and S2S tracking code; to run a test conversion, see Test a conversion.
Who this is for
Anyone integrating or testing conversion tracking — network managers and the advertiser’s own
developers. Reading the response needs no permission in Epom: it is whatever your browser, your
server or curl receives from the ad server.
Read why a conversion did not count
The pixel always answers 200 OK with a 1×1 GIF, so nothing in the picture tells you whether
the conversion landed. The outcome is on the response instead, in an XD-reason header —
present on every conversion response, success included, from the cookie pixels
(/action.gif, /action.js) and from the server-to-server endpoint (/at) alike.
On the image pixel you can also add debug=1 to the URL and get the outcome as a small JSON
body instead of the image (any non-empty value works; the JS pixel and the S2S endpoints do not
serve it):
curl -s 'https://ads.example.com/action.gif?key=<key>&debug=1'
{"reason":"no-cookie","counted":false,"description":"No click or view cookie reached the ad server - the browser dropped it, or this user never saw the ad."}
counted is true only when the conversion was added to the advertiser’s totals — which is
ok and nothing else. The codes you meet while integrating:
XD-reason | What happened |
|---|---|
ok | Counted. |
expired | A click or view matched but fell outside the action’s attribution window. Recorded as a diagnostic, not counted. |
no-cookie | No click or view cookie arrived — wrong browser, cookies cleared, or a consent tool dropped it. |
no-match | Cookies arrived, but none of them belongs to a creative of this campaign — or of any campaign sharing its conversion-tracking alias. Usually the tag sits on a page reached without seeing the ad. |
already-tracked | The same click or view already produced this conversion; the repeat is deduplicated. On the pixel the check is server-side as well as cookie-side, so it holds even when the visitor’s browser could not store the tracking cookie. Clicking the ad again starts a new chance to convert. On a postback the same reason means something narrower — see the corner case on repeats below. |
window-disabled | That event type has a zero-length window on this action, so it never converts. |
cookie-data-missing | A cookie matched, but it carried no ad data to build the conversion from. |
key-unknown | The key= value does not resolve to a conversion action. key= is the pixel parameter, so this is a stale or mistyped copy of a pixel code block — a postback never sends it. |
action-data-missing | The request named no conversion the ad server could read — no subId, no key + click-id pair, or a value in one of them it could not decode. Almost always a truncated postback URL, or a placeholder that resolved to nothing so the parameter arrived empty; a double-encoded click id does it too. Check what your server actually sent, not what the template says. |
The rest all say the same thing in different words — the entity named by the cookie or the click id is not live in ad serving: deleted, or with conversion tracking switched off. A cache reload lags a settings change by up to ten minutes, so one of these right after an edit may simply be the ad servers not having caught up.
XD-reason | Which entity |
|---|---|
pair-unknown | The key and the click id both parsed, but together they name no conversion action. |
action-unknown | The conversion action. |
banner-unknown | The creative — unknown, or conversion tracking is off for it. |
campaign-unknown | The campaign — unknown, or conversion tracking is off for it. |
advertiser-unknown | The advertiser. |
placement-unknown | The placement. |
country-unknown | Not an entity: the country of the conversion could not be resolved from the request. |
error | Not an entity either: the ad server failed while processing the conversion. Worth reporting. |
Two more headers ride along on the same response:
| Header | What it says |
|---|---|
XD-attribution | Which cookie jar answered, as an attribution type (POST_CLICK, POST_VIEW, and the expired variants). Set on the cookie pixels only, and worth reading when a landing page’s click id went missing: the pixel reads both jars and a click beats a view, so a conversion you assumed was post-view is frequently a post-click whose easclid= never arrived. |
XD-warning | A parameter the ad server did not use on a conversion it otherwise counted. Today the only value is payout-word-refused. Unlike XD-reason these accumulate — one response can carry several. |
Outcomes that predate the reason header still emit their original single-purpose flag alongside
it, valued 1, for anything already parsing them:
XD-reason | Legacy flag |
|---|---|
action-data-missing | XD-data-key |
pair-unknown | XD-pair |
action-unknown | XD-action |
placement-unknown | XD-placement |
banner-unknown | XD-banner |
campaign-unknown | XD-campaign |
advertiser-unknown | XD-advertiser |
country-unknown | XD-countryData |
error | XD-exc |
Reading these headers from JavaScript on the advertiser’s own page works: all of them, XD-warning
included, are named in Access-Control-Expose-Headers. A plain new Image().src = … pixel cannot
read response headers at all — use fetch(), or just debug=1 from the command line.
Corner case: on the server-to-server endpoints the HTTP status is almost the same answer —
/atreplies404when nothing was logged and200when something was. The exception is the one that matters: an expired conversion logs a diagnostic row, so it answers200whileXD-reason: expiredsays it was not counted. Read the header, not the status.
Corner cases and gotchas
Corner case: repeated test conversions from the same machine can silently disappear. The pixel counts one conversion per click or view: fire it twice for the same click and the second is a duplicate, never shows up in reports, and answers
already-tracked. A “Duplicate conversion window (per IP)” on the action removes further repeats from the same address. Wait out the window, click the ad again, or test from another machine.Server-to-server postbacks are different: there the only identity is the one your partner sends, so repeats are counted unless you tick Reject duplicate conversions on the action and your network runs duplicate detection — the checkbox is not even offered when it does not.
The landing-page snippet posts to the same endpoint but refuses the repeat before it leaves the browser, so a second fire of the same event on the same click is not counted whatever those two settings say. See Install the landing-page helper.