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.
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.
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:
| Cookie | Typical use |
|---|---|
gclid, gbraid, wbraid, fbclid, msclkid, twclid, li_fat_id, ttclid, rdt_cid | Submit the click id with your own lead form |
utm_source, utm_medium, utm_campaign, utm_term, utm_content | Record which campaign produced a lead |
firstTouchUrl, lastTouchUrl | Where the visitor entered and where they converted |
originalReferrer, lastReferrer | The referring domain |
Cookie names have no prefix — gclid 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_id100 — 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.comandapp.example.comread 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.
Related
- Pixel Installation — setup, the full cookies table, and the config reference
flexFieldWatch— let the pixel fill a form field for you instead of reading manuallyflexUrlParamSync— carry attribution to another domain