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

Track search result clicks in C# SDK

Enable click-through tracking with the C# SDK and render trackable links in Optimizely Graph 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 records user interaction without delaying page navigation.

How click-through tracking 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

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();

Register the tag helpers

Add the tag helper namespace to _ViewImports.cshtml:

@addTagHelper *, Optimizely.Graph.AspNetCore

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

Add .Track() to the search query

Call .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();
    }
}

Render results with 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 through 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>
}

Render results with 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 and anti-forgery tokens

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

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

<meta name="__RequestVerificationToken">

Troubleshooting

Tracking URLs are null

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

Correct usage:

client.QueryContent<T>()
      .SearchFor(q)
      .Track()
      .GetAsContentAsync();

Tracking does not work without .SearchFor():

// This will NOT work — missing .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. This is 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 });
}