Using AI to power smart content recommendations for your headless CMS

A powerful but less-known feature of Kontent is its AI-powered Smart Recommendations API.

Picture of Luminary CTO Andy smiling with a black background

By Andy Thompson, 12 February 20215 minute read

Updated August 2022

This content is a little out of date! Kontent.ai now supports a direct integration with Recombee, which we have implemented in this site. The good news is, Andy has blogged about that too!

Read the updated post

On a website with a lot of high quality content, content recommendations could be called a number of different things - 'read more', 'you may also like' and so on, but the implementation of them always shares a number of key characteristics. Firstly, they are very effective in helping visitors discover more content on your website. Secondly, they take a lot of work to maintain!

So why would you maintain them yourself if you could let AI do it instead?

Kontent has a native integration with the AI-powered recommendation engine Recombee, made available through its Smart Recommendations API for implementation into your website, app or other channels, but also configurable through a visual user interface right within Kontent.

Don't go looking straight away... it's an advanced add-on feature and not enabled by default. But it's easy to get it added and enabled for your project if you're keen - just hit the chat button within Kontent and speak to the support team!

We've been using it for some time right here on this site (you'll see some recommendations at the bottom of this post!) since it was in an early beta, and I recently migrated it across to the latest release version of the APIs and infrastructure. It was quite easy to set up, so I thought I'd write up some pointers for anyone else who was keen to give it a try.

Kentico of course has an official tutorial for setting up Smart Recommendations if you'd prefer this from Kentico and in a pure JavaScript flavour.

1. Set it up in Kontent

Firstly of course, get it turned on πŸ˜‰. It should look like this:

Kontent Smart Recommendations setup screen screenshot

Click the big blue button to set it up, and save your 'Setup Password' somewhere safe! It's important because it allows you to modify the configuration of the Recombee AI engine behind the scenes. It's not stored within Kontent, but you can save it in your favourite password manager (mine is 1Password) just like any other login/password. You'll need it any time you see this modal pop up:

Kontent Smart Recommendations setup password prompt

Next, you need to tell the system which Content Types you want it to use for recommendations. This is what you want the AI engine to be processing and recommending, not necessarily what pages you want to show your recommendations on (although they might end up being the same).

Screenshot of selecting a Content Type to add to Smart Recommendations in Kontent

You add one Content Type at a time. No need to define any fields or settings, just wait for a moment for the system to do it's Smart thing!

Screenshot of Kontent Smart Recommendations Content Type list

That's it in terms of setting up the Smart Recommendations module. But there's one more step we shouldn't forget - we need to set up a webhook to let the recommendation engine know when your content has been updated and it needs to update its recommendations.

Copy your 'Recommendations API Key' and head in to the regular Webhooks section of Kontent to add it in there:

Screenshot of Smart Recommendations webhook configuration

And that's it! Set and forget - the only time you'll need to come back here is if your content model changes in the future and you need to tell the recommendation engine about some new Content Types you want it to start recommending.

2. Implement recommendations in your app/website(s)

There are two main SDKs available - JavaScript and .NET - to make your developers' lives extremely easy. Of course you don't need to use an SDK, the API is completely open and documented so you can implement recommendations into literally any platform or language that supports HTTP requests (that's the Internet 😜).

In our case, our website is currently running on .NET Core, so my examples are in C# and I'm using the .NET SDK. But it's pretty simple and the same logic should apply to any language you're using.

Firstly, we set up a controller action for recommendations that returns a partial view, containing the rendered HTML for our recommended pages. This is not a full code file so don't just copy and paste this into your project, but it should give you pointers for all the steps you'll need to take in your own .NET Core MVC site.

[HttpGet]
public async Task<ActionResult> GetRecommendations(string recommendationType, string itemCodename, int count = 2)
{
    var apiKey = _projectOptions.Value.KontentRecommendationApiKey;
    var rc = new RecommendationClient(apiKey, 10);

    try {
        // Get the tracking cookie either from google analytics, or from our custom tracking cookie, or create a new one
        var cookie = RecommendationCookieHelper.GetGoogleTrackingCookie(Request) ?? RecommendationCookieHelper.GetRecommendationTrackingCookie(Request) ??           RecommendationCookieHelper.SetNewRecommendationTrackingCookie(Request, Response, TimeSpan.FromDays(60), "mydomain.com");

        //Generated visitId we can use in the recommendation request
        var visitId = cookie.VisitId; 

        // The helper also lets you extract an initialised VisitorDetails object
        // It fills out the referrer and IP address
        var visitor = RecommendationCookieHelper.GetVisitorDetails(Request); 

        // retrieve codenames of recommended content items from the recommendation engine
        var recommendationRequest = new RecommendationRequest {
            VisitId = visitId,
            CurrentItemCodename = itemCodename,
            ResponseLimit = count,
            RequestedTypeCodename = contentType,
            Visitor = visitor
        };    
        RecommendedContentItem[] recommendedContentItemCodenames = await rc.GetRecommendationsAsync(recommendationRequest);

        var recommendedCodenames = recommendedContentItemCodenames.Select(r => r.Codename).ToArray();

        // get full details for the recommended content items from the Delivery API      
        if (recommendedCodenames.Any()) {
            var recommendationResponse = await _deliveryClient.GetItemsAsync(
                new InFilter("system.codename", recommendedCodenames),
                new DepthParameter(2)
            );

            // build out a typed list of items to bind to the tile viewer
            List<IPageType> tiles = new List<IPageType>();
            foreach(ContentItem item in recommendationResponse.Items) {
                tiles.Add(ReverseCustomTypeProvider.GetTypedContent(item));
            }

            return PartialView("_Tiles", tiles);
        }
        else {
            return PartialView("_Tiles", new List<IPageType>());
        }
    }
    catch (Exception ex) {
        _logger.LogError(ex, "Oh no! The recommender failed.", itemCodename);

        return PartialView("_Tiles", new List<IPageType>());
    }
}

With this controller wired up properly, your site should respond to a request for something like /recommendations/blog/headless_cms_website_seo/2 with the HTML for your recommendations.

The last piece of the puzzle is some very simple JavaScript to insert these recommendations into the page when it loads. By dropping something like this into the Razor view for the Blog Post page:

<script defer type="text/javascript">
    window.addEventListener("load", function(event) {
        var recommendationsTarget = document.getElementById('recommendations-container');
        fetch('/recommendations/blog/@Model.PostCodename/2', {
                credentials: 'same-origin'
            })
            .then(function(response) {
                return response.text().then(function(text) {
                    if (!(/^\s*$/.test(text))) {
                        recommendationsTarget.innerHTML = text;
                    }
                });
            });
    });
</script>

A div with the id 'recommendations-container' will magically be filled with AI-powered content recommendations based on the current page.

If you're wondering why bother with partial views and JavaScript requests when I could just include the recommendations code in the controller for a ViewComponent or similar... I'd strongly recommend you don't do this synchronously in your server-side code. In other words, don't make your page wait for recommendations before it can load.Β 

There are a few reasons it's a far better idea to do it asynchronously after your page loads (via a JavaScript/XHR/AJAX request):

  • It allows the rest of your page to load much more quickly
  • It allows you to cache your page content (or use recommendations in a static site!) yet still have real-time personalised recommendations
  • It removes a dependency on a third party API for your page to load (in other words, removes a potential single point of failure)
  • Your recommendations are highly likely to be below/after the main content on your page so a slight delay is usually ok anyway
  • Recommendations are individually personalised per visitor, so you don't want/need search engines to be indexing dynamic recommendations anyway
  • Kontent's official guide to setting this up says you should too!

Hot tips

An optional extra in the .NET SDK is the CookieHelper (I've implemented it in my code above) which gives you a unique ID for each visitor. It'll also make it easy to retrieve and pass through the HTTP Referrer and IP address for each visitor to improve recommendations based on their geographic location.

Of course, these are optional and you need to be aware of any privacy laws in your area when using visitor data (that's your responsibility, not Kontent's!).

Finally, if you want to give your editors control over what kinds of recommendations are shown and where, that's pretty easy too! Rather than putting all of your recommendation logic in your code (like I did with recommending blog posts on the blog post page in my example), you can use a custom element inside Kontent. Being a headless CMS, this isn't available out of the box, but it's not hard to implement. The Kontent team have even provided an open source custom element you can base yours on!

Go forth and recommend

The Smart Recommendations API in Kontent is very powerful and very easy to implement.

Simply enable it, configure it, and implement it in your app or website using the provided SDKs, code samples and tutorials, and never manually manage a 'related content' field in your CMS again!

Want to dig deeper into Headless CMS?

Download the Marketer's Guide to Headless CMS

Our Kontent experts

Luminary has some of the most experienced headless CMS experts in the world, including Kentico MVP Andy, and Emmanuel, the developer of the first Kentico Kontent powered website in the world!

Keep Reading

Want more? Here are some other blog posts you might be interested in.