Docs·Usage
JavaScript SDK
The tracking script puts one object on your page, window.Spreelytics. This page is the full
reference for it: how to load it, every method with its arguments and return value, every option of
init(), and the requests it sends.
If you only need the script on your site, Tracking script is the shorter read.
Loading the script
The standard snippet
<script src="https://cdn.spreelytics.com/cdn/sdk/spreelytics-loader.js"></script>
<script>
Spreelytics.init({ client_key: "YOUR_CLIENT_KEY" });
</script>
The first tag creates window.Spreelytics straight away, as a small stand-in that queues your calls.
init() makes it fetch the main script from https://cdn.spreelytics.com. When the main script arrives,
it takes the stand-in's place and runs the queued calls in the order you made them.
So you can call any method below as soon as the snippet has run, with two exceptions: trackLink()
only exists once the main script has loaded, and getVisitorId() and getSessionId() return null
until then.
The queue form
To load the loader without holding up the page, create the queue yourself and load the loader with
async:
<script>
window.Spreelytics = window.Spreelytics || [];
window.Spreelytics.push(["init", { client_key: "YOUR_CLIENT_KEY" }]);
</script>
<script async src="https://cdn.spreelytics.com/cdn/sdk/spreelytics-loader.js"></script>
Until the loader has run, window.Spreelytics is a plain array, so only push() works on it, and a call
such as Spreelytics.track(...) would throw. Use push() for every call: it works before and after the
script loads. Always go through window.Spreelytics rather than keeping your own reference to it,
because the loader replaces the array with its own object. Keep the init command first, because
commands in front of it are dropped.
Loading from code
When your framework only lets you run code, or you want to load the script later, for example after a visitor accepts cookies, add the loader from JavaScript. This is how the Spreelytics app loads it for itself:
function loadSpreelytics() {
const script = document.createElement("script");
script.src = "https://cdn.spreelytics.com/cdn/sdk/spreelytics-loader.js";
script.async = true;
script.onload = () => {
window.Spreelytics.init({ client_key: "YOUR_CLIENT_KEY" });
};
document.head.appendChild(script);
}
Run it from code that only runs in the browser: in a server-rendered app, that is a client-only plugin
or component. Until onload fires, window.Spreelytics does not exist, so calls made before then fail
unless you guard them, as in window.Spreelytics?.track("signup_completed"), and guarded calls are lost.
To keep early calls, combine this with the queue form: create the array and push the init command
before you append the script.
The later the script starts, the more short visits end before it can record them, so load it as early as your page allows.
Methods
init(options)
Spreelytics.init(options: object): void
Starts the script. client_key is required; the other options are listed under
Init options. Returns nothing.
- Call it once, before anything else. A second call is ignored, so options cannot be changed once the script has started.
- Without a
client_key, nothing is loaded and nothing is recorded. - Calls made before
init()are dropped. - Before the first pageview, the script waits for the audience's cookieless setting, for 2.5 seconds at most, then records the pageview of the current page.
Spreelytics.init({
client_key: "YOUR_CLIENT_KEY",
track_links: "outbound",
});
page(url?)
Spreelytics.page(url?: string): void
Records a pageview. You rarely need it, because the first page and every route change are recorded by themselves. Returns nothing.
- With no argument, it records the current address.
- With an argument, it takes a full URL or a path, read against the current page the way a link's
hrefis:/pricingonhttps://www.example.com/blogrecordshttps://www.example.com/pricing. A value that is not a valid address is ignored. - It does nothing when the address points at the page that was counted last. A new query string or an
#anchoris not a new page; a new#/route is. - If 30 minutes have passed since the last activity, it starts a new visit first.
- The title it records is the document's title at that moment.
One use is a form whose steps do not change the address:
// After the visitor moves on to the second step
Spreelytics.page("/signup/step-2");
track(event, properties?, attributes?)
Spreelytics.track(event: string, properties?: object, attributes?: object): void
Sends a custom event. Returns nothing: the request goes out in the background.
| Argument | What it is |
|---|---|
event | The event name. Names are case sensitive, so signup_completed and Signup Completed are two different events. |
properties | Details about this occurrence. Values can be text, numbers or true and false. Nested objects are flattened ({ cart: { total: 49 } } becomes cart.total), objects in an array become keys such as items[].name, a plain array is stored as one value per item, and null values are dropped. |
attributes | Details about the person, with the same keys as identify(). They are applied only when the visitor has already been identified, and ignored otherwise. |
Spreelytics.track("plan_purchased", { plan: "growth", seats: 12 });
- It does nothing until the script has started a visit, which is also why nothing is sent from an automated browser.
- An event counts as an interaction, so the visit is no longer a bounce.
- A request under 16 KB is sent in a way that survives the page closing, so an event fired right before a redirect still arrives. A larger one can be cut off when the page changes.
- Ten property names are kept for payment data:
revenue.amount,revenue.currency,revenueReportingAmount,reportingCurrency,transactionId,refunded,renewal,isFreeTrial,customerNameandcustomerId. Sent from the browser, they are stored but left out of the property lists in the app, and they add nothing to revenue. Name your own ids something else, such asaccount_id. Payments come from your payment provider or your server (see Revenue).
Track events covers naming events and choosing properties.
identify(email, attributes?)
Spreelytics.identify(email: string, attributes?: object): Promise<void>
Ties the current visitor to a person by email address. Their earlier anonymous visits join the person, and once the same person is identified on another device, both devices' activity lands on one profile. See Identify users.
- Email only. The value has to look like an address: some text, an
@, and a domain with a dot. Anything else is skipped without an error. Withdebug: true, the console saysIdentify skipped: invalid email format. - Send it in lower case. The check that finds an existing person compares the address exactly as
you send it, while new people are saved in lower case, so
Ada@Example.comcan end up as a second profile next toada@example.com. Trim and lower-case the address first. - Attributes. These keys are saved on the person, and their values must be text:
first_name,last_name,company,phone,website,lang,address,address_line2,city,state,zipandcountry. Other keys are ignored. - Return value. A promise that resolves once the request has been answered, or at once when the
call is skipped. It never rejects. Called while only the loader is on the page, the call is queued and
returns
undefinedinstead. - The request is sent in a way that survives the page closing, so calling it right before the redirect that follows a login is fine.
await Spreelytics.identify(user.email.trim().toLowerCase(), {
first_name: user.firstName,
company: user.companyName,
});
trackLink(element, properties?)
Spreelytics.trackLink(element: HTMLElement, properties?: object): void
Watches one element and sends a Link Click event each time it is clicked. The event carries the
link's href, text and id, as described in Autocapture, plus your properties.
When a name clashes, your value wins. Returns nothing.
- It only exists once the main script has loaded. Called earlier, it throws
Spreelytics.trackLink is not a function. The queued form works at any time:
Spreelytics.push(["trackLink", document.querySelector("#pricing-cta"), { position: "hero" }]);
- Each element is watched once. A second call on the same element is ignored, along with its properties.
- It works whether or not
track_linksis on. Iftrack_linksalso covers the link, each click is recorded twice. - It is meant for links. On an element without an
href, thehrefproperty holds the current page's address. - Link clicks count as an interaction, and do not count toward your allowance.
getVisitorId()
Spreelytics.getVisitorId(): string | null
Returns the anonymous id of this browser, which starts with vid_, or null.
- It returns
nulluntil the script has started, which takes a moment afterinit(). On a cookieless audience the id is created by Spreelytics and arrives with the answer to the heartbeat request (see Requests the script sends), so it staysnulluntil then, and a callback passed topush()can still seenull. - Its main use is crediting payments to visits: pass it into your checkout as the metadata field
spreelytics_visitor_id(see Revenue). Read it when the visitor checks out, not when the page loads.
getSessionId()
Spreelytics.getSessionId(): string | null
Returns the id of the current visit, which starts with sess_, or null before a visit exists.
- Pass it next to the visitor id as
spreelytics_session_idto tie a payment to the exact visit it came from. - On a cookieless audience, Spreelytics assigns visit ids itself and this value does not match them, so send only the visitor id there.
reset()
Spreelytics.reset(): Promise<void>
Call it when someone logs out, so the next person on the same browser does not carry on as them.
- In the default mode it gives the browser a new visitor id (a new cookie), clears the current visit, starts a new one and straight away records a pageview of the current page for the new visitor. Call it right before you send the visitor away from the page.
- On a cookieless audience there is no stored id to replace. Spreelytics keeps recognising the browser
as the same visitor for the rest of the day, so
reset()cannot separate two people who share a computer. - It returns a promise that resolves once the reset is done. Before
init()it does nothing, and while only the loader is on the page it is queued and returnsundefined.
async function logOut() {
await Spreelytics.reset();
window.location.assign("/login");
}
allowCookies()
Spreelytics.allowCookies(): void
For a cookie banner. Call it when a visitor whose page was started with cookieless: true clicks
Accept (see cookieless). Returns nothing.
- The visitor id Spreelytics has been counting them under is saved in the
spreelytics_vidcookie. The next page they load, started withoutcookieless, carries on as the same visitor, so what they did before accepting stays theirs. - The rest of the current page is still counted without cookies, so the visit in progress is not split. On a site where every click loads a new page, the next page starts a new visit for the same visitor.
- If they accept in the first moment after the page loads, before Spreelytics has sent back their id, the cookie is saved as soon as the id arrives.
- On a page that already uses the cookie it does nothing. While only the loader is on the page it is queued and runs once the main script is ready.
acceptButton.addEventListener("click", () => {
saveBannerAnswer("accepted"); // your banner's own record of the choice
Spreelytics.allowCookies();
});
startReplay() and stopReplay()
Spreelytics.startReplay(): void
Spreelytics.stopReplay(): void
Start and stop session replay at moments of your own, such as sign-in and sign-out. Both return nothing. They matter only on an audience with session replay on.
By default a recorded visit is recorded from the moment the page loads. With
session_replay: { manual_start: true } (see session_replay), nothing is recorded
until startReplay():
Spreelytics.init({ client_key: "YOUR_CLIENT_KEY", session_replay: { manual_start: true } });
// After sign-in:
Spreelytics.startReplay();
// On sign-out, or before a part of the site that should not be recorded:
Spreelytics.stopReplay();
- Each call lasts for the rest of the visit. After
startReplay(), later pages keep recording without another call. AfterstopReplay(), nothing is recorded untilstartReplay(), and what was recorded before the stop is kept. - A new visit, after 30 idle minutes or
reset(), goes back tomanual_start. It is fine to callstartReplay()on every page a signed-in user sees. - Neither call overrides the audience: nothing is recorded with session replay off, for a visitor counted without cookies, or past your plan's monthly replays.
- Both can be called before the script has loaded; the loader queues them.
increment(), decrement() and multiply()
Spreelytics.increment(key: string, amount: number): void
Spreelytics.decrement(key: string, amount: number): void
Spreelytics.multiply(key: string, amount: number): void
These change a number stored on an identified person. They only act on a number field that already exists for the people in your workspace, and Spreelytics has no screen for creating one, so on a Spreelytics audience these calls have no effect.
push(command)
Spreelytics.push(command: unknown[] | (() => void)): void
Calls any of the methods above by name, and works at every stage: before the loader has run (in the queue form), while the main script is loading, and after. Returns nothing.
| Command | Does the same as |
|---|---|
["init", options] | init(options) |
["page", url] | page(url) |
["track", event, properties, attributes] | track(event, properties, attributes) |
["identify", email, attributes] | identify(email, attributes) |
["trackLink", element, properties] | trackLink(element, properties) |
["reset"] | reset() |
["allowCookies"] | allowCookies() |
| A function | Runs the function once the script is ready |
Spreelytics.push(["track", "newsletter_signup", { form: "footer" }]);
Spreelytics.push(function () {
console.log("Spreelytics is ready");
});
An unknown command name logs the warning [Spreelytics] Unknown command: and does nothing else.
Init options
| Option | Type | Default | What it does |
|---|---|---|---|
client_key | string | none, required | The audience's client key, from Settings → Tracking script or Settings → API keys. |
track_links | false, "outbound" or true | false | Records link clicks as Link Click events: "outbound" for links to other hostnames, true for every link. |
track_data_attributes | boolean | true | Records clicks on elements tagged with data-spree-track as custom events. |
cookie_domain | string or false | worked out by the script | The domain the visitor cookie is set on. See cookie_domain. |
redact_url | function | none | Rewrites page addresses before they are sent. See redact_url. |
api_host | string | https://sdk.spreelytics.com/1.0 | The base address of every request the script sends, for a proxy on your own domain. Trailing slashes are removed. The script files still load from cdn.spreelytics.com. See Proxy guide. |
cookieless | boolean | the audience's setting | true counts this visitor without cookies, for a visitor who turned down your cookie banner or has not answered it. See cookieless. |
session_replay | object | none | How session replay records, on an audience that has it on. See session_replay. |
debug | boolean | false | Logs each step to the browser console, in lines starting [Spreelytics] and [Spreelytics Tracker]. |
Attributes on the <script> tag are not read: every option goes through init(). There are no
options to turn off pageviews, scroll depth or the end-of-visit report, to change the 30-minute visit
timeout or to sample traffic. The script does not read Do Not Track. For a cookie banner, either count
visitors without cookies until they accept (see cookieless), or load the script only once
the visitor has agreed (see Loading from code).
cookie_domain
By default the visitor cookie goes on the widest domain the browser accepts for the page. On
www.example.com that is example.com, so app.example.com and blog.example.com share the same
visitor. Browsers refuse public suffixes such as co.uk, so a page on shop.example.co.uk gets
example.co.uk.
- A string sets the domain yourself. It has to be the page's hostname or a parent of it; otherwise the console shows a warning and the cookie stays on the exact hostname.
falsekeeps the cookie on the exact hostname, so each subdomain counts its visitors separately.- IP addresses,
localhostand hostnames without a dot always get a cookie on the exact hostname. - It has no effect on a cookieless audience, where nothing is stored. There, the same person is recognised across your subdomains anyway, because Spreelytics works the visitor out from the request.
A shared visitor does not mean a shared visit: in the default mode each subdomain keeps its own visit (see Autocapture).
redact_url
A function that takes an address and returns the one to send. It runs on the page address, the
referrer, the previous page, the exit page and the page path sent with scroll depth. It always receives
a full URL, even for the scroll path, so new URL(url) works inside it.
Spreelytics.init({
client_key: "YOUR_CLIENT_KEY",
redact_url: (url) => url.replace(/\/invoice\/\d+/, "/invoice/:id"),
});
- Before your function runs, the script already replaces any query-string value that looks like an
email address with
[redacted]. That step cannot be turned off. - If your function throws, or returns something other than text, the address goes out with only that first step applied.
- It does not touch the
hrefof link clicks, page titles or event properties.
cookieless
Left out, the script asks Spreelytics for the audience's setting on every page load and follows the Cookieless tracking switch. That is what most sites want.
Set it to true for one visitor at a time, when your cookie banner says so: they turned it down, or
have not answered it yet. That visitor is counted the way a cookieless audience counts everyone.
Nothing is stored in their browser, and Spreelytics works out who they are from each request. Everyone
else on the audience keeps the cookie.
Spreelytics.init({
client_key: "YOUR_CLIENT_KEY",
cookieless: !visitorAcceptedCookies(), // your banner's own check
});
When the visitor clicks Accept on that page, call allowCookies(). From the next page
on, start the script without cookieless. Cookieless tracking explains how these
visitors are counted and what that costs.
false puts the cookie and browser storage back on a cookieless audience, even though Spreelytics then
ignores them. It is only there for pages that load the main script without the loader.
session_replay
Whether an audience records visits is set in the app, under Settings → Tracking script, so your code turns nothing on. These options change how it records. Each is optional.
Spreelytics.init({
client_key: "YOUR_CLIENT_KEY",
session_replay: {
mask_text_selectors: [".user-name", "#account-email"],
block_selector: ".ads",
},
});
| Option | Default | What it does |
|---|---|---|
manual_start | false | Records nothing until the page calls startReplay(). |
mask_all_inputs | true | Masks what is typed into every form field. Password and email fields stay masked either way. |
mask_text_selectors | [] | CSS selectors whose text is masked. |
mask_text_class | "rr-mask" | A class whose elements' text is masked. |
block_class | "rr-block" | A class whose elements are left out, shown as an empty box. |
block_selector | none | A CSS selector for elements to leave out. |
ignore_class | "rr-ignore" | A class for fields whose typing is not recorded at all. |
ignore_selector | none | A CSS selector for fields whose typing is not recorded at all. |
collect_fonts | false | Records your web fonts, so the replay draws in them. Recordings get much bigger. |
slim_dom_options | "all" | Leaves scripts, comments and head metadata out of recordings. false keeps them. |
Whatever you set, three rules stay on: data-spree-mask masks an element's text, data-spree-block
leaves an element out, and email addresses in page text are masked. A selector the browser cannot read
is skipped rather than breaking the recording.
Requests the script sends
Every request goes to https://sdk.spreelytics.com/1.0, or to your api_host, followed by the path
below.
| Request | When |
|---|---|
GET /track/config | Once per page load, before anything else. Asks whether the audience is cookieless and whether it records session replays; the answer can be reused for up to a minute. |
POST /track/heartbeat | Once when the script starts, and after reset(). It drives the install status in Settings → Tracking script. |
POST /track/page | For every pageview. |
GET /track/stats | Two seconds after each pageview. Reads back the visitor's page and visit counts. |
POST /track/event | For custom events, tagged clicks, link clicks and scroll depth. |
POST /track/session-end | Each time the tab is hidden, and when the page is closed. |
POST /track/ping | When a hidden tab becomes visible again. |
POST /track/identify | For identify(). |
PUT /track/attribute | For increment(), decrement() and multiply(). |
POST /track/replay | While a visit is being recorded: a piece of the recording every 5 seconds, and when the page is hidden. Only on an audience with session replay on. |
The POST requests carry a form-encoded body, a clientKey field and a data field holding JSON,
which keeps them simple enough that browsers send no CORS preflight before them. A visitor counted
without cookies also sends a cookieless field set to 1. POST /track/replay is the exception: it
sends the recording gzip-compressed as plain text, with the key, the visit and the visitor in its
address, and answers whether to keep recording. GET /track/stats
and PUT /track/attribute are preceded by an OPTIONS preflight. Spreelytics answers with an empty
200, except for the config, heartbeat and stats requests, which return a little JSON, and processes
what it accepted in the background.
What the script stores
In the default mode the script keeps a visitor cookie, spreelytics_vid, for a year from the last visit,
plus spreelytics_visitor in local storage and spreelytics_session_data in session storage. On a
cookieless audience, and for a visitor started with cookieless: true, it stores nothing until
allowCookies(). Cookieless tracking lists every item and what
it holds.
With session replay on, a recorded visit also loads the recorder, spreelytics-replay.js, from the
same place as the script, and keeps spreelytics_replay in session storage once it has sent its first
piece of recording.
Related
- Tracking script: the snippet and where it goes.
- Track events: custom events and their properties.
- Identify users: what happens when you identify someone.
- Proxy guide: use
api_hostwith a proxy on your own domain.