This documentation is for the Countly Web SDK version 24.11.X. The SDK source code repository can be found here.
Click here, to access the documentation for older SDK versions.
Countly can run with all browsers that supports ECMAScript 5. Minimum versions of major internet browsers that fully support ES5 are:
IE | Edge | Firefox | Firefox (Android) | Opera | Opera (Mobile) | Safari | Safari (iOS) | Chrome | Chrome (Android) |
10 | 12 | 21 | 96 | 15 | 64 | 6 | 6 | 23 | 98 |
To examine the example integrations please have a look here.
Adding the SDK to the Project
To track your web pages (SPA, MPA or mobile applications that consist of HTML5 views) and browser extensions, you can use Countly Web SDK.
It is automatically hosted on your Countly server as a UMD file:
https://yourdomain.com/sdk/web/countly.min.js
https://yourdomain.com/sdk/web/countly.js
Optionally, you may also use package managers to gain access to the SDK:
npm install countly-sdk-web
yarn add countly-sdk-web
You can also reach the SDK through CDN:
// latest non minified
cdn.jsdelivr.net/npm/countly-sdk-web@latest/lib/countly.js
// latest minified
cdn.jsdelivr.net/npm/countly-sdk-web@latest/lib/countly.min.js
// 24.11.0 non minified
cdn.jsdelivr.net/npm/countly-sdk-web@24.11.0/lib/countly.js
// 24.11.0 minified (JSDelivr or Cloudflare)
cdn.jsdelivr.net/npm/countly-sdk-web@24.11.0/lib/countly.min.js
or
cdnjs.cloudflare.com/ajax/libs/countly-sdk-web/24.11.0/countly.min.js
Lastly as an alternative option, you may download countly.min.js from our GitHub repository and upload it to any server from where you would like to host it.
SDK Integration
Minimal Setup
The SDK is a single UMD file which can be loaded directly to your HTML file or can be imported with a package manager to your project and bundled with your bundler to your build.
You may use the Countly Web SDK synchronously or asynchronously. Asynchronous usage would work without blocking content loading. This would also allow you to use Countly while the Countly script has not yet been loaded. This can be done by pushing function calls into the Countly.q queue, which SDK will execute after it is initialized.
Here you would also need to provide your Countly server app's application key and server URL. Please check here for more information on how to acquire your application key (APP_KEY) and server URL.
You can check all the config options you can use to manipulate the SDK behaviour from here.
An example setup would look like this:
import Countly from "countly-sdk-web";
Countly.init({
// server credentials
app_key: "YOUR_APP_KEY",
url: "http://yourdomain.com"
});
// Track sessions and page views automatically (recommended)
Countly.track_sessions();
Countly.track_pageview();
<!--inside <head></head>-->
<script type='text/javascript'>
var Countly = Countly || {};
Countly.q = Countly.q || [];
// server credentials
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "https://yourdomain.com";
// Track sessions and page views automatically (recommended)
Countly.q.push(['track_sessions']);
Countly.q.push(['track_pageview']);
// Load Countly script asynchronously
(function() {
var cly = document.createElement('script'); cly.type = 'text/javascript';
cly.async = true;
cly.src = 'https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/lib/countly.min.js';
cly.onload = function(){Countly.init()};
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(cly, s);
})();
</script>
<!--inside <head></head>-->
<script type='text/javascript' src='https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/lib/countly.min.js'></script>
<script type='text/javascript'>
Countly.init({
// server credentials
app_key: "YOUR_APP_KEY",
url: "http://yourdomain.com"
});
// Track sessions and page views automatically (recommended)
Countly.track_sessions();
Countly.track_pageview();
</script>
If you are in doubt about the correctness of your Countly SDK integration you can learn about the verification methods from here.
SDK Logging
If logging is enabled, then our SDK will print out debug messages about its internal state (events it's recording and sending) and encountered problems.
Set debug option to true at the Countly initialization to enable logging:
Countly.debug = true;
Countly.init({
debug:true,
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
});
For more information on where to find the SDK logs you can check the documentation here.
Crash Reporting
To automatically capture and report unhandled JavaScript errors on your website (You can add more information to error reports by providing an object with key/value pairs):
Countly.q.push(['track_errors', {
"your_extra_info_key": your_info
}])
Countly.track_errors({
"your_extra_info_key": your_info
})
You can also report handled exceptions to the server. Optionally you can provide extra information (an object) with the report (or use the ones provided with the track_errors method as default ones).
try{
//do something here
}
catch(ex){
//report error to Countly
Countly.q.push(['log_error', ex, extra_info]);
}
try{
//do something here
}
catch(ex){
//report error to Countly
Countly.log_error(ex, extra_info);
}
You can leave breadcrumbs throughout the code on different user actions. These breadcrumbs will then be combined in a single log and reported to the server as well.
Countly.q.push(['add_log', "user clicked button a"]);
Countly.add_log("user clicked button a");
Symbolication
Crash symbolication is available for Enterprise Edition users.
If the js files you serve are minified, transpiled or otherwise processed from your source files, you can use symbolication. With symbolication, your stacktraces will correctly point to the lines on the source files, which will help your developers debug crashes and errors. Here's an example:
Error: Error at depth 3
at cause_error (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5657:13)
at multiply (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5651:3)
at saveRecords (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5648:3)
at initializeCoreFunctions (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5645:3)
at save (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5642:3)
at removeHashes (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5639:3)
at calculateParams (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5636:3)
at addRecords (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5633:3)
at HTMLButtonElement.unhandled_error (http://127.0.0.1:5501/examples/symbolication/dist/main.js:5675:5)
Error: Error at depth 3
at src/index.js:66:12
at cause_error (src/index.js:59:2)
at multiply (src/index.js:56:2)
at saveRecords (src/index.js:53:2)
at initializeCoreFunctions (src/index.js:50:2)
at save (src/index.js:47:2)
at removeHashes (src/index.js:44:2)
at calculateParams (src/index.js:41:2)
at addRecords (src/index.js:87:6)
When using any build tool you will need to choose an option that generates the source map as a separate file and not inline with the final js file (devtool option for webpack for example). If you set it up correctly your builds will produce a source map file ending in .map
, which is the source map file you will upload to your Countly server.
To start Symbolicating your errors you just need to upload your source map file to your Countly instance via the side menu Crashes > Manage Symbols. In this view, the source map files you have uploaded are listed. To upload one, you click the "Add Debug Symbol File", fill the symbol type and app version fields accordingly and upload your .map
file. With your source map file uploaded, you can Symbolicate the crash reports for that app version on Countly. For more information, see Crash Symbolication documentation.
Events
Adding an Event
Events are a way to track any custom actions or other data you would like to track from your website. You may also set segments to be able to view a breakdown of the action by providing the segment values.
All data passed to the Countly instance via the SDK or API should be in UTF-8.
An event consists of a JavaScript object with keys:
- key - the name of the event (mandatory)
- count - number of events (optional) (defaults to 1)
- sum - sum to report with the event (optional)
- dur - duration expressed in seconds (optional)
- segmentation - an object with key/value pairs (optional)
Here is an example of adding an event with all possible properties:
Countly.q.push(['add_event',{
"key": "Survey_success",
"count": 1,
"sum": 0,
"dur": 25,
"segmentation": {
"Answer_1": "I would love to",
"Answer_2": 12
}
}]);
Countly.add_event({
"key": "Survey_success",
"count": 1,
"sum": 0,
"dur": 25,
"segmentation": {
"Answer_1": "I would love to",
"Answer_2": 12
}
});
Timed Events
Timed events are SDK's convenience methods to calculate the duration of an activity easily. There are three methods available to use to calculate the duration property of an event: start_event, cancel_event, and end_event.
However, it's important to note that these methods operate on the memory layer and shouldn't be used to calculate durations in situations where a browser restart/refresh occurs.
The start_event method starts an internal timer within the SDK for a given event name. This timer works by taking the current timestamp and storing it in memory.
Countly.q.push(['start_event', 'timedEvent']);
Countly.start_event("timedEvent")
The cancel_event method erases the timestamp associated with a given event name if a start_event was previously called for that event.
Countly.q.push(['cancel_event', 'timedEvent']);
Countly.cancel_event("timedEvent")
The end_event method stops the timer and creates an event for the given name with the calculated duration and adds it to the event queue. You can also pass an event object to this method, and in that case, it will use the key value as the event name.
//end event
Countly.q.push(['end_event', 'timedEvent']);
//or end event with additional data
Countly.q.push(['end_event',{
"key": "timedEvent",
"count": 1,
"sum": 1.5,
"segmentation": {
"key1": "value1",
"key2": "value2"
}
}]);
//end event
Countly.end_event("timedEvent")
//or end event with additional data
Countly.end_event({
"key": "timedEvent",
"count": 1,
"sum": 1.5,
"segmentation": {
"key1": "value1",
"key2": "value2"
}
});
If end_event is not called, no event will be sent to the server.
Sessions
Sessions are the continuous time blocks that indicates the user interacts with your website. Each recorded interaction after a session starts will be recorded under that session until a new session starts.
Automatic Session Tracking
To leave session tracking logic to the SDK you can just call:
Countly.q.push(['track_sessions']);
Countly.track_sessions();
Manual Sessions
If you need a custom session logic then you can use manual session methods to:
- Starting a session at the beginning
- Marking the end of a session
Only use the methods below if you aren’t planning on using the automatic session tracking and set the use_session_cookie
setting to false during init for more granular control of the session.
SDK will automatically report elapsed session duration with 60 seconds intervals (this can be modified during init with session_update
config option).
Beginning a Session
To begin a session:
Countly.q.push(['begin_session']);
Countly.begin_session();
Ending a session
To end a session:
Countly.q.push(['end_session']);
Countly.end_session(sec)
View Tracking
SDK only provides manual views (you will need to determine when a view starts). Depending on your site's structure (MPA or SPA) you might need to modify how you track page views with the SDK.
Manual View Recording
All views are auto-stopped meaning there can only be one view tracked at a time and it will end at the end of a session or when another one starts.
You can track the current page that the SDK initialized in by using the method below for MPAs. This uses location.path
as the page name and then reports it to the server:
Countly.q.push(['track_pageview']);
Countly.track_pageview();
If location.path
as the page name is not useful for you (for Ajax updated contents and single page web applications), pass the page name as a parameter to record the new page view, for example
Countly.q.push(['track_pageview', "pagename"]);
Countly.track_pageview("pagename");
Here is a good example if your single-page app uses URL hash:
Countly.q.push(['track_pageview',location.pathname+location.hash]);
$(window).on('hashchange', function() {
Countly.q.push(['track_pageview',location.pathname+location.hash]);
});
Countly.track_pageview(location.pathname+location.hash);
$(window).on('hashchange', function() {
Countly.track_pageview(location.pathname+location.hash);
});
If you are relying on the default page name assigned by the SDK you might like to ignore some URLs to exclude from tracking, such as dynamic URLs that include the user ID in the URL, internal URLs, or for any other reason. You may do so by providing another parameter with a list of strings of views to ignore or a list of regular expressions to ignore.
//Ignoring specific page
Countly.q.push(['track_pageview',["/test-page"]]);
//Ignoring multiple specific pages
Countly.q.push(['track_pageview',["/test1", "/test2", "/test3"]]);
//Ignoring all /download/{download_id} pages but not /download page
Countly.q.push(['track_pageview',["/download/*"]]);
//Ignoring specific page while providing custom values (like hash value) for page view
Countly.q.push(['track_pageview', location.pathname+location.hash,["/test-page"]]);
//Ignoring specific page
Countly.track_pageview(["/test-page"]);
//Ignoring multiple specific pages
Countly.track_pageview(["/test1", "/test2", "/test3"]);
//Ignoring all /download/{download_id} pages but not /download page
Countly.track_pageview(["/download/*"]);
//Ignoring specific page while providing custom values (like hash value) for page view
Countly.track_pageview(location.pathname+location.hash, ["/test-page"]);
Optionally, you may provide view segments (key/value pairs) to track them with the view (as the third parameter). There is a list of reserved segment keys that should not be used:
- start
- visit
- bounce
- end
- name
- domain
- view
- segment
- platform
//Provide view segments
Countly.q.push(['track_pageview', null, null, {theme:"red", mode:"fullscreen"}]);
//Provide view segments
Countly.track_pageview(null, null, {theme:"red", mode:"fullscreen"});
Overriding View Name and URL Getters
There are cases when determining the view name requires more complex logic, and in some cases, you will need to separate the URL and the View naming. This way you give a human readable page name, yet you have the valid URL underneath them to view action maps, such as clicks and scrolls.
To do so you may define the view name and URL getter functions. That way you won’t need to provide separate view names, rather the SDK will use this getter function to receive the proper view name and the URL you would like to use.
Here is an example of how to create a getter for View and URL.
Countly.getViewName = function(){
//get base for our view
var view = location.pathname;
//if this is a page with dynamic id, we don't need id itself
if (view.startsWith("/id/")) {
view = "/id";
}
//now let's beautify the name
//remove first character which always is slash (/)
view = view.substring(1);
//split by sub paths and use spaces instead
view = view.split("/").join(" ");
//upper case first letter
view[0].toUpperCase() + view.substring(1)
//return beautified view name
return view;
}
Countly.getViewUrl = function(){
//we want to have path and query string and hash as url
return location.pathname + location.search + location.hash;
};
Device ID Management
Retrieving Current Device ID
Countly offers a convenience method (get_device_id
) for you to get the current user's device ID:
var id = Countly.get_device_id();
SDK records the type of an ID. These types are:
DEVELOPER_SUPPLIED
SDK_GENERATED
TEMPORARY_ID
DEVELOPER_SUPPLIED
device ID means this ID was assigned by your internal logic. SDK_GENERATED
device ID means the ID was generated randomly by the SDK, and TEMPORARY_ID
device ID means the SDK is in offline mode.
You can get the device ID type of a user by calling the get_device_id_type
function:
var idType = Countly.get_device_id_type();
You can use the DeviceIdType
enums to evaluate the device ID type you retrieved:
var idType = Countly.get_device_id_type();
if (idType === Countly.DeviceIdType.SDK_GENERATED) {
// ...do something
}
Changing Device ID
You can change the device ID of a user with set_id method:
Countly.q.push(['set_id', "newId"]);
Countly.set_id("newId");
This method's effect on the server will be different according to the type of the current ID stored in the SDK at the time you call it:
-
If current stored ID is
SDK_GENERATED
then in the server all the information recorded for that device ID will be merged to the new ID you provide and old user with theSDK_GENERATED
ID will be erased. -
If the current stored ID is
DEVELOPER_SUPPLIED
orTEMPORARY_ID
then in the server it will also create a new user with this new ID if it does not exist.
If you need a more complicated logic then you will need to use this method mentioned here instead.
NOTE: The call will reject invalid device ID values. A valid value is not null, not undefined, of type string and is not an empty string.
Temporary Device ID (Offline mode)
Some cases do exist when you would like the SDK to collect data but not send it to the server until a certain point. Additionally, this mode allows you to delay providing the device_id property until a later time.
E.g. if you would like to track your users with a custom device_id, such as with your internal customer ID, and you may only receive that value as soon as the user logs in. Yet, you would also like to track what the user did before logging in.
Using offline mode within this context allows you to omit the user merging and server overhead that comes with it, including any possibly skewed aggregation data.
If offline mode is entered and consent was enabled, all previously given consent will be removed. This means that all features will cease to function until new consent has been given again. Therefore after entering the offline mode, you should reestablish consent again.
To launch the SDK in offline mode, simply provide the offline_mode config value as true. At this point you may omit providing the device_id value if you would like.
Countly.debug = false;
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "https://try.count.ly";
Countly.offline_mode = true;
Countly.init();
Countly.init({
debug:false,
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
offline_mode: true
});
Or you can enable offline mode at any point later in the SDK.
Countly.q.push(['enable_offline_mode']);
Countly.enable_offline_mode();
If you would like to disable offline mode and optionally provide the device_id at a later time, you may do so by adhering to the following:
Countly.q.push(['disable_offline_mode', device_id]);
Countly.disable_offline_mode(device_id);
Device ID Generation
By default Countly generates and assigns a random device ID (UUID) to each device that visits your website. This ID is saved at the localstorage of the browser. Assuming the localstorage is not cleared, this device will be recognized with this stored ID from now on.
Limitations to this method:
- Multiple users using the same device would be seen as a single user
- When a user reaches to your website from multiple devices or browsers in incognito mode then all devices would be seen as separate users
If these scenarios are an issue of concern to you, you can mitigate them with various device management strategies like:
- Adding a login/authentication page and assigning device ID there
- Entering the offline mode (mentioned above) until you identify the user and assigning the ID then
You can also provide an ID during init to prevent the SDK from generating a random ID:
// adding device ID here will prevent the generation of a random ID
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://your.server",
device_id:"yourDeviceID",
});
You can also provide an ID in the search query of the link that opens your site. If you use the param cly_device_id
its value will be set as the device ID instead of generating a random one:
// you can assign a device ID to a user through a link that you have provided to them by adding cly_device_id to your url query
yoursite.com + ?cly_device_id=yourDeviceID
If you want to erase the previously stored device ID from the storage you can set clear_stored_id flag to true at the init config:
// this will erase the stored device ID from the local storage every time the Countly is initialized
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
clear_stored_id: true,
});
Device ID Priority
If you have used multiple methods to set a device ID for your users during your first init, Countly would fall back to the device ID priority hierarchy to assign the the correct ID for your user. This hierarchy is as follows:
URL set ID > Developer set ID > Temp ID (offline mode) > SDK generated ID
User Location
With the SDK integrated into your website, you might want to track your user location. By default, the Countly Server uses the GeoIP database to deduce a user's location from the coming requests (you can check details of it from here). However the SDK provides the ability to set custom locations for your users.
Setting Location
There are 3 location parameters that can be provided with the SDK:
- Country code in the two-letter, ISO standard ("jp", "gr" etc.)
- City name ("Kyoto", "Athens" etc.)
- Your user’s IP address
You can provide any of these information while initializing the SDK:
// you can directly provide the city and the country code
Countly.city = "Tokyo";
Countly.country_code = "jp";
// or you can provide the ip address
Countly.ip_address = "198.168.1.1";
// Initialize the SDK
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.server.ly",
// you can directly provide the city and the country code
city: "Tokyo",
country_code: "jp",
// or you can provide the ip address
ip_address: "198.168.1.1",
});
Heatmaps
Heatmaps feature is a web exclusive plugin that helps you to visualize user interactions on your website. Web SDK supports this functionality by providing user click and scroll information to your server. Then from your server, you can trigger Heatmaps overlay to visualize these click clusters and scroll zones on your website.
To display this overlay the SDK loads certain scripts from your server. To enable these scripts to be loaded from somewhere else other than the Countly server SDK sends information to (for example your internal server), the SDK offers a whitelisting option during the initialization. To whitelist domains other than your Countly server you should provide an array of these domains, as String values, under the heatmap_whitelist
flag during the initialization:
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "https://try.count.ly";
Countly.heatmap_whitelist = ["https://you.domain1.com", "https://you.domain2.com"];
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
heatmap_whitelist: ["https://you.domain1.com", "https://you.domain2.com"]
});
Tracking Clicks
This method will automatically track clicks on the last reported view and display them on the heatmap.
Countly.q.push(['track_clicks']);
Countly.track_clicks();
In the event you are facing issues with viewing heatmaps, kindly go through this Troubleshooting guide.
Viewing heatmaps with HTTP/HTTPS content:
Note that browsers do not allow loading HTTP iframe content on HTTPS websites. For this reason, if you are using HTTPS on your Countly instance, you will only be able to view HTTPS content and no HTTP page content will be visible.
After you integrate the SDK and start sending click data, all generated heatmaps may be viewed under Analytics > Heatmaps, as shown below:
Tracking Scrolls
This method will automatically track scrolls on the last reported view and display them on the heatmap.
Countly.q.push(['track_scrolls']);
Countly.track_scrolls();
As with Click Heatmaps, collected data is viewable under Analytics > Heatmaps. You may change the heatmap type on the top bar once a view is open.
Remote Config
Remote Config feature enables you to fetch data that you have created in your server. Depending on the conditions you have set, you can fetch data from your server for the specific users that fits those conditions and process the Remote Config data in anyway you want. Whether to change the background color of your site to showing a certain message, the possibilities are virtually endless. For more information on Remote Config please check here.
While fetching Remote Config, the SDK will automatically enroll the user to A/B testing. But you are able to explicitly enroll (or not) your users to the A/B testing while fetching the remote config values or afterwards. For more information on A/B testing please check here.
Automatic Remote Config
Automatic Remote Config functionality is disabled by default and needs to be explicitly enabled. When automatic Remote Config is enabled, the SDK will try to fetch it upon some specific triggers. For example, after SDK initialization or changing device ID.
You may enable this feature by providing to the remote_config flag a callback function or by setting it to true while initializing the SDK.
If you provide a callback, the callback will be called when the Remote Config is initially loaded and when it is reloaded if you change the device_id. This callback should have two parameters, first is for error, and second is for the Remote Config object.
// in your Countly init script
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "https://try.count.ly";
Countly.debug = true;
Countly.remote_config = true;
// OR
// provide a callback to be notified when configs are loaded
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "https://try.count.ly";
Countly.debug = true;
Countly.remote_config = function(err, remoteConfigs){
if (!err) {
//we have our remoteConfigs here
console.log(remoteConfigs);
}
};
// in your Countly init script
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
debug: true,
remote_config: true
});
// OR
// provide a callback to be notified when configs are loaded
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
debug: true,
remote_config: function(err, remoteConfigs){
if (!err) {
//we have our remoteConfigs here
console.log(remoteConfigs);
}
}
});
Manual Remote Config
If you want, you can manually fetch the Remote Config in order to receive the latest value anytime after the initialization. To do so you have to use the fetch_remote_config call. This method is also used for reloading the values for updating them according to the latest changes you made on your server.
By using this method, you can simply load the entire object or load some specific keys or omit some specific keys in order to decrease the amount of data transfer needed, assuming the values for some of the keys are large. This call will automatically save the fetched keys internally.
Fetch All Keys
Here you so not need to provide any parameters to the call but providing a callback is the recommended practice. This callback should have two parameters, first is for error, and second is for the Remote Config object.
// load the whole configuration object with a callback
Countly.fetch_remote_config(function(err, remoteConfigs){
if (!err) {
console.log(remoteConfigs);
// or do something else here if you want with remoteConfigs object
}
});
// or whole configuration object with no params
Countly.fetch_remote_config();
Fetch Specific Keys
Here the keys should be provided as string values in an array, as the first parameter in fetch_remote_config call. You can provide a callback function as a second parameter. This callback should have two parameters, first is for error, and second is for the Remote Config object.
// load specific keys only, as `key1` and `key2`
Countly.fetch_remote_config(["key1","key2"], function(err, remoteConfigs){
if (!err) {
console.log(remoteConfigs);
// or do something else here if you want with remoteConfigs object
}
});
Fetch All Except Specific Keys
Here the first parameter should be set to 'null' or 'undefined' and the keys that you want to omit must be provided as the second parameter as an array of keys as string. As a third parameter you can provide a callback function. This callback should have two parameters, first is for error, and second is for the Remote Config object.
// load all key values except specific keys, as `key1` and `key2'
Countly.fetch_remote_config(null, ["key1","key2"], function(err, remoteConfigs){
if (!err) {
console.log(remoteConfigs);
// or do something else here if you want with remoteConfigs object
}
});
Accessing Remote Config Values
You may call get_remote_config each time you would like to receive the Remote Config object of a value for a specific key or all keys from your local storage.
This method should be called once the Remote Config have been successfully loaded, or it will simply return an empty object or undefined values.
//get whole Remote Config object var remoteConfig = Countly.get_remote_config(); //or get value for specific key like 'test' var test = Countly.get_remote_config("test");
A/B Testing
To do so you have to set the use_explicit_rc_api flag to true during init (by default it is false). This will use the new Remote Config API and enroll your users to the A/B testing if they are eligible. However if you want to use the new API without enrolling your users automatically rc_automatic_optin_for_ab flag should be set to false during init (by default it is true).
If you would like to enroll user to A/B testing without going through the Remote Config API, instead you can use the call enrollUserToAb with keys (an array of string values) that you want to enroll the user to.
// enrolling user for 'key1' and 'key2'
Countly.enrollUserToAb(["key1","key2"]);
Consent
If consents are enabled, to fetch the Remote Config data you have to provide the 'remote-config' consent for this feature to work.
User Feedback
If you want to receive feedback from your users, there are a couple of ways you can do that in Countly. Users can leave feedback through Feedback Widgets (Surveys, NPS, Ratings). With the help of these widgets, you can ask your customers multiple questions and learn about their opinions and preferences in detail.
Feedback Widgets
Feedback Widgets is a Countly Enterprise plugin.
It is possible to display 3 kinds of feedback widgets: NPS, Survey and Rating.
For more detailed information about Feedback Widgets, you can refer to here.
Before any feedback widget can be shown, you need to create them in your Countly dashboard.
To simply show available widgets to your users you can use the methods available through the feedback interface. These methods will take a single parameter:
- nameTagOrID - String value to select a widget according to its name, tag or ID (optional)
If this value is not provided or no widget with the provided parameter can not be found it will display the first widget available instead:
Countly.q.push(() => { Countly.feedback.showNPS("nameTagOrID"); });
Countly.q.push(() => { Countly.feedback.showSurvey("nameTagOrID"); });
Countly.q.push(() => { Countly.feedback.showRating("nameTagOrID"); });
Countly.feedback.showNPS("nameTagOrID");
Countly.feedback.showSurvey("nameTagOrID");
Countly.feedback.showRating("nameTagOrID");
If you need a more complex logic to display your widgets you can check extended documentation from here.
Manual Reporting
Reporting Feedback Widgets manually consists of 3 main steps:
- Fetching widget list from the server with 'get_available_feedback_widgets'
- Fetching one widget's data from that list with 'getFeedbackWidgetData'
- Reporting that single widget's results with 'reportFeedbackWidgetManually'
At first step, by using the 'get_available_feedback_widgets' function, you can fetch the list of available widgets from your server as an Array of widget Objects. This function takes a callback as a parameter and this callback should have two parameters, first one for the returned list and the second one for the error. Inside your callback you should process this array of widget objects and pick one object that you want to report the results for. This array and the objects that you can pick would look like this:
{
"result":[
{
"_id":"614811419f030e44be07d82f",
"type":"rating",
"appearance":{
"position":"mleft",
"bg_color":"#fff",
"text_color":"#ddd",
"text":"Feedback"
},
"tg":["/"],
"name":"Leave us a feedback"
},
{
"_id":"614811419f030e44be07d839",
"type":"nps",
"name":"One response for all",
"tg":[]
},
{
"_id":"614811429f030e44be07d83d",
"type":"survey",
"appearance":{
"position":"bLeft",
"show":"uSubmit",
"color":"#0166D6",
"logo":null,
"submit":"Submit",
"previous":"Previous",
"next":"Next"
},
"name":"Product Feedback example",
"tg":[]
}
]
}
Here you would want to pick a widget according to its type and name or any other information you are looking for. For more information on this data please check here.
At second step, by using the 'getFeedbackWidgetData' function, you can fetch the data of the widget of your choice. This functions has two parameters, first one is the widget object obtained from the first step and the second one is a callback with two parameters, one for the retrieved data and second one is for the error. Inside your callback you can process the returned object and gather information necessary for you to provide at the next step.
The third and last step is the part where you would report the results for the widget of your choice by using the 'reportFeedbackWidgetManually' function. This function takes three parameters, first the widget object obtained from the first step, second the widget data from the second step and third the result object that you want to report for your widget. This result object would have different key/value pairs depending on the type of widget you are reporting about so you can reach to an in-depth explanation on how to form this object from here.
And example implementation of the mentioned concepts can be seen here:
// an example of getting the widget list, using it to get widget data and then recording data for it manually. widgetType can be 'nps', 'survey' or 'rating'
function getFeedbackWidgetListAndDoThings(widgetType) {
// get the widget list
Countly.get_available_feedback_widgets(
// callback function, 1st param is the feedback widget list
function (feedbackList, err) {
if (err) { // error handling
console.log(err);
return;
}
// find the widget object with the given widget type. This or a similar implementation can be used while using fetchAndDisplayWidget() as well
const countlyFeedbackWidget = feedbackList.find(widget = widget.type === widgetType);
if (!countlyFeedbackWidget) {
console.error(`[Countly] No ${widgetType} widget found`);
return;
}
// Get data with the widget object
Countly.getFeedbackWidgetData(CountlyFeedbackWidget,
// callback function, 1st param is the feedback widget data
function (feedbackData, err) {
if (err) { // error handling
console.error(err);
return;
}
const CountlyWidgetData = feedbackData;
// record data according to the widget type
if (CountlyWidgetData.type === 'nps') {
Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, { rating: 3, comment: "comment" });
} else if (CountlyWidgetData.type === 'survey') {
var widgetResponse = {};
// form the key/value pairs according to data
widgetResponse["answ-" + CountlyWidgetData.questions[0].id] = CountlyWidgetData.questions[0].type === "rating" ? 3 : "answer";
Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, widgetResponse);
} else if (CountlyWidgetData.type === 'rating') {
Countly.reportFeedbackWidgetManually(CountlyFeedbackWidget, CountlyWidgetData, { rating: 3, comment: "comment", email: "email", contactMe: true });
}
}
);
})
}
Consent
If consents are enabled, to use Feedback Widgets you have to provide the 'feedback' and the 'star-rating' consents for this feature to work.
User Profiles
User Details
If you have any details about the user/visitor, you may provide Countly with that information. This will allow you to track every specific user on the "User Profiles" tab, which is available with Countly Enterprise Edition.
If a parameter is set as an empty string, it will be deleted on the server side.
The list of possible parameters you may pass:
Countly.q.push(['user_details',{
"name": "Arturs Sosins",
"username": "ar2rsawseen",
"email": "test@test.com",
"organization": "Countly",
"phone": "+37112345678",
//Web URL to picture
"picture": "https://pbs.twimg.com/profile_images/1442562237/012_n_400x400.jpg",
"gender": "M",
"byear": 1987, //birth year
"custom":{
"key1":"value1",
"key2":"value2",
...
}
}]);
Countly.user_details({
"name": "Arturs Sosins",
"username": "ar2rsawseen",
"email": "test@test.com",
"organization": "Countly",
"phone": "+37112345678",
//Web URL pointing to user picture
"picture": "https://pbs.twimg.com/profile_images/1442562237/012_n_400x400.jpg",
"gender": "M",
"byear": 1987, //birth year
"custom":{
"key1":"value1",
"key2":"value2",
...
}
});
Modifying Custom Data
Additionally, you may perform different manipulations on custom data values, such as incrementing the current value on the server or storing an array of values under the same property.
After using modifiers, don't forget to call userData.save
to send data to server.
The list of available methods may be found below:
Countly.q.push(['userData.set', key, value]) //set custom property
Countly.q.push(['userData.unset', key]) //remove custom property
Countly.q.push(['userData.set_once', key, value]) //set custom property only if property does not exist
Countly.q.push(['userData.increment', key]) //increment value in key by one
Countly.q.push(['userData.increment_by', key, value]) //increment value in key by provided value
Countly.q.push(['userData.multiply', key, value]) //multiply value in key by provided value
Countly.q.push(['userData.max', key, value]) //save max value between current and provided
Countly.q.push(['userData.min', key, value]) //save min value between current and provided
Countly.q.push(['userData.push', key, value]) //add value to key as array element
Countly.q.push(['userData.push_unique', key, value]) //add value to key as array element, but only store unique values in array
Countly.q.push(['userData.pull', key, value]) //remove value from array under property with key as name
Countly.q.push(['userData.save']) //send userData to server
Countly.userData.set(key, value) //set custom property
Countly.userData.set_once(key, value) //set custom property only if property does not exist
Countly.userData.increment(key) //increment value in key by one
Countly.userData.increment_by(key, value) //increment value in key by provided value
Countly.userData.multiply(key, value) //multiply value in key by provided value
Countly.userData.max(key, value) //save max value between current and provided
Countly.userData.min(key, value) //save min value between current and provided
Countly.userData.push(key, value) //add value to key as array element
Countly.userData.push_unique(key, value) //add value to key as array element, but only store unique values in array
Countly.userData.pull(key, value) //remove value from array under property with key as name
Countly.userData.save() //send userData to server
Orientation Tracking
Orientation tracking is enabled by default and will be sent if the required "user" consent is given (if enabled). Countly will report the device orientation once a session starts, and at any time the orientation changes.
You may disable orientation tracking by providing the enable_orientation_tracking setting when initializing the SDK as follows.
// in your Countly init script
Countly.enable_orientation_tracking = false;
//to disable orientation tracking
Countly.init({
debug:false,
app_key:"YOUR_APP_KEY",
device_id:"1234-1234-1234-1234",
url: "https://try.count.ly",
enable_orientation_tracking: false //this will disable orientation tracking
});
In case you need to force reporting orientation, you may call the following method.
//user stored conversion data
Countly.q.push(['report_orientation', "portrait"]);
//user stored conversion data
Countly.report_orientatio("portrait");
Application Performance Monitoring
If you want to record some performance metrics regarding your website there are 2 ways to report these performance traces. One way is to construct and report them manually. The other is using a plugin that will utilize BoomerangJS to collect website's performance data and report it as a performance trace.
You can reach to example implementations of APM with BoomerangJS from the following links:
Async Apm Example
Sync Apm Example
Custom Traces
To manually report trace you need to construct the trace object and call a method like this:
//report custom trace
Countly.q.push(["report_trace",{
type: "device", //device or network
name: "test call", //use name to identify trace and group them by
stz: 1234567890123, //start timestamp in milliseconds
etz: 1234567890123, //end timestamp in milliseconds
apm_metrics: {
duration: 1000 //duration of trace
}
}]);
//report custom trace
Countly.report_trace({
type: "device", //device or network
name: "test call", //use name to identify trace and group them by
stz: 1234567890123, //start timestamp in milliseconds
etz: 1234567890123, //end timestamp in milliseconds
apm_metrics: {
duration: 1000 //duration of trace
}
});
Whether you are using Countly synchronously or asynchronously, you should always provide the 'duration' key and its value in apm_metrics, otherwise custom traces won't be recorded.
Automatic Device Traces
Automatic trace reporting has two different implementation depending on if you are using Countly synchronously or asynchronously. Normally we would like Countly script to load first and BoomerangJS related scripts right after.
Asynchronous Implementation
To use automatic device traces in your async Countly implementation you will need to set loadAPMScriptsAsync
flag to true
in Countly object. This would ensure that the correct script load order is established. You can provide two additional flags to the Countly object. First one is the BoomerangJS script source path as customSourceBoomerang
and the second is the countly_boomerang script source path as customSourceCountlyBoomerang
. If not provided the SDK would use the latest CDN scripts as the source:
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.loadAPMScriptsAsync = true;
// Countly.customSourceBoomerang = "../somewhere/boomerang.min.js";
// Countly.customSourceCountlyBoomerang = "../somewhere/countly_boomerang.js";
// ...
Also, in your Countly init script you need to call a method to start reporting 'loading' and 'network' traces automatically:
// enables APM
Countly.q.push(["track_performance"]);
This method accepts a BoomerangJS config object (more information on BoomerangJS) as an optional second parameter. If you are familiar with it, you can modify it on your own depending on your needs (you can find the used files here). By default the SDK would use this configuration:
{
//page load timing
RT:{},
//required for automated networking traces
instrument_xhr: true,
captureXhrRequestResponse: true,
AutoXHR: {
alwaysSendXhr: true,
monitorFetch: true,
captureXhrRequestResponse: true
},
//required for screen freeze traces
Continuity: {
enabled: true,
monitorLongTasks: true,
monitorPageBusy: true,
monitorFrameRate: true,
monitorInteractions: true,
afterOnload: true
}
}
Synchronous Implementation
To automatically report traces you will need to include 2 additional files in your project directly after declaring the Countly script like this with the correct paths according to your project structure:
// Option 1: You can provide local paths
<script type='text/javascript' src="../plugin/boomerang/boomerang.min.js"></script> <script type='text/javascript' src='../plugin/boomerang/countly_boomerang.js'></script>
// Option 2: Or you can use CDN for path
<script type='text/javascript' src="https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/plugin/boomerang/boomerang.min.js"></script>
<script type='text/javascript' src="https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/plugin/boomerang/countly_boomerang.js"></script>
After that, you would call a method to start reporting 'loading' and 'network' traces automatically. You can optionally provide here a BoomerangJS config object if you are familiar with it as mentioned above at Async implementation. Default usage inside your Countly init script would be like this:
//automatically report traces
Countly.track_performance();
User Consent
This section talks about how to set up GDPR compliant consent management with the Countly Web SDK.
If consent management is enabled then features of the SDK would require consent to be provided before start working. This way you would have full control over your user tracking in every part of your website.
It should be noted that there is no persistency for consent state in the SDK and the developer is fully responsible for saving/keeping the state of the user's consent before providing it to the SDK.
Feature Names
The SDK provides different features for consent. You may check all the supported features for the current SDK by checking the Countly.features property. Here is a list containing all the properties with explanations:
- sessions - tracks when, how often, and how long users use your website
- events - allows your events to be sent to the server
- views - allows for the views/pages accessed by a user to be tracked
- scrolls - allows a user’s scrolls to be tracked on the heatmap
- clicks - allows a user’s clicks and link clicks to be tracked on the heatmap
- forms - allows a user’s form submissions to be tracked
- crashes - allows JavaScript errors to be tracked
- attribution - allows direct attribution tracking
- users - allows user information, including custom properties, to be collected/provided
- star-rating - allows user rating and feedback tracking through rating widgets
- feedback - allows survey, nps and rating widgets usage and reporting
- apm - allows performance tracking of application by recording traces
- location - allows a user’s location (country, city area) to be recorded
- remote-config - allows users to download remote config from the server
Setup During Init
To enable consent management mode ( and to disable tracking until consent is given for a specific feature), all you need to do is pass true to the require_consent config flag during SDK initialization.
// in your Countly init script
Countly.require_consent = true;
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.server.ly",
require_consent: true // this will enable consent management
});
Changing Consent
Upon a visitor’s arrival to your website, you could check if you already have consent from this visitor. If not, you could present them with a popup explaining what will be tracked and allow them to consent to tracking. When a user selects the consent preferences, you should persistently store it, and on each Countly load, let Countly know for which features the user gave consent by calling the Countly.add_consent method and passing one or multiple features (as an array).
Also you can also allow the user to change their mind regarding separate settings and when changes are going to be made you can call the Countly.add_consent or Countly.remove_consent methods to allow Countly to track specific features or disable tracking for them.
Here is a high-level example of how it could look:
// to add consent {string|array}
Countly.q.push(['add_consent', feature]);
// to remove consent {string|array}
Countly.q.push(['remove_consent', feature]);
// to add consent {string|array}
Countly.add_consent(feature)
// to remove consent {string|array}
Countly.remove_consent(feature)
Feature Groups
Depending on your website and use case, you may also want to combine some of the features into one using the group_features method.
Countly.group_features({
activity:["sessions","events","views"],
interaction:["scrolls","clicks","forms"]
});
//After this call Countly.add_consent("activity") to allow "sessions","events","views"
//or call Countly.add_consent("interaction") to allow "scrolls","clicks","forms"
//or call Countly.add_consent("crashes") to allow some separate feature
Countly.group_features({
all:["sessions","events","views","scrolls","clicks","forms","crashes","attribution","users"]
});
//After this call Countly.add_consent("all") to allow all features
Security and Privacy
Parameter Tamper Protection
You can set an optional salt
for the checksum calculation of request data which will be sent alongside each request, using the &checksum
field. You will need to set the exact same salt
on the Countly server (Management > Applications> Salt for checksum). When the salt
on the Countly server is set, all requests would be checked for the validity of the &checksum
field before being processed.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "https://yourdomain.com";
Countly.salt = "your_salt";
// init after
Countly.init({
app_key: "YOUR_APP_KEY",
url: "http://yourdomain.com",
salt: "your_salt"
});
Other Features and Notes
SDK Config Parameters Explained
Here are the properties you may set up upon Countly initialization:
- app_key - mandatory, app key for your app created in Countly
- device_id - to identify a visitor, will be autogenerated if not provided
- url - your Countly server URL - you may also use your own server URL or IP here
- app_version - (optional) the version of your app or website
- country_code - (optional) country code for your visitor
- city - (optional) name of the city of your visitor
- ip_address - (optional) IP address of your visitor
- debug - output debug info into the console (default: false)
- ignore_bots - option to ignore traffic from bots (default: true)
- interval - set an interval for how often inspections should be made to see if there is any data to report and then report it (default: 500 ms)
- queue_size - the maximum amount of queued requests to store (default: 1000)
- fail_timeout - set the time to wait in seconds after a failed connection to the server (default: 60 seconds)
- inactivity_time - the time limit after which a user will be considered inactive if no actions have been made. No mouse movement, scrolling, or keys pressed. Expressed in minutes (default: 20 minutes)
- session_update - how often a session should be extended, expressed in seconds (default: 60 seconds)
- max_events - maximum amount of events to send in one batch (default: 100)
- max_breadcrumb_count - the maximum amount of breadcrumbs to store for crash logs (default: 100)
- ignore_referrers - array with referrers to ignore (default: none)
- salt - string salt for checksums (default: none)
- ignore_prefetch - ignore prefetching and pre-rendering from counting as real website visits (default: true)
- heatmap_whitelist - Array of trusted domains (as string) that can trigger heatmap script loading. By default the SDK whitelists your server url.
- force_post - force using post method for all requests (default: false)
- ignore_visitor - ignore this current visitor (default: false)
- require_consent - Pass true if you are implementing GDPR compatible consent management. This would prevent running any functionality without proper consent (default: false)
- utm - object instructing which UTM parameters to track (default: {"source":true, "medium":true, "campaign":true, "term":true, "content":true})
- use_session_cookie - use cookies to track sessions (default: true)
- session_cookie_timeout - how long until a cookie session should expire, expressed in minutes (default: 30 minutes)
- remote_config - enable automatic remote config fetching, provide the callback function to be notified when fetching is complete (default: false)
- rc_automatic_optin_for_ab - opts in the user for A/B testing while fetching the remote config (default: true)
- use_explicit_rc_api - set it to true to use the explicit remote config API (default: false)
- namespace - have a separate namespace for persistent data when using multiple trackers on the same domain
- track_domains - Set to false to disable domain tracking, so no domain data would be reported (default: true)
- headers - object to override or add headers to all SDK requests
- storage - What type of storage to use, by default uses local storage and would fallback to cookies, but you can set values "localstorage" or "cookies" to force only specific storage, or use "none" to not use any storage and keep everything in memory
- metrics - provide metrics override or custom metrics for this user. For more information on the specific metric keys used by Countly, check here.
Setting up properties on the Countly Web SDK is as follows (use your own server name if not using try.count.ly below):
Countly.debug = false;
Countly.app_key = "YOUR_APP_KEY";
Countly.device_id = "1234-1234-1234-1234";
Countly.url = "https://try.count.ly";
Countly.app_version = "1.2";
Countly.country_code = "LV";
Countly.city = "Riga";
Countly.ip_address = "83.140.15.1";
Countly.init({
debug:false,
app_key:"YOUR_APP_KEY",
device_id:"1234-1234-1234-1234",
url: "https://try.count.ly",
app_version: "1.2",
country_code: "LV",
city: "Riga",
ip_address: "83.140.15.1"
});
Example Integrations
Examples are located in examples folder.
Angular folder contains a basic Angular integration.
react folder contains a basic React integration.
Symbolication folder covers crash integrations and reporting.
mpa folder contains basic MPA integration.
example_apm is a simple APM integration.
example_apm_async is a simple async APM integration.
example_formdata is a simple integration to collect data from form data.
example_gdpr is a simple integration to show how giving consent works.
example_multi_instances is a simple demonstration for multi instancing the Countly SDK.
example_opt_out is a simple demonstration to show how to opt out from services.
examples_feedback_widgets is a simple integration for feedback widgets.
example_remote_config is a simple integration for remote config.
example_sync is a sample integration for sync operations.
example_web_worker is a sample for web worker.
example_async is a sample integration for async usage of the SDK.
SDK Storage and Requests
Countly Web SDK stores various information like device ID, request queue, session information and more in your device. This helps Countly to provide data consistency and enable convenience methods like offline mode.
The default storage location of user-specific data, except the session information, is your browser’s local storage. Information stored here is persistent, and as long as it was not erased or overwritten, it will stay on your device indefinitely. However, Countly allows you to change this behavior by selecting persistent cookies as the main storage option or choosing not to store any data at all, depending on your needs. These storage options are mutually exclusive, meaning only one option can be selected at a given time.
If cookies were selected as the main storage medium, persistent cookies have an expiration date and the information stored in them would be rendered obsolete after a while. In case of the session information, it is stored in session cookies and would expire when the tab or browser is closed. Lastly, if you decide not to store any information, all information will stay in memory and would be gone when the memory is cleared.
These options can be selected during the initialization:
//possible options are "localstorage", "cookies" and "none"
Countly.storage = "localstorage";
//possible options are "localstorage", "cookies" and "none"
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
storage: "localstorage"
});
Automatically Fill User Data
In most cases, you won’t know anything about your users, yet you will still want to try to collect any data possible. We provide 2 helper methods for this exact reason.
Collect User Data From Filled Forms
This method will look into the forms filled out by your users and will try to gather data, such as names, email addresses, usernames, etc.
All forms will automatically be checked, but you have the option to provide a form element if you would like to collect data only from a specific form, or select a method multiple times for different forms. Also, if you are already providing data for users, then you would not want to overwrite it. You may set the third parameter as true to indicate that data found should be stored in custom properties.
//collect data from forms
Countly.q.push(['collect_from_forms']);
//collect data from specific form
Countly.q.push(['collect_from_forms', formElement]);
//collect from forms and report as custom user properties
Countly.q.push(['collect_from_forms', document, true]);
//collect data from forms
Countly.collect_from_forms();
//collect data from specific form
Countly.collect_from_forms(formElement);
//collect from forms and report as custom user properties
Countly.collect_from_forms(document, true);
Passwords and other sensitive data will be omitted, however, if you would explicitly like to exclude some form input from being processed, just add the css class cly_user_ignore to that element. Oppositely, you may need to specify data from this input to be collected as the provided key by adding the prefixed css class cly_user_key. Therefore, if you would like to store data as a name, you should specify the cly_user_name css class.
<form method='post' name='test_form'>
<!-- data will be checked in this input -->
<p><input type="text" name="e" value="myemail@mydomain.com"></p>
<!-- ignore this input -->
<p><input type="text" name="e" value="notmyemail@notmydomain.com" class="cly_user_ignore"></p>
<!-- get customfield by class -->
<p><input type="text" name="custom" id="custom" value="value" class="cly_user_key1"></p>
<p><input id="submit-form" type="submit" value="Submit"></p>
</form>
Collect User Data From Facebook
If your website uses the Facebook JavaScript SDK, you may use this helper method to automatically collect user data from their Facebook accounts. Select the method right after Facebook SDK initialization and optionally set the object with custom properties and graph paths for values on where to receive them.
Here is an example how to receive data from Facebook, including locations and time zones as custom properties.
<script src="https://connect.facebook.net/en_US/all.js"></script>
<script type="text/javascript">
FB.init({
appId: '251676171676751',
status: true,
cookie: true,
oauth: true
});
function CountlyGatherFBData(){
Countly.collect_from_facebook({"location":"location.name", "tz":"timezone"});
};
FB.getLoginStatus(function(stsResp) {
if(stsResp.authResponse) {
CountlyGatherFBData();
} else {
FB.login(function(loginResp) {
if(loginResp.authResponse) {
CountlyGatherFBData();
} else {
alert('Please authorize this application to use it!');
}
});
}
});
</script>
Attribution
When using Countly attribution analytics, you may also report conversions to the Countly server, e.g. when a visitor purchases an item or registers on your site.
If a user visits your website through the Countly campaign link, the campaign information will be automatically stored by default for this user and used when reporting conversion. If conversion has not yet been reported, the campaign information will be overwritten when that user visits through another campaign link. When you report the conversion, it would report the latest campaign user used to access your website.
However, you may also overwrite that data and provide any specific campaign ID for which you would like to report conversion.
Conversion will not be reported if there is no stored campaign data and you have yet to provide a campaign ID.
Note: Conversion for each user may only be reported once, all other conversions will be ignored for that same user.
//user stored conversion data
Countly.q.push(['recordDirectAttribution']);
//or provide campaign id yourself
Countly.q.push(['recordDirectAttribution', "MyCampaignID"]);
//user stored conversion data
Countly.recordDirectAttribution();
//or provide campaign id yourself
Countly.recordDirectAttribution("MyCampaignID");
Track Link Clicks
This method will track clicks to specific links and will report events with the linkClick key as well as the link's text, ID, and URLs as segments.
All links will automatically be tracked by default for the entire page, but you may provide the parent node as a parameter for which to track link clicks.
Countly.q.push(['track_links']);
Countly.track_links();
As soon as you include this one-liner coder, you will automatically be able to see the "linkClick" event in your dashboard as data flows in with the text, ID, and URLs as segments.
Track Form Submissions
This method will automatically track form submissions and collect form data. It will then input values in the form and report them as a Event with the formSubmit key.
All forms will automatically be tracked by default for the entire page, but you may provide the parent node as a parameter for which to track forms.
The second parameter controls whether to collect hidden inputs or not. Hidden inputs are not collected by default.
//will not collect hidden inputs
Countly.q.push(['track_forms']);
//will collect hidden inputs
Countly.q.push(['track_forms', null, true]);
//will not collect hidden inputs
Countly.track_forms();
//will collect hidden inputs
Countly.track_forms(null, true);
Using the Web SDK in Webview
If you are going to use the Web SDK in the Webview of your app, there are prerequisites that must be checked to ensure it is fully functioning. There are no known iOS issues at this moment, but some specific settings need to be enabled for Android.
Ensure JavaScript has been enabled for your Webview
myWebView.getSettings().setJavaScriptEnabled(true);
Ensure local storage has been enabled
//change the path to where you want to store local storage data
myWebView.getSettings().setDomStorageEnabled(true);
myWebView.getSettings().setDatabaseEnabled(true);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
myWebView.getSettings().setDatabasePath("/data/data/" + myWebView.getContext().getPackageName() + "/databases/");
}
If you would like to use Countly both in the native app and Webview, then you would maybe also like to match the device_id between them, so the transitions may be seamless and you may continue to track events and data from both for the same user.
In this case, there are a couple things you should do:
- Defer initializing Countly by putting the initialization code in some function.
- Do not track sessions in Webview as they are already tracked by the native app.
- Pass the device_id to Webview and run the initialization function once Webview has loaded.
<!--Countly script in webview-->
<script type='text/javascript'>
var Countly = Countly || {};
Countly.q = Countly.q || [];
//provide countly initialization parameters
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "http://yourdomain.com";
//track views or anything else you want to track
Countly.q.push(['track_pageview']);
//function to initialize Countly
function InitializeCountly(device_id) {
//assign passed device_id
Countly.device_id = device_id;
//load countly script
var cly = document.createElement('script'); cly.type = 'text/javascript';
cly.async = true;
//enter url of script here
cly.src = 'https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/lib/countly.min.js';
cly.onload = function(){Countly.init()};
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(cly, s);
};
</script>
Then, on the native app side, all you have to do is call the JavaScript functions in the Webview and pass on the device_id to complete this function.
myWebView.loadUrl("javascript:InitializeCountly('"+device_id+"');");
#import "CountlyDeviceInfo.h"
NSString \*js = [NSString stringWithFormat: @"InitializeCountly('%@');", CountlyDeviceInfo.sharedInstance.deviceID];
[myWebView stringByEvaluatingJavaScriptFromString:js];
Tracking Users with Javascript Disabled
In some cases, a user might have JavaScript disabled, meaning normal ways of tracking those users will prove ineffective. In such a case, you may use the transparent 1px x 1px image hosted on your Countly server as reporting the URL and report all the same parameters as all the SDKs have been described here.
Assuming your Countly is hosted at domain.com and the app_key is "12345", the default setup should look like this:
<noscript><img src='http://domain.com/pixel.png?app_key=12345&begin_session=1'/></noscript>
Simply place it anywhere in your HTML code and it should only work if JavaScript is disabled, which means SDK tracking won't work.
This would then track how many total sessions there are in where users have JavaScript disabled. The server will attach the device_id as a no_js by default and add the user profile name as ‘No JS’, so you may easier identify how many users without JavaScript you have.
However, as mentioned before, this accepts any parameters as a normal SDK endpoint does. Thus, if you dynamically generate data via the server, you may also dynamically generate this URL to provide information that you have about the user. That might be the device_id parameter (for identification), OS, OS version, and any other metrics or information you have, as for example
<noscript><img src='http://domain.com/pixel.png?app_key=12345&device_id=test@test.com&begin_session=1&metrics={"_os":"Android", "_os_version":"4.1"}'/></noscript>
Multiple Trackers on the Same Domain
Sometimes you would like to track different parts of the same domain/website as separate applications.
If you just instantiate the Countly SDK in both places, but with different app IDs, then both of their continuous storages will clash storing information for both apps. There are times when this is exactly what you would like to have happen. Generally, it’s function will share a device_id, and although they might be storing requests together, each will be sent to a different app on the same server.
However, there are situations where you would like to keep them completely separate. To do so you will need to provide a namespace for the different trackers to keep their local storages from clashing.
Simply providing a namespace as the init option will suffice.
Countly.debug = false;
Countly.app_key = "YOUR_APP_KEY";
Countly.device_id = "1234-1234-1234-1234";
Countly.url = "https://try.count.ly";
Countly.namespace = "forum";
Countly.init();
Countly.init({
debug:false,
app_key:"YOUR_APP_KEY",
device_id:"1234-1234-1234-1234",
url: "https://try.count.ly",
namespace: "forum"
});
Cross Website/Domain Tracking
You will need to use the same device_id value on the same user in both places to track the same user across different websites or domains.
This may be achieved by passing the device ID as a URL parameter when transferring a user from one website/domain to the other.
Simply take the device ID value from Countly.device_id and pass it as a URL parameter named cly_device_id as follows: http://newdomain.com/?cly_device_id=your-user-device-id.
Tracked Cookie List and Explanations
By default, Countly Web SDK uses local storage to keep information between page views, but if local storage is not available, Web SDK will try to fallback to cookies.
- cly_queue - a queue of requests that will be made to the server and acknowledged
- cly_event - a queue of events reported by SDK
- cly_remote_configs - cached remote config from server
- cly_ignore - ignore the user and do not track anything if this is set to true
- cly_id - current user's device id
- cly_cmp_id - last campaign id user came from
- cly_cmp_uid - user identifier for last campaign
- cly_session - timestamp when the last session started so we would not call begin_session on each page load
- cly_token - token passed for retrieving heat map data from the server
- cly_old_token - to detect token expiration and reset for action map data
Multi Instancing
You can initialize Countly multiple times at the same page with different app keys to send information to different apps you own and gather data with higher flexibility and precision. These new instances have all functionality of Countly but depending on your init configuration they would behave differently. You can attach different events to different Countly instances to send events to specific applications even from the same button trigger or much more. A simple integration example would be:
Countly = Countly || {};
Countly.q = Countly.q || [];
// initializing first instance, which will be global Countly
Countly.init({
app_key: "YOUR_APP_KEY_1",
url: "https://try.count.ly" //your server goes here
})
// report event to first app
Countly.add_event({
key:"first_app"
});
// initialize second instance for another app
Countly.q.push(["init", {
app_key: "YOUR_APP_KEY_2", //must have different APP key
url: "https://try.count.ly" //your server goes here
}])
// report event to second app asynchronously by passing app key as first argument
Countly.q.push(["YOUR_APP_KEY_2", "add_event", {
key:"second_app"
}]);
// initializing first instance, which will be global Countly
Countly.init({
app_key: "YOUR_APP_KEY_1",
url: "https://try.count.ly" //your server goes here
})
// report event to first app
Countly.add_event({
key:"first_app"
});
// initialize second instance for another app
var Countly2 = Countly.init({
app_key: "YOUR_APP_KEY_2", //must have different APP key
url: "https://try.count.ly" //your server goes here
});
// report event to second app
Countly2.add_event({
key:"second_app"
});
SDK Internal Limits
If values or keys provided by the user exceeds certain internal limits, they will be truncated. Please have a look here for the list of properties effected by these limits.
You can override these internal limits during initialization.
Key Length
max_key_length
- 128 chars by default. Keys that exceed this limit will be truncated.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.max_key_length = 50;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "YOUR_SERVER_URL",
max_key_length: 50
});
Value Size
max_value_size
- 256 chars by default. Values that exceed this limit will be truncated.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.max_value_size = 12;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "YOUR_SERVER_URL",
max_value_size: 12
});
Segmentation Values
max_segmentation_values
- 100 dev entries by default. Key/value pairs that exceed this limit in a single event will be removed.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.max_segmentation_values = 67;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "YOUR_SERVER_URL",
max_segmentation_values: 67
});
Breadcrumb Count
max_breadcrumb_count
- 100 entries by default. If the limit is exceeded, the oldest entry will be removed from stored breadcrumbs.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.max_breadcrumb_count = 45;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "YOUR_SERVER_URL",
max_breadcrumb_count: 45
});
Stack Trace Lines Per Thread
max_stack_trace_lines_per_thread
- 30 lines by default. Crash stack trace lines that exceed this limit (per thread) will be removed.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.max_stack_trace_lines_per_thread = 23;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "YOUR_SERVER_URL",
max_stack_trace_lines_per_thread: 23
});
Stack Trace Line Length
max_stack_trace_line_length
- 200 chars by default. Crash stack trace lines that exceed this limit will be truncated.
Countly.app_key = "YOUR_APP_KEY";
Countly.url = "YOUR_SERVER_URL";
Countly.max_stack_trace_line_length = 10;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "YOUR_SERVER_URL",
max_stack_trace_line_length: 10
});
Setting Maximum Request Queue Size
When you initialize Countly, you can specify a value for the queue_size flag. This flag limits the number of requests that can be stored in the request queue when the Countly server is unavailable or experiencing connection problems.
If the server is down, requests sent to it will be queued on the device. If the number of queued requests becomes excessive, it can cause problems with delivering the requests to the server, and can also take up valuable storage space on the device. To prevent this from happening, the queue_size flag limits the number of requests that can be stored in the queue.
If the number of requests in the queue reaches the queue_size limit, the oldest requests in the queue will be dropped, and the newest requests will take their place. This ensures that the queue doesn't become too large, and that the most recent requests are prioritized for delivery.
If you do not specify a value for the queue_size flag, the default setting of 1,000 will be used.
Countly.queue_size = 5000;
Countly.init({
app_key:"YOUR_APP_KEY",
url: "https://try.count.ly",
queue_size: 5000
});
UTM Tags
If you are providing possible users links to your website, and if you would like to track those users who have clicked those links, you can do so by using some utm tags within your links and Countly web SDK would recognize and record these users for you.
The web SDK offers you two options to set these tags in your links. First you can use the default tags that has been set to be recognized by the SDK by default. These tags are 'source', 'medium', 'campaign', 'term' and 'content'. If you add any or all of these tags into your links as query Countly would recognize and record them accordingly. An example usage of the default tags is as follows:
// basic structure of a link that contains all available default tags
yourUrl + ?utm_source=someValue&utm_medium=someValue&utm_campaign=someValue&utm_term=someValue&utm_content=someValue
//or you can omit tags
yourUrl + ?utm_source=someValue&utm_campaign=someValue
// yourUrl is the url of your site and someValue is any string value you want to pass
Second option is to define your custom utm tags during the initialization of Countly and use those tags in your links. You have to provide a map of key value pairs where the key is the tag and the value is 'true' if you want the Countly to check for that key in your links:
Countly.app_key = "YOUR_APP_KEY";
Countly.device_id = "1234-1234-1234-1234";
// add your custom tags and set their value to true
Countly.utm = {tag1: true, tag2: true};
// then your links should look like this:
yourUrl + ?utm_tag1=someValue&utm_tag2=someValue
Countly.init({
app_key:"YOUR_APP_KEY",
device_id:"1234-1234-1234-1234",
// add your custom tags and set their value to true
utm: {tag1: true, tag2: true}
});
// then your links should look like this:
yourUrl + ?utm_tag1=someValue&utm_tag2=someValue
The values you have provided will be captured and stored as user properties, under the keys 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term' and 'utm_content' if you have used the default tags or as 'utm_yourCustomTag1', 'utm_yourCustomTag1'... if you have used custom tags, together with the corresponding user when a user clicks that link. This will enable you to segment users in all places around the dashboard, where granular data is used and segmentation capabilities are provided.
GA Adapter
If you are using Google Universal Analytics in your website and you would also like to integrate Countly to your project, GA Adapter plugin can help you send your Google Analytics data also to your Countly server without any extra integration. This plugin will recognize your Google Analytics data and convert them into Countly events and send it to your server, so you can observe your analytics data from your Countly dashboard with ease.
There are 3 steps you have to follow to enable the GA Adapter plugin:
- First integrate Countly into your project (You can reach details from here)
- Then declare the plugin script before your Google Analytics implementation block
- Finally, add 'CountlyGAAdapter();' into your Google Analytics snippet
A simple implementation would look something like this:
// ...
// ...
// ... Countly implementation was here
// </script>
// write the correct path to the GA plugin depending on your project structure
<script src="../plugin/ga_adapter/ga_adapter.js"></script>
// Google Analytics implementation
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
// add this line into your google analytics snippet to use the GA plugin
CountlyGAAdapter();
// now Countly will recognize the GA commands like below and send them to your Countly server too
ga('create', 'UA-56295140-3', 'auto');
ga('send','event','category','action','label');
ga('send','pageview','page.html');
</script>
You can reach to an example implementation of this plugin from the following link:
Running the SDK in Web Worker Context
SDK can be used in a dedicated Worker if needed. Currently Module Workers are not supported so instead the SDK should be imported by importScripts
method. It would look something like this:
// Path or URL
importScripts("../path/to/countly.js");
After importing the Countly script in your worker, you can call the Countly methods as usual. However, Countly in Web Workers has limited availability. Currently only the manual tracking methods are supported. Moreover the SDK would not use persistent storage by default. But now it extends its storage methods with which you can provide your own storage logic to make things persistent.
A sample worker (let's say 'worker.js') could look like this:
importScripts("../path/to/countly.js"); // CDN is possible
const STORE={}; // in-memory storage for worker
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.domain.countly",
debug: true,
storage: {
// getItem will recieve a string key param with which it should return the item under it
getItem: function (key) {
return STORE[key];
},
// setItem will recieve two params, a string key and a value of any type, then it should store the value under the key
setItem: function (key, value) {
STORE[key] = value;
},
// removeItem will recieve a string key with which it should erase the key and any data under that key
removeItem: function (key) {
delete STORE[key];
}
}
});
onmessage = function (e) {
console.log(`Worker: Message received from main script:[${JSON.stringify(e.data)}]`);
// Get an process messages to worker
const data = e.data.data; const type = e.data.type;
if (type === "event") { // you can send an event
Countly.add_event(data);
} else if (type === "view") { // you can record a view
Countly.track_pageview(data);
} else if (type === "session") { // you can manually control sessions
if (data === "begin_session") {
Countly.begin_session();
return;
}
Countly.end_session(null, true);
}
}
In your website, an example communication can happen like this with the worker:
// create a worker
const myWorker = new Worker("worker.js");
// send messages to the Worker
function clickEvent() { // send event
myWorker.postMessage({ type: "event", data: myEvent });
}
function recordView() { // track views
myWorker.postMessage({ type: "view", data: "home_page" });
}
function beginSession() { // start a session
myWorker.postMessage({ type: "session", data: "begin_session" });
}
function endSession() { // end a session
myWorker.postMessage({ type: "session", data: "end_session" });
}
Opt In / Opt Out
The Countly SDK will always be opt in by default, meaning all tracking is enabled, but you may easily disable all tracking by selecting the opt_out method. It will persistently save settings to prevent tracking after page reloads. Then if you want to opt in again you can use opt_in method to resume tracking at next page load.
// to stop tracking user data at next page load
Countly.q.push(['opt_out']);
// to resume tracking user data at next page load
Countly.q.push(['opt_in']);
// to stop tracking user data at next page load
Countly.opt_out();
// to resume tracking user data at next page load
Countly.opt_in();
Disabling tracking for specific users with these methods can be sufficient for testing or situations where you decide to not track a specific user's information from now on. However, should you desire more granular feature control, checkout the User Consent section.
If you would like to have opt out selected by default, combine these methods with the initial setting ignore_visitor on the Countly init object.
Extended Feedback Widget Options
If you have many widgets and the way you want to show them is intricate you can use the methods shown below.
All three widgets use the same API to fetch feedbacks from the server as well as to display them to the end user. By default, the created widget will be appended to the end of the html document. In some scenarios you might prefer to have the widget injected in a specific element. For those scenarios we have added optional selectors. The first one is used for selecting an element by it's id and the second one is used to select the element by it's class selector. If you want to inject the feedback widget in a specific element, you can do so by specifying the element ID or the class name. You can also add custom segmentation while presenting a widget.
To use feedback widgets, you need to give "feedback" consent (in case consent is required).
//Fetch user's feedback widgets from the server
Countly.q.push(['get_available_feedback_widgets', feedbackWidgetsCallback]);
// Feedback widget callback function, err is for error and countlyPresentableFeedback contains an array of widhet objects
function feedbackWidgetsCallback(countlyPresentableFeedback, err) {
if (err) {
console.log(err);
return;
}
// Decide which widget to show. Here, the first rating widget is selected.
const widgetType = "rating";
const countlyFeedbackWidget = countlyPresentableFeedback.find(widget = widget.type === widgetType);
if (!countlyFeedbackWidget) {
console.error(`[Countly] No ${widgetType} widget found`);
return;
}
//Define the element ID and the class name (optional, pass undefined if you don't use)
const selectorId = "targetIdSelector";
const selectorClass = "targetClassSelector";
// Define the segmentation (optional)
const segmentation = { page: "home_page" };
//Display the feedback widget to the end user
Countly.present_feedback_widget(countlyFeedbackWidget, selectorId, selectorClass, segmentation);
}
//Fetch user's feedback widgets from the server
Countly.get_available_feedback_widgets(feedbackWidgetsCallback);
// Feedback widget callback function, err is for error and countlyPresentableFeedback contains an array of widget objects
function feedbackWidgetsCallback(countlyPresentableFeedback, err) {
if (err) {
console.log(err);
return;
}
// Decide which widget to show. Here, the first rating widget is selected.
const widgetType = "rating";
const countlyFeedbackWidget = countlyPresentableFeedback.find(widget => widget.type === widgetType);
if (!countlyFeedbackWidget) {
console.error(`[Countly] No ${widgetType} widget found`);
return;
}
//Define the element ID and the class name (optional, pass undefined if you don't use)
const selectorId = "targetIdSelector";
const selectorClass = "targetClassSelector";
// Define the segmentation (optional)
const segmentation = { page: "home_page" };
//Display the feedback widget to the end user
Countly.present_feedback_widget(countlyFeedbackWidget, selectorId, selectorClass, segmentation);
}
Note: Feedback Widgets' show policies are handled internally by the Web SDK.
Extended Device ID Management
In some cases, you may want to change the ID of the user/device that you provided or Countly automatically generated, e.g. when a user was changed.
Countly.q.push(['change_id', "myNewId"]);
Countly.change_id("myNewId");
If device ID is changed without merging and consent was enabled, all previously given consent will be removed. This means that all features will cease to function until new consent has been given again for that new device ID.
In some other cases, you may also need to change a user's device ID in a way so that that server will merge the data of both user IDs (both the new and existing ID you provided) on the server, e.g. when a user used the website without authenticating and recorded some data and then authenticated, and you would like to change the ID to your internal ID of this user to keep tracking it across multiple devices.
This call will merge any data recorded for the current ID and save it as a user with a newly provided ID.
Countly.q.push(['change_id', "myNewId", true]);
Countly.change_id("myNewId", true);
FAQ
Can I integrate Countly Web SDK to my TypeScript Project
TypeScript is a strict syntactical superset of JavaScript. It helps you catch errors early by adding static typing to the language. It compiles down to basic JavaScript so it can be used anywhere JavaScript can run, whether Node.js or browser.
Countly Web SDK is written in basic JavaScript so it is compatible with your TypeScript projects by enabling allowJs in your project's tsconfig.json file. However as we use javascript features that can run in the browser, your project must also be runnable on the browser.
Ignoring your own bots
The default behavior of Countly Web SDK is to ignore bots crawling your site to provide you a more accurate user analytics data. However, Countly can't detect and block all bots crawling the internet as we use the userAgent string to detect bots and spammy bots can hide by providing conventional UserAgent. However, this is not the case for a bot that you have created your own.
To include your bots to be also ignored by the Countly Web SDK you have to include 'CountlySiteBot' in your userAgent string. This enables your SDK to recognize your bot as one of the bots to be ignored and the SDK would stop recording data for your bot.
Why aren’t I able to see AngularJS errors on the Countly dashboard?
AngularJs swallows errors by default. You will need to extend Angular's $exceptionHandler
to call Countly.log_error()
. For more information, see this blog post.
Incognito mode and ad blockers
Incognito mode prevents the browser from storing cookies and other site data. Cookies are small files that websites use to track user activity, remember preferences, and provide personalized experiences. In incognito mode, cookies are not saved. This means that any information our SDK saves in the browser's local storage would be gone the next time the user opens their incognito browser. These things include things like device ID and event/request queues. Each time a person visits a Countly integrated website in incognito mode, they would be perceived as a new user. This can be mitigated by having an authentication page on your website.
Ad blockers employ various techniques to identify and block unwanted requests, such as analyzing URL patterns, known tracking domains, or specific JavaScript code snippets. They can also detect and block requests made to known analytics or tracking services, making it possible for them to block requests made to the Countly server from our SDKs. This can result in incomplete or missing data, as the blocked requests may not reach the Countly server for processing. This is a possibility, but not all ad blockers would be blocking our requests, depending on their filter settings.
To mitigate this issue to a certain extent, you can enable force using the POST method for all requests with the SDK. Switching to a POST request can make it slightly harder for ad blockers to detect and block the request, as the parameters are sent in the request body instead of the URL. However, it doesn't make the request invisible or immune to blocking. Ad blockers can still analyze network traffic and inspect the request headers and payload, so if your server is Countly hosted and the domain name is in the filter of the ad blocker, it would still be blocked.
What Information is Collected by the SDK?
The data that SDKs gather to carry out their tasks and implement the necessary functionalities is mentioned in here