HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Track search result clicks

Learn how to enable click-through tracking in C# SDK using Optimizely Graph and how to render trackable links in search results

Search click-through tracking records which search results users select and sends that data to Optimizely Graph. Optimizely Graph analyzes this interaction data and uses it to improve search relevance over time.

When you enable tracking, each search result receives a unique tracking URL. When a user selects a result, the tracking script sends a click event to the Optimizely Graph tracking endpoint using the browser Beacon API. This process records user interaction without delaying page navigation.

How it works

  1. Add .Track() to a search query.
  2. Optimizely Graph returns a unique tracking URL for each result.
  3. Render results using <graph-trackable-link>. The component sends click events automatically through the Beacon API.
  4. The Beacon API sends tracking data asynchronously without blocking navigation.

Prerequisites

Install the required packages::

dotnet add package Optimizely.Graph.Cms.Query
dotnet add package Optimizely.Graph.AspNetCore

Set up click-through tracking

1. Register the tracking middleware

Call UseGraphTrackingScripts() in Program.cs or Startup.cs. This middleware serves the tracking JavaScript from /optimizely/graph/scripts/.

// Program.cs or Startup.cs
app.UseGraphTrackingScripts();
app.UseStaticFiles();
app.UseRouting();

2. Register the tag helpers

Add the tag helper namespace to _ViewImports.cshtml:

@addTagHelper *, Optimizely.Graph.AspNetCore

3. Add tracking setup to the layout

Add <graph-tracking-setup /> inside the <head> element of your shared layout. The component renders the tracking script and an anti-forgery token required by the tracking endpoint.

<!-- _Layout.cshtml -->
<head>
    <!-- Renders tracking script + anti-forgery token (recommended) -->
    <graph-tracking-setup />

    <!-- Or configure individually: -->
    <!-- <graph-tracking-script /> -->
</head>

Implement click-through tracking

Controller

Controller .Track() together with .SearchFor(). when executing a search query. Tracking works only when the query includes .SearchFor().

public class SearchController : Controller
{
    private readonly IGraphContentClient _graphClient;

    public SearchController(IGraphContentClient graphClient)
    {
        _graphClient = graphClient;
    }

    public async Task<IActionResult> Search(string q)
    {
        var results = await _graphClient
            .QueryContent<ArticlePage>()
            .SearchFor(q)
            .UsingFullText()
            .Track()   // enables click-through tracking
            .IncludeTotal()
            .Limit(100)
            .GetAsContentAsync();

        ViewBag.SearchResults = results;
        return View();
    }
}

View using the tag helper

The <graph-trackable-link> tag helper renders an <a> element that includes a data-track-url attribute. The tracking script intercepts clicks and sends the tracking URL via the Beacon API.

@{
    var results = ViewBag.SearchResults as IGetAsContentResult<IContent>;
}

@foreach (var item in results)
{
    var trackUrl = results.TrackUrls?.TryGetValue(item, out var url) == true ? url : null;

    <article class="search-result">
        <h2>
            <graph-trackable-link url="@item.Metadata?.Url"
                                  track-url="@trackUrl"
                                  class="result-link">
                @item.Metadata?.DisplayName
            </graph-trackable-link>
        </h2>
    </article>
}

View using a standard link

Any <a> element that includes a data-track-url attribute sends click-through tracking data. The tag helper is optional.

<a href="@item.Metadata?.Url"
   data-track-url="@trackUrl"
   class="result-link">
    @item.Metadata?.DisplayName
</a>

Security

The tracking endpoint (/Optimizely/Track/TrackClickThrough) requires an anti-forgery token.

<graph-tracking-setup/> automatically renders a meta tag containing the token:

<meta name="__RequestVerificationToken">

Troubleshooting

Tracking URLs are null

.Track() only works when combined with .SearchFor().

  • Correct usage
    client.QueryContent<T>()
          .SearchFor(q)
          .Track()
          .GetAsContentAsync();
  • Tracking does not work without .SearchFor():
    client.QueryContent<T>()
          .Track()
          .GetAsContentAsync();

Tracking requests return 400 Bad Request

The anti-forgery token is missing or invalid. Verify the following:

  • <graph-tracking-setup /> (or <graph-tracking-script />) exists in the layout <head>
  • services.AddAntiforgery() is registered in the service collection (included by default in CMS)

OptimizelyGraphTracking is not defined

The tracking script is not loaded.

Verify the following:

  • app.UseGraphTrackingScripts() runs during application startup
  • The script loads from /optimizely/graph/scripts/optimizely-graph-tracking.js

Enable debug logging during development:

if (window.OptimizelyGraphTracking) {
    OptimizelyGraphTracking.configure({ debugMode: true });
}