Skip to main content

Reading Cookie Values

The pixel stores campaign attribution in first-party cookies on your domain. You may want to read those values yourself — to submit them with your own form, show them in a debug panel, or pass them to another system.

The pixel does not expose a cookie reader

There is no flexGetCookie or similar. The pixel reads its own cookies internally, but that helper is not part of the public API, so you read them with ordinary browser JavaScript as shown below.

For the most common case — putting a value into a form field — you do not need to do this at all. Use flexFieldWatch, which handles the timing for you.

A reader you can copy

function readFlexCookie(name) {
const match = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([.*+?^${}()|[\]\\])/g, '\\$1') + '=([^;]*)'));

if (!match) {
return null;
}

try {
return decodeURIComponent(match[1]);
} catch {
// decodeURIComponent throws URIError on a malformed value (a stray "%", or a cookie truncated
// by another script). Treat it as absent rather than letting it break the caller.
return null;
}
}

readFlexCookie('gclid'); // "Cj0KCQjw..." or null
readFlexCookie('utm_source'); // "google" or null

If you already use a cookie library such as js-cookie, use it instead — Cookies.get('gclid') does the same thing and handles decoding for you.

Decode the value

The pixel writes cookies URL-encoded. Reading document.cookie directly gives you the raw encoded string, so a landing page URL comes back as https%3A%2F%2Fexample.com%2Flp%3Fa%3D1 rather than the real URL.

Always decodeURIComponent the value, as the snippet above does. Skipping it is the most common mistake here, and it usually shows up as mangled values in whatever system you forward them to.

What you can read

Every cookie in the cookies table is readable this way. The ones usually wanted:

CookieTypical use
gclid, gbraid, wbraid, fbclid, msclkid, twclid, li_fat_id, ttclid, rdt_cidSubmit the click id with your own lead form
utm_source, utm_medium, utm_campaign, utm_term, utm_contentRecord which campaign produced a lead
firstTouchUrl, lastTouchUrlWhere the visitor entered and where they converted
originalReferrer, lastReferrerThe referring domain

Cookie names have no prefixgclid is stored as gclid.

If your site also sets a cookie with one of these names, the two can coexist: cookies are keyed on name plus Domain and Path, so gclid at .example.com/ and gclid at www.example.com/checkout are two different cookies both present in document.cookie. A reader like the one above returns the first match in that string, and the order is decided by the browser rather than by which you wrote last — so you cannot rely on getting the one you meant. Avoid reusing the pixel's cookie names; see the warning in the cookies table.

Reading at the right time

A cookie only exists once the pixel has written it, which happens as the pixel initialises. Reading in a script that runs before the pixel returns null for values that will exist a moment later.

Two safe patterns:

On submit. Read inside your submit handler rather than at page load. By the time a visitor submits, the pixel has long since initialised.

const form = document.querySelector('#contact-form');

form.addEventListener('submit', () => {
// Scoped to this form: a page with more than one form can have several inputs named "gclid",
// and document.querySelector would return whichever appears first in the document.
const field = form.querySelector('input[name="gclid"]');

if (field) {
field.value = readFlexCookie('gclid') ?? '';
}
});

After initialisation. window.flexInitialized is true once flexInit has completed, so you can wait for it:

function whenPixelReady(callback, { maxAttempts = 20, interval = 300, onTimeout } = {}) {
if (window.flexInitialized) {
callback();

return;
}

// Bounded, like the pixel's own flexFieldWatch (20 attempts at 300ms). An unbounded poller would
// keep running for the life of the page when flexInit never happens — which is exactly the case
// you are most likely to hit.
if (maxAttempts <= 0) {
onTimeout?.();

return;
}

setTimeout(() => whenPixelReady(callback, { maxAttempts: maxAttempts - 1, interval, onTimeout }), interval);
}

whenPixelReady(() => {
console.log('campaign:', readFlexCookie('utm_campaign'));
});

Expect null

Every one of these can legitimately be absent, and your code should treat that as normal rather than an error:

  • A click id is only present when the visitor arrived from that platform's ad. Most visitors have none, and never more than one or two.
  • UTM parameters only exist if the landing URL carried them.
  • Values expire at different rates. A click id lasts 30 days, everything else in the table 90, and tnt_id 100 — so a returning visitor may have some and not others.
  • Cookies are scoped to your registrable domain. Every subdomain of it shares them — www.example.com and app.example.com read the same values — but an unrelated registrable domain shares nothing, so a visitor who crossed to one mid-funnel has none. See Sites spanning several domains.

Send an empty string rather than the literal "null" or "undefined" when a value is missing. Those strings are a common cause of unattributable conversions, because they look like real values downstream.

Do not write these cookies yourself

Read them freely; do not set or overwrite them. The pixel maintains first-touch values by writing them only when absent, and overwriting one from your own code breaks that. If you need your own value persisted, use your own cookie name.