Skip to main content
Cludo provides comprehensive analytics to help you understand how users search, what they find, and where to improve.

Tracking events

Analytics data is populated by tracking events. The recommended client-side setup is the Cludo API analytics script. If you use the React hooks from @cludosearch/cludo-search-components, analytics events are handled automatically by the components package. If you implement search directly via the API, send tracking events yourself.

Client-side event tracking

Track search events from your frontend. These endpoints accept any auth method, including anonymous requests with no auth header.
const BASE_URL = "https://api.cludo.com";
// Use https://api-us1.cludo.com for US region

async function trackSearch(customerId, engineId, queryId, query, resultCount) {
  await fetch(
    `${BASE_URL}/api/v3/${customerId}/${engineId}/search/pushstat/querylog`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        sw: query,
        qid: queryId,
        rc: String(resultCount),
        dt: "desktop"
      })
    }
  );
}

async function trackClick(customerId, engineId, queryId, clickUrl, clickIndex, title) {
  await fetch(
    `${BASE_URL}/api/v3/${customerId}/${engineId}/search/pushstat/clicklog`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        ls: "searchresult",
        sw: "search query",
        qid: queryId,
        clurl: clickUrl,
        cli: String(clickIndex),
        title: title
      })
    }
  );
}
The tracking body uses flat key-value pairs (for example {"sw": "query", "qid": "..."}). Do not wrap them in a values object. See API Reference → Tracking for the full list of parameters.

Server-side tracking and IP forwarding

If your integration sends tracking events from a backend rather than directly from the browser, the API receives your server’s IP instead of the real user IP. This affects analytics deduplication and geographic data. To pass the real client IP, include it as an ip field in the request body:
body: JSON.stringify({
  sw: query,
  qid: queryId,
  rc: String(resultCount),
  dt: "desktop",
  ip: "203.0.113.42"   // real client IP captured server-side
})
The tracking endpoints do not read the X-Real-IP, X-Forwarded-For, or Forwarded headers. To pass a client IP from a server-side integration, use the ip body field.

Search-as-you-type implementations

If your implementation fires search requests on every keystroke (search-as-you-type, or SAYT), follow the three steps below. Skipping them is the most common cause of noisy analytics. Symptoms include partial keystroke queries filling up Searches Without Results, a 0% click-through rate, and every search appearing as “ineffective” in the dashboard.
1

Mark keystroke searches with searchContext

Add searchContext: "sayt" to the body of search requests fired on each keystroke. This tells the backend to classify them as SAYT and exclude them from Dashboard analytics (Popular Searches, Trending, etc.).
body: JSON.stringify({
  query: partialQuery,
  searchContext: "sayt"   // keystroke search; omit for committed searches
})
2

Only log committed searches

Call Track Search Query (querylog) only when the user commits a final search, on Enter or explicit submission, not on every debounced keystroke. Logging every partial query produces rows like r, ri, riv, rive… in Searches Without Results.
3

Always log result clicks

Call Track Result Click (clicklog) whenever a user clicks a result. Missing click events is the sole cause of a 0% click-through rate and all searches appearing as “ineffective” in the dashboard.

Viewing analytics

Analytics data is available in the Cludo Dashboard. The dashboard provides:
  • Search term performance: top queries, zero-result queries, content gaps
  • Search volume over time: total and trending search volume
  • Click-through rate: are users finding what they need?
  • AI analytics: volume & feedback scores