In this article, we will cover how the recommendation that is setup in the Klevu Merchant Center can be integrated in the different channels e.g. email marketing OR some internal marketing blog sites.
The Klevu recommendation works by default when the Klevu JS is available on the page but there are channels where Klevu JS can not be integrated and we need to use the Klevu APIs to fetch recommendation and display it. One of the very much asked use-case is email marketing. The idea is to fetch the API request of specific recommendation banner and use it to fetch the product data. Lastly we will go about how to fetch the personalized data.
Step 1: Executing Recommendation Configuration API
The first action would be to execute Recommendation Configuration API, This API is executed to get specific data to query into Klevu backend to fetch products. The JS API key and Recs banner ID are required for this action. Detailed documentation could be found here: https://docs.klevu.com/apis/smart-recommendations-configurations
Let's consider an example involving the trending products, denoted as RECS_TRENDING, from the store. By passing the recommendation banner ID for trending products in the API request, we will receive the relevant details in the response, which can then be used to proceed further.
Note: The example provided focuses on recommending trending products for the store. For recommendations specific to product pages or category pages, you will need to use the product ID or group product ID, or the category hierarchy, as specified in the configuration documentation. Additionally, to implement "Customers Also Bought" recommendations, please follow the API instructions detailed in the documentation directly on step 5th.
Request
GET https://config-cdn.ksearchnet.com/recommendations/klevu-170202994581416798/settings/k-5c7511c2-51f8-443c-957f-6a14b6bd902f
Response
{
"metadata": {
"title": "fdsfds",
"recsKey": "k-5c7511c2-51f8-443c-957f-6a14b6bd902f",
"pageType": "HOME",
"logic": "TRENDING",
"maxProducts": 10,
"productThreshold": 1,
"enabled": true,
"spotKey": "k-5c7511c2-51f8-443c-957f-6a14b6bd902f",
"spotName": "dfdsfdsf",
"segmentKey": null,
"segmentName": null,
"action": null
},
"search": {
"basePath": "eucs33v2.ksearchnet.com/cs/v2/search",
"payload": "{\"recordQueries\":[{\"id\":\"klevuRECSItemList\",\"typeOfRequest\":\"RECS_TRENDING\",\"settings\":{\"typeOfRecords\":[\"KLEVU_PRODUCT\"],\"limit\":10}}]}"
},
"templates": {
"base": "<% var metadata = data.metadata; var logic = (metadata.logic) ? metadata.logic : \"\"; var pageType = (metadata.pageType) ? metadata.pageType : \"\"; var recsKey = (metadata.recsKey) ? metadata.recsKey : \"\"; var title = (metadata.title) ? metadata.title : \"\"; %>\r\n<div class=\"kuRECSContainer\" data-logic=\"<%= logic %>\" data-recskey=\"<%= recsKey %>\" data-page-type=\"<%= pageType %>\">\r\n <% var recommendationItemList = klevu.getObjectPath(data,\"query.klevuRECSItemList.result\");%>\r\n <% if(recommendationItemList && recommendationItemList.length) { %>\r\n <div class=\"kuRECSHeader\">\r\n <h3 class=\"kuRECSTitle\" title=\"<%= klevu.dom.helpers.escapeHTML(title) %>\">\r\n <%= title %>\r\n </h3>\r\n </div>\r\n <div class=\"kuRECSWrap\">\r\n <div class=\"kuRECSResults\">\r\n <div class=\"kuRECSResultsInner\">\r\n <% helper.each(recommendationItemList, function(key, item){ %>\r\n <div class=\"kuRECS-itemWrap kuRECSItem\" data-id=\"<%= item.id %>\">\r\n <div class=\"kuRECS-item\">\r\n <div class=\"kuRECS-itemImg\">\r\n <a title=\"<%= klevu.dom.helpers.escapeHTML(item.name) %>\" href=\"<%= item.url %>\" data-id=\"<%= item.id %>\" class=\"kuRECSItemClick kuRECSImg\">\r\n <img src=\"<%= item.image %>\" origin=\"<%= item.image %>\" alt=\"<%= klevu.dom.helpers.escapeHTML(item.name) %>\" onerror=\"klevu.dom.helpers.cleanUpProductImage(this)\" class=\"prodImg\" width=\"100%\">\r\n </a>\r\n </div>\r\n <div class=\"kuRECS-itemDesc\">\r\n <a title=\"<%= klevu.dom.helpers.escapeHTML(item.name) %>\" href=\"<%= item.url %>\" data-id=\"<%= item.id %>\" class=\"kuRECSItemClick kuRECSTitle\">\r\n <%= item.name %>\r\n </a>\r\n <div class=\"kuRECS-itemPrice\">\r\n <% if(item.ondiscount && item.ondiscount == \"true\") { %>\r\n <% if(item.salePrice ) { %>\r\n <div class=\"kuSalePrice kuSpecialPrice\">\r\n <%=helper.processCurrency(item.currency,parseFloat(item.salePrice) )%>\r\n </div>\r\n <% } %>\r\n <% if(item.price) { %>\r\n <div class=\"kuOrigPrice\">\r\n <%=helper.translate(\"Original price %s\",helper.processCurrency(item.currency,parseFloat(item.price)))%>\r\n </div>\r\n <% } %>\r\n <% } else { %>\r\n <% if(item.salePrice ) { %>\r\n <div class=\"kuSalePrice\">\r\n <%=helper.processCurrency(item.currency,parseFloat(item.salePrice) )%>\r\n </div>\r\n <% } %>\r\n <% } %>\r\n </div>\r\n </div>\r\n <div class=\"kuRECSItemBottom\">\r\n <button data-id=\"<%= item.id %>\" data-qty=\"1\" data-url=\"<%= item.url %>\" class=\"kuRECSAddToCartBtn\">Add to Cart\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n <% }) %>\r\n </div>\r\n </div>\r\n </div>\r\n <% } %>\r\n</div>"
},
"styles": {
"base": ".kuRECSContainer{\r\n position:relative;\r\n margin:15px 0px;\r\n}\r\n.kuRECSContainer .kuRECSHeader .kuRECSTitle{\r\n display:block;\r\n font-size:18px;\r\n font-weight:bold;\r\n margin:20px 0px;\r\n overflow:hidden;\r\n white-space:nowrap;\r\n text-overflow:ellipsis;\r\n}\r\n.kuRECSContainer .kuRECSWrap{\r\n max-width:100%;\r\n position:relative;\r\n padding:0px;\r\n box-sizing:border-box;\r\n font-family:inherit;\r\n overflow:hidden;\r\n}\r\n.kuRECSContainer .kuRECSWrap *{\r\n outline:none;\r\n}\r\n.kuRECSContainer .kuRECSResults{\r\n width:100%;\r\n max-width:100%;\r\n overflow:auto;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner{\r\n transition:-ms-transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s, transform 0.5s ease 0s;\r\n position:relative;\r\n white-space:nowrap;\r\n transition:all 0.5s ease;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemWrap{\r\n display:inline-block;\r\n margin:12px;\r\n margin-left:0px;\r\n vertical-align:top;\r\n width:180px;\r\n border:1px solid #efefef;\r\n border-radius:2px;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-item{\r\n margin:0 auto;\r\n padding:12px;\r\n background:#FFF;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemImg{\r\n display:flex;\r\n justify-content:center;\r\n align-items:center;\r\n flex-direction:column;\r\n width:100%;\r\n height:210px;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner a.kuRECSImg img{\r\n max-width:100%;\r\n height:auto;\r\n width:auto;\r\n display:inline-block;\r\n margin:0 auto;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner a.kuRECSTitle{\r\n font-size:16px;\r\n font-weight:bold;\r\n text-decoration:none;\r\n overflow:hidden;\r\n white-space:nowrap;\r\n text-overflow:ellipsis;\r\n display:block;\r\n color:#000;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemDesc{\r\n margin-top:12px;\r\n box-sizing:border-box;\r\n overflow:hidden;\r\n white-space:nowrap;\r\n text-overflow:ellipse;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemTag{\r\n color:rgba(0, 0, 0, 0.5);\r\n line-height:110%;\r\n margin-bottom:5px;\r\n min-height:20px;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemSku{\r\n color:rgba(0, 0, 0, 0.5);\r\n font-size:80%;\r\n line-height:110%;\r\n margin-bottom:5px;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemPrice{\r\n font-size:12px;\r\n color:#545454;\r\n margin-bottom:4px;\r\n margin-top:4px;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECS-itemPrice .kuSalePrice{\r\n overflow:hidden;\r\n white-space:nowrap;\r\n text-overflow:ellipsis;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECSAddToCartBtn{\r\n border:0px;\r\n width:100%;\r\n padding:6px;\r\n cursor:pointer;\r\n margin-top:8px;\r\n background:#ececec;\r\n color:#000;\r\n border-radius:2px;\r\n}\r\n.kuRECSContainer .kuRECSResultsInner .kuRECSAddToCartBtn:hover{\r\n background:#333333;\r\n color:#ececec;\r\n}"
},
"scripts": {
"recsObject": ""
},
"staticContent": null
}
Step 2 : Extracting relevant information
As we have executed the API call, now we only need to consider the following data from the response payload:
Field | Description |
search.basePath | This would be used as endpoint to execute API to fetch product data. |
search.payload | The API call request payload used to fetch product data. |
The above fields data would be used to create the API call to get the product data. Once we extract and beautify the request payload, it looks like as below:
{
"recordQueries":[
{
"id":"klevuRECSItemList",
"typeOfRequest":"RECS_TRENDING",
"settings":{
"typeOfRecords":[
"KLEVU_PRODUCT"
],
"limit":10
}
}
]
}
Step 3: Add store JS API key in request
We have the request payload, but we're still unable to identify the specific store. To resolve this, we need to modify the request by adding the store's JS API key. The updated API request should appear as shown below. Ensure to set the typeOfRequest according to your needs. For more details on other parameters, please refer to the example on the Trending Product Recommendation API page link here.
{
"recordQueries":[
{
"id":"klevuRECSItemList",
"typeOfRequest":"RECS_TRENDING",
"settings":{
"typeOfRecords":[
"KLEVU_PRODUCT"
],
"limit":10
}
}
],
"context": {
"apiKeys": [
"klevu-170202994581416798"
]
}
}
Step 4 : Adding personalization data
These steps are necessary if you want to customize the product display according to your preferences instead of directly relying on the Klevu API response. Klevu offers two methods to personalize data. Here they are:
1. Tracking recently clicked products allows for personalized experiences. By sending product IDs as part of an API call to the Klevu personalization engine, the engine can return products based on these preferences. Here's an example of such an API call:
{
"recordQueries":[
{
"id":"klevuRECSItemList",
"typeOfRequest":"RECS_TRENDING",
"settings":{
"personalisation":{
"enablePersonalisation":true
},
"context":{
"recentObjects":[
{
"typeOfRecord":"KLEVU_PRODUCT",
"records":[
{
"id":"31366487507006"
},
{
"id":"31366466633790"
},
{
"id":"31366456311870"
},
{
"id":"31366467944510"
}
]
}
]
},
"typeOfRecords":[
"KLEVU_PRODUCT"
],
"limit":10
}
}
],
"context":{
"apiKeys":[
"klevu-170202994581416798"
]
}
}
As you would be able to see that product IDs are being passed in recentObjects , You can specify product IDs which are recently interacted by user.
2. You can boost products based on some keywords, filters and even product IDs. Here is the example API call:
{
"recordQueries":[
{
"boost":{
"filters":[
{
"key":"brand",
"values":[
"KKE"
],
"weight":200
},
{
"key":"brand",
"values":[
"MNH",
"KSD"
],
"weight":150
}
],
"keywords":[
{
"phrase":"comfortable",
"weight":100
}
],
"records":[
{
"id":"31366487375934",
"weight":300
}
]
},
"id":"klevuRECSItemList",
"typeOfRequest":"RECS_TRENDING",
"settings":{
"typeOfRecords":[
"KLEVU_PRODUCT"
],
"limit":10
}
}
],
"context":{
"apiKeys":[
"klevu-170202994581416798"
]
}
}
In the example API request, we boosted the "brand" attribute with specific values. Additionally, the keyword "comfortable" and the product ID 31366487375934 were also boosted.
Step 5 : Execute API call to get and use it for display
With the API request ready, the final step is to execute it and obtain the product data as a response. This response data includes details such as the title, URL, and price, which can be used for display in various channels according to your setup, such as email. Below is an example request and response. Please note that the API key and endpoint will vary depending on your store.
Endpoint
POST https://eucs33v2.ksearchnet.com/cs/v2/search
Request
{
"recordQueries":[
{
"boost":{
"filters":[
{
"key":"brand",
"values":[
"KKE"
],
"weight":200
},
{
"key":"brand",
"values":[
"MNH",
"KSD"
],
"weight":150
}
],
"keywords":[
{
"phrase":"comfortable",
"weight":100
}
],
"records":[
{
"id":"31366487375934",
"weight":300
}
]
},
"id":"klevuRECSItemList",
"typeOfRequest":"RECS_TRENDING",
"settings":{
"typeOfRecords":[
"KLEVU_PRODUCT"
],
"limit":3
}
}
],
"context":{
"apiKeys":[
"klevu-170202994581416798"
]
}
}
Response
{
"meta": {
"qTime": 5,
"responseCode": 200
},
"queryResults": [
{
"id": "klevuRECSItemList",
"meta": {
"qTime": 4,
"noOfResults": 3,
"totalResultsFound": 12,
"typeOfSearch": "WILDCARD_AND",
"offset": 0,
"debuggingInformation": {},
"notificationCode": 1,
"searchedTerm": "*",
"apiKey": "klevu-170202994581416798",
"isPersonalised": false
},
"records": [
{
"discount": "",
"hideGroupPrices": "",
"itemGroupId": "8862056120624",
"freeShipping": "",
"storeBaseCurrency": "USD",
"inventory_item_id": "49272000348464",
"price": "2629.95",
"toPrice": "",
"imageUrl": "https://cdn.shopify.com/s/files/1/0847/4473/2976/products/Main_b9e0da7f-db89-4d41-83f0-7f417b02831d_medium.jpg?v=1701424245",
"currency": "USD",
"inStock": "yes",
"id": "47214620213552",
"imageHover": "",
"grams": "0",
"sku": "sku-hosted-1",
"brand": "b2b store check",
"basePrice": "2629.95",
"startPrice": "",
"image": "https://cdn.shopify.com/s/files/1/0847/4473/2976/products/Main_b9e0da7f-db89-4d41-83f0-7f417b02831d_medium.jpg?v=1701424245",
"deliveryInfo": "",
"hideAddToCart": "",
"salePrice": "2629.95",
"swatchesInfo": "",
"weight": "",
"klevu_category": "KLEVU_PRODUCT;Products @ku@kuCategory@ku@",
"totalVariants": 0,
"groupPrices": "",
"isCustomOptionsAvailable": "",
"url": "https://b2b-store-check.myshopify.com/products/the-3p-fulfilled-snowboard",
"tags": "Accessory, Sport, Winter",
"name": "The 3p Fulfilled Snowboard new",
"shortDesc": "",
"typeOfRecord": "KLEVU_PRODUCT"
},
{
"discount": "",
"hideGroupPrices": "",
"itemGroupId": "8862056153392",
"freeShipping": "",
"storeBaseCurrency": "USD",
"inventory_item_id": "49272000381232",
"price": "1025.00",
"toPrice": "",
"imageUrl": "https://cdn.shopify.com/s/files/1/0847/4473/2976/products/Main_d624f226-0a89-4fe1-b333-0d1548b43c06_medium.jpg?v=1701424246",
"currency": "USD",
"inStock": "yes",
"id": "47214620246320",
"imageHover": "",
"grams": "0",
"brand": "Hydrogen Vendor",
"basePrice": "1025.0",
"startPrice": "",
"image": "https://cdn.shopify.com/s/files/1/0847/4473/2976/products/Main_d624f226-0a89-4fe1-b333-0d1548b43c06_medium.jpg?v=1701424246",
"deliveryInfo": "",
"hideAddToCart": "",
"salePrice": "1025.0",
"swatchesInfo": "",
"weight": "",
"klevu_category": "KLEVU_PRODUCT;Products;;Hydrogen @ku@kuCategory@ku@",
"totalVariants": 0,
"groupPrices": "",
"isCustomOptionsAvailable": "",
"url": "https://b2b-store-check.myshopify.com/products/the-collection-snowboard-oxygen",
"tags": "Accessory, Sport, Winter",
"name": "The Collection Snowboard: Oxygen",
"shortDesc": "",
"typeOfRecord": "KLEVU_PRODUCT"
},
{
"discount": "",
"hideGroupPrices": "",
"itemGroupId": "8862055924016",
"freeShipping": "",
"storeBaseCurrency": "USD",
"inventory_item_id": "49272000053552",
"price": "949.95",
"toPrice": "",
"imageUrl": "https://cdn.shopify.com/s/files/1/0847/4473/2976/products/snowboard_purple_hydrogen_medium.png?v=1701424244",
"currency": "USD",
"inStock": "yes",
"id": "47214619918640",
"imageHover": "",
"grams": "0",
"sku": "sku-untracked-1",
"brand": "b2b store check",
"basePrice": "949.95",
"startPrice": "",
"image": "https://cdn.shopify.com/s/files/1/0847/4473/2976/products/snowboard_purple_hydrogen_medium.png?v=1701424244",
"deliveryInfo": "",
"hideAddToCart": "",
"salePrice": "949.95",
"swatchesInfo": "",
"weight": "",
"klevu_category": "KLEVU_PRODUCT;Products @ku@kuCategory@ku@",
"totalVariants": 0,
"groupPrices": "",
"isCustomOptionsAvailable": "",
"url": "https://b2b-store-check.myshopify.com/products/the-inventory-not-tracked-snowboard",
"tags": "Accessory, Sport, Winter",
"name": "The Inventory Not Tracked Snowboard",
"shortDesc": "",
"typeOfRecord": "KLEVU_PRODUCT"
}
],
"filters": []
}
]
}