If you are using InstantSearch, it’s best to send events directly through InstantSearch. If you aren’t, you can send click, conversion, and other events using one of the API clients. The API clients provide dedicated methods for each type of event.
This guide uses an online marketplace as an example. It includes code snippets for how to send events when a user clicks on search results or engages in other behaviors you’ve decided are worth tracking. The queryID
, positions
, objectIDs
, and userToken
values in these examples are placeholders. Be sure to adjust these values for your implementation.
Since you can send events with just a few lines of code, it’s tempting to jump right in and do it. Before that, it’s best to read the best practices for sending events.
Initializing the Insights client
1
2
3
4
| $insights = Algolia\AlgoliaSearch\InsightsClient::create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
);
|
1
| insights = Algolia::Insights::Client.create('YourApplicationID', 'YourSearchOnlyAPIKey')
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <!-- This requires installing the search-insights separate library: -->
<!-- https://github.com/algolia/search-insights.js -->
<!-- You can load the library by adding this snippet in the <head> of your HTML file. -->
<script>
var ALGOLIA_INSIGHTS_SRC = "https://cdn.jsdelivr.net/npm/search-insights@1.6.3";
!function(e,a,t,n,s,i,c){e.AlgoliaAnalyticsObject=s,e[s]=e[s]||function(){
(e[s].queue=e[s].queue||[]).push(arguments)},i=a.createElement(t),c=a.getElementsByTagName(t)[0],
i.async=1,i.src=n,c.parentNode.insertBefore(i,c)
}(window,document,"script",ALGOLIA_INSIGHTS_SRC,"aa");
</script>
<!-- You can also load the library using NPM. -->
<!-- https://www.npmjs.com/package/search-insights -->
<!-- Compatible with Node.js since v1.3.0 -->
|
1
2
3
4
| insights = InsightsClient.create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
)
|
1
2
3
4
5
| Insights.register(
appId: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
userToken: "user-123456"
)
|
1
2
3
4
| val client = ClientInsights(
applicationID = ApplicationID("YourApplicationID"),
apiKey = APIKey("YourSearchOnlyAPIKey")
)
|
1
2
3
4
| var insights = new InsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
);
|
1
2
3
4
| InsightsClient insights = DefaultInsightsClient.create(
"YourApplicationID",
"YourSearchOnlyAPIKey"
);
|
1
2
3
4
5
| client := insights.NewClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
)
userClient := client.User("user-123456")
|
1
| val client = new AlgoliaClient("YourApplicationID", "YourSearchOnlyAPIKey")
|
1
2
3
4
| val client = ClientInsights(
applicationID = ApplicationID("YourApplicationID"),
apiKey = APIKey("YourSearchOnlyAPIKey")
)
|
The JavaScript, iOS, and Android API clients require you to install the search-insights
library to send events. For installation instructions, please see their respective pages:
You must use the an API key with the search
ACL to send events.
Search-related events must occur within one hour of their search. In other words, after a user searches, the analytics engine only counts events related to that search if they have a timestamp
within one hour of the search. Events must be received by the insights API within four days of their occurrence.
Imagine a user arriving at the marketplace and beginning a search for “harry potter”. Any behavior related to this and other searches, whether it be clicking on a result, wishlisting a result, or eventually making a purchase, should be classified as search-related events.
To properly denote that an event is search-related, you must use the appropriate API methods:
The only difference between these methods and the corresponding methods for events that are unrelated to search, is that they require a queryID
.
Retrieving the queryId
The first step to retrieve the queryID
is to set the clickAnalytics
search parameter to true
.
1
2
3
| $res = $index->search('query', [
'clickAnalytics' => true
]);
|
1
2
3
| results = index.search('query', {
clickAnalytics: true
})
|
1
2
3
4
5
| index.search('query', {
clickAnalytics: true
}).then(({ hits }) => {
console.log(hits);
});
|
1
2
3
4
| res = index.search(
'query',
{'clickAnalytics': True}
)
|
1
2
3
4
5
6
7
8
| let query = Query("query")
.set(\.clickAnalytics, to: true)
index.search(query: query) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
|
1
2
3
4
| index.search(
new Query("query")
.setClickAnalytics(true)
);
|
1
2
3
4
5
| index.Search(
new Query("query") {
ClickAnalytics = true
}
);
|
1
2
3
4
| index.search(
new Query("query")
.setClickAnalytics(true)
);
|
1
2
3
4
| index.Search(
"query",
opt.ClickAnalytics(false),
)
|
Once you’ve done this, each results set includes the queryID
field. The queryID
is unique for every search made to Algolia. For example, Algolia returns a new queryID
each time a user appends a new character in a search-as-you-type implementation. In this user’s case:
- The first keystroke (for the character “h”) has
aef12442b87e97ac
as queryID
.
- The second key stroke (for the characters “ha”) has
cba8245617aeace44
as queryID
.
If the user were to perform the same search at a different time, each query would receive a new unique queryID
. For accurate Click and Conversion Analytics, always use the latest queryID
with the API client methods.
After typing just two characters into the search bar, the user sees all the Harry Potter books in their results set. They’re interested in the last book of the series and click on it.
To track search-related clicks, you need to send events using the ClickedObjectIdsAfterSearch
API client method. This method requires the following parameters:
userToken
: This is the user’s unique user identifier, in this example user-123456
. Depending on the client, you may provide this during initialization rather than as a parameter.
eventName
: This is up to you—it’s best to follow the naming guidelines. This example uses the eventName:Product Clicked
.
indexName
: This is the clicked result’s index. In this case, the name of the index is products
.
objectIDs
: These are the clicked results’ objectID
s. The user is interested in the last book in the series, the item with objectID:9780545139700
, the ISBN-13 code of the book. Even if you’re sending only one item, you must send it in an array: ["9780545139700"]
.
positions
: These are the clicked results’ positions in the list of Algolia search results. Like objectIDs
, the parameter expects an array. Since the user’s interested in the last book of a seven-part series and the series are the first seven search results, the position is [7]
.
queryID
: This comes from the response of the last search request as previously described.
1
2
3
4
5
6
7
8
9
10
11
12
| $insights = Algolia\AlgoliaSearch\InsightsClient::create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
);
$insights->user("user-123456")->clickedObjectIDsAfterSearch(
'Product Clicked',
'products',
['9780545139700'],
[7],
'cba8245617aeace44'
);
|
1
2
3
4
5
6
7
8
9
| insights = Algolia::Insights::Client.create('YourApplicationID', 'YourSearchOnlyAPIKey')
insights.user('user-123456').clicked_object_ids_after_search(
'Product Clicked',
'products',
['9780545139700'],
[7],
'cba8245617aeace44'
)
|
1
2
3
4
5
6
7
8
9
10
11
12
| // This requires installing the search-insights separate library:
// https://github.com/algolia/search-insights.js
// https://www.npmjs.com/package/search-insights
aa('clickedObjectIDsAfterSearch', {
userToken: 'user-123456',
eventName: 'Product Clicked',
index: 'products',
queryID: 'cba8245617aeace44',
objectIDs: ['9780545139700'],
positions: [7],
});
|
1
2
3
4
5
6
7
8
9
| insights = client.init_insights_client().user('user-123456')
insights.clicked_object_ids_after_search(
'Product Clicked',
'products',
['9780545139700'],
['7l'],
'cba8245617aeace44'
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| Insights.register(
appId: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
userToken: "user-123456"
)
Insights.shared?.clickedAfterSearch(
eventName: "Product Clicked",
indexName: "products",
objectIDs: ["9780545139700"],
positions: [7],
queryID: "cba8245617aeace44"
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| Insights.register(
context,
"YourApplicationID",
"YourSearchOnlyAPIKey",
"user-123456"
)
Insights.shared?.clickedAfterSearch(
"Product Clicked",
"products",
"cba8245617aeace44",
EventObjects.IDs("9780545139700"),
listOf(7)
)
|
1
2
3
4
5
6
7
8
9
10
11
12
| var insights = new InsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
).User("user-123456");
insights.ClickedObjectIDsAfterSearch(
"Product Clicked",
"products",
new List<string> { "9780545139700" },
new List<uint> { 7 },
"cba8245617aeace44"
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| AsyncUserInsightsClient insights = new AsyncInsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
client
).user("user-123456");
insights.clickedObjectIDsAfterSearch(
"Product Clicked",
"products",
Arrays.asList("9780545139700"),
new ArrayList<>(Arrays.asList(7l)),
"cba8245617aeace44"
);
|
1
2
3
4
5
6
7
8
9
10
11
12
| client := insights.NewClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
).User("user-123456")
res, err := client.ClickedObjectIDsAfterSearch(
"Product Clicked",
"products",
[]string{"9780545139700"},
[]int{7},
"cba8245617aeace44",
)
|
1
2
3
4
5
6
7
8
9
10
| client.execute {
send event ClickedObjectIDsAfterSearch(
"user-123456",
"Product Clicked",
"products",
Seq("9780545139700"),
Seq(7),
"cba8245617aeace44"
)
}
|
When the user sees the Harry Potter books in their search results, they decide to add the last two books of the series to their wishlist. Book six has an objectID of 9780439785969
and book seven has an objectID of 9780545139700
. This example considers wishlisting products as a conversion event.
Unlike sending click events, when sending conversion events, you don’t need to send the converted objects’ positions
with it. Other than that, you send the same data as with the previous click event. In this case, the eventName
is Product Wishlisted
.
1
2
3
4
5
6
7
8
9
10
11
| $insights = Algolia\AlgoliaSearch\InsightsClient::create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
);
$insights->user("user-123456")->convertedObjectIDsAfterSearch(
'Product Wishlisted',
'products',
['9780545139700', '9780439785969'],
'cba8245617aeace44'
);
|
1
2
3
4
5
6
7
8
| insights = Algolia::Insights::Client.create('YourApplicationID', 'YourSearchOnlyAPIKey')
insights.user('user-123456').converted_object_ids_after_search(
'Product Wishlisted',
'products',
['9780545139700', '9780439785969'],
'cba8245617aeace44'
)
|
1
2
3
4
5
6
7
8
9
10
11
| // This requires installing the search-insights separate library
// https://github.com/algolia/search-insights.js
// https://www.npmjs.com/package/search-insights
aa('convertedObjectIDsAfterSearch', {
userToken: 'user-123456',
index: 'products',
eventName: 'Product Wishlisted',
queryID: 'cba8245617aeace44',
objectIDs: ['9780545139700', '9780439785969']
});
|
1
2
3
4
5
6
7
8
| insights = client.init_insights_client().user('user-123456')
insights.converted_object_ids_after_search(
'Product Wishlisted',
'products',
['9780545139700', '9780439785969'],
'cba8245617aeace44'
)
|
1
2
3
4
5
6
7
8
9
10
11
12
| Insights.register(
appId: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
userToken: "user-123456"
)
Insights.shared?.convertedAfterSearch(
eventName: "Product Wishlisted",
indexName: "products",
objectIDs: ["9780545139700", "9780439785969"],
queryID: "cba8245617aeace44"
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| Insights.register(
context,
"YourApplicationID",
"YourSearchOnlyAPIKey",
"user-123456"
)
Insights.shared?.convertedAfterSearch(
"Product Wishlisted",
"products",
"cba8245617aeace44",
EventObjects.IDs("9780545139700", "9780439785969")
)
|
1
2
3
4
5
6
7
8
9
10
11
| var insights = new InsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
).User("user-123456");
insights.ConvertedObjectIDsAfterSearch(
"Product Wishlisted",
"products",
new List<string> { "9780545139700", "9780439785969" },
"cba8245617aeace44"
);
|
1
2
3
4
5
6
7
8
9
10
11
12
| AsyncUserInsightsClient insights = new AsyncInsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
client
).user("user-123456");
insights.convertedObjectIDsAfterSearch(
"Product Wishlisted",
"products",
Arrays.asList("9780545139700", "9780439785969"),
"cba8245617aeace44"
);
|
1
2
3
4
5
6
7
8
9
10
11
| client := insights.NewClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
).User("user-123456")
res, err := client.ConvertedObjectIDsAfterSearch(
"Product Wishlisted",
"products",
[]string{"9780545139700", "9780439785969"},
"cba8245617aeace44",
)
|
1
2
3
4
5
6
7
8
9
| client.execute {
send event ConvertedObjectIDsAfterSearch(
"user-123456",
"Product Wishlisted",
"products",
Seq("9780545139700", "9780439785969"),
"cba8245617aeace44"
)
}
|
If your UI enables the user to go directly to a conversion point, make sure that you are capturing all relevant events that precede the conversion. For example, if you’ve implemented a “Buy now” or one-click buy, you need to process both the click and conversion events before proceeding with the actual sale.
While you should send as many types of click
and conversion
events as are meaningful for your use case, Algolia calculates one overall click-through rate and one overall conversion rate.
Tracking the queryID
The previous conversion code snippet works well if you send conversion events directly from the search results page. It works because the search page contains the queryID
, and other necessary information, such as the index
name and the objectID
, to send the event. However, not all click and conversion events originate from the search page, even if they’re related to a search.
For example, a user could add a product to their shopping cart from the product detail page. In this case, you need to pass the queryID
and other necessary information from the search page to the detail page.
There are several solutions to this problem. For web applications, you could use localStorage, cookies, a query string, or other methods. See the guide on keeping track of queryID
for more information.
You can also send click
, conversion
, and other events that are unrelated to search. While these events won’t be taken into account when calculating Click and Conversion Analytics they’re useful for the Personalization feature. Personalization relies on user behavior—related or unrelated to search—to build user affinity profiles. Algolia can then use these profiles to personalize results for the individual user.
Even if you aren’t planning on implementing Personalization yet, it’s a good idea to collect these events so that you have a corpus of data ready for you when you do.
While you can only send click and conversion events for behaviors related to search, there is a greater variety of user behavior you can capture that is unrelated to search. Specifically, you can send view
events and events that are related to categories rather than specific items.
For example, if a user decides to browse a particular category page, you can send a view
event for the category, rather than having to provide objectIDs
for all the items on the page. For more information and examples of these event types, please see the guide on capturing user behavior as events.
There are dedicated API methods for each of these events:
These methods require the same parameters as their search-related counterparts. The only difference is that they don’t require a queryID
.
Sending view events
Here is an example of how to send a view event when a user decides to browse a particular category page. In this case, the category is called best-sellers
.
1
2
3
4
5
6
7
8
9
10
| $insights = Algolia\AlgoliaSearch\InsightsClient::create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
);
$insights->user("user-123456")->viewedFilters(
'Category Page Viewed',
'products',
['category:best-sellers']
);
|
1
2
3
4
5
6
7
| insights = Algolia::Insights::Client.create('YourApplicationID', 'YourSearchOnlyAPIKey')
insights.user('user-123456').viewed_filters(
'Category Page Viewed',
'products',
['category:best-sellers']
)
|
1
2
3
4
5
6
7
8
9
10
| // This requires installing the search-insights separate library
// https://github.com/algolia/search-insights.js
// https://www.npmjs.com/package/search-insights
aa('viewedFilters', {
userToken: 'user-123456',
index: 'products',
eventName: 'Category Page Viewed',
filters: [ 'category:best-sellers' ]
});
|
1
2
3
4
5
6
7
| insights = client.init_insights_client().user('user-123456')
insights.viewed_filters(
'Category Page Viewed',
'products',
['category:best-sellers']
)
|
1
2
3
4
5
6
7
8
9
10
11
| Insights.register(
appId: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
userToken: "user-123456"
)
Insights.shared?.viewed(
eventName: "Category Page Viewed",
indexName: "products",
filters: ["category:best-sellers"]
)
|
1
2
3
4
5
6
7
8
9
10
11
12
| Insights.register(
context,
"YourApplicationID",
"YourSearchOnlyAPIKey",
"user-123456"
)
Insights.shared?.viewed(
"Category Page Viewed",
"products",
EventObjects.Filters("category:best-sellers")
)
|
1
2
3
4
5
6
7
8
9
10
| var insights = new InsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
).User("user-123456");
insights.ViewedFilters(
"Category Page Viewed",
"products",
new List<string> { "category:best-sellers" }
);
|
1
2
3
4
5
6
7
8
9
10
11
| AsyncUserInsightsClient insights = new AsyncInsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
client
).user("user-123456");
insights.viewedFilters(
"Category Page Viewed",
"products",
Arrays.asList("category:best-sellers")
);
|
1
2
3
4
5
6
7
8
9
10
| client := insights.NewClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
).User("user-123456")
res, err := client.ViewedFilters(
"Category Page Viewed",
"products",
[]string{"category:best-sellers"},
)
|
1
2
3
4
5
6
7
8
| client.execute {
send event ViewedFilters(
"user-123456",
"Category Page Viewed",
"products",
Seq("category:best-sellers)
)
}
|
A user might click on links to product detail pages or filter products without searching for them first. In that case, you can send click events without any queryID
.
1
2
3
4
5
6
7
8
9
10
| $insights = Algolia\AlgoliaSearch\InsightsClient::create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
);
$insights->user("user-123456")->clickedObjectIDs(
'Product Clicked',
'products',
['9780545139700']
);
|
1
2
3
4
5
6
7
| insights = Algolia::Insights::Client.create('YourApplicationID', 'YourSearchOnlyAPIKey')
insights.user('user-123456').clicked_object_ids(
'Product Clicked',
'products',
['9780545139700']
)
|
1
2
3
4
5
6
7
8
9
10
| // This requires installing the search-insights separate library:
// https://github.com/algolia/search-insights.js
// https://www.npmjs.com/package/search-insights
aa('clickedObjectIDs', {
userToken: 'user-123456',
index: 'products',
eventName: 'Product Clicked',
objectIDs: ['9780545139700'],
});
|
1
2
3
4
5
6
7
| insights = client.init_insights_client().user('user-123456')
insights.clicked_object_ids(
'Product Clicked',
'products',
['9780545139700']
)
|
1
2
3
4
5
6
7
8
9
10
11
| Insights.register(
appId: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
userToken: "user-123456"
)
Insights.shared?.clicked(
eventName: "Product Clicked",
indexName: "products",
objectIDs: ["9780545139700"]
)
|
1
2
3
4
5
6
7
8
9
10
11
12
| Insights.register(
context,
"YourApplicationID",
"YourSearchOnlyAPIKey",
"user-123456"
)
Insights.shared?.clicked(
"Product Clicked",
"products",
"cba8245617aeace44"
)
|
1
2
3
4
5
6
7
8
9
10
| var insights = new InsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
).User("user-123456");
insights.ClickedObjectIDs(
"Product Clicked",
"products",
new List<string> { "9780545139700" }
);
|
1
2
3
4
5
6
7
8
9
10
11
| AsyncUserInsightsClient insights = new AsyncInsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
client
).user("user-123456");
insights.clickedObjectIDs(
"Product Clicked",
"products",
Arrays.asList("9780545139700")
);
|
1
2
3
4
5
6
7
8
9
10
| client := insights.NewClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
).User("user-123456")
res, err := client.ClickedObjectIDs(
"Product Clicked",
"products",
[]string{"9780545139700"},
)
|
1
2
3
4
5
6
7
8
| client.execute {
send event ClickedObjectIDs(
"user-123456",
"Product Clicked",
"products",
Seq("9780545139700")
)
}
|
Similarly, a user might buy a product or take another action you’ve designated as a conversion event without making a search first. In that case, you can send conversion events without any queryID
.
1
2
3
4
5
6
7
8
9
10
| $insights = Algolia\AlgoliaSearch\InsightsClient::create(
'YourApplicationID',
'YourSearchOnlyAPIKey'
);
$insights->user("user-123456")->convertedObjectIDs(
'Product Purchased',
'products',
['9780545139700']
);
|
1
2
3
4
5
6
7
| insights = Algolia::Insights::Client.create('YourApplicationID', 'YourSearchOnlyAPIKey')
insights.user('user-123456').converted_object_ids(
'Product Purchased',
'products',
['9780545139700']
)
|
1
2
3
4
5
6
7
8
9
10
| // This requires installing the search-insights separate library:
// https://github.com/algolia/search-insights.js
// https://www.npmjs.com/package/search-insights
aa('convertedObjectIDs', {
userToken: 'user-123456',
index: 'products',
eventName: "Product Purchased',
objectIDs: ['9780545139700'],
});
|
1
2
3
4
5
6
7
| insights = client.init_insights_client().user('user-123456')
insights.converted_object_ids(
'Product Purchased',
'products',
['9780545139700']
)
|
1
2
3
4
5
6
7
8
9
10
11
| Insights.register(
appId: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
userToken: "user-123456"
)
Insights.shared?.converted(
eventName: "Product Purchased",
indexName: "products",
objectIDs: ["9780545139700"]
)
|
1
2
3
4
5
6
7
8
9
10
11
12
| Insights.register(
context,
"YourApplicationID",
"YourSearchOnlyAPIKey",
"user-123456"
)
Insights.shared?.converted(
"Product Purchased",
"products",
"cba8245617aeace44"
)
|
1
2
3
4
5
6
7
8
9
10
| var insights = new InsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey"
).User("user-123456");
insights.ConvertedObjectIDs(
"Product Purchased",
"products",
new List<string> { "9780545139700" }
);
|
1
2
3
4
5
6
7
8
9
10
11
| AsyncUserInsightsClient insights = new AsyncInsightsClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
client
).user("user-123456");
insights.convertedObjectIDs(
"Product Purchased",
"products",
Arrays.asList("9780545139700")
);
|
1
2
3
4
5
6
7
8
9
10
| client := insights.NewClient(
"YourApplicationID",
"YourSearchOnlyAPIKey",
).User("user-123456")
res, err := client.ConvertedObjectIDs(
"Product Purchased",
"products",
[]string{"9780545139700"},
)
|
1
2
3
4
5
6
7
8
| client.execute {
send event ConvertedObjectIDs(
"user-123456",
"Product Purchased",
"products",
Seq("9780545139700")
)
}
|