Skip to main content

Read Search Analytics — top queries, zero-result, click-through

Every in-app search your team runs — the ?q= filter on messages, contacts, calls, campaigns, and the inbox, plus the global command-palette search — is recorded per tenant against a normalized form with personal data masked. The Search Analytics dashboard (console route /insights/search-analytics) reads those rows back as four panels so you can see what your team types most, which queries come back empty, how often someone clicks a result, and which inputs the search grammar rejects. This guide is the narrative counterpart of the Search Analytics API reference — use the console first, and reach for the API when you need export or integration.

What Search Analytics measures

Each completed search writes one analytics row: the normalized query, the surface it ran against (messages, contacts, calls, campaigns, inbox, or the global search), the result count, and whether the query parsed. Click attribution ships separately via POST /api/v1/search-analytics/click — the dashboard fires that call the moment a user opens a result, stamping the click back to the search that produced the list. The call is idempotent and silent on unknown ids, so frontend clients can fire-and-forget. Four read aggregations sit on top of those rows: All four GET endpoints share the same range filter (from, to, surface, limit), so a dashboard can keep one filter state across panels. The lookback is capped at 90 days; a deeper from is silently pinned to 90 days before to. Searches run under support impersonation are excluded from a customer’s own numbers by design. See the API reference for the full shape of every panel.

Reading the dashboard

Open /insights/search-analytics in the console. A window selector (24h, 7d, 30d, 90d — plus a custom date range) and a per-surface selector sit at the top of the page; the same pair applies to every panel below it. Read the selectors before interpreting any number, because a panel that spans 24 hours looks nothing like the same query over 90 days. The four tabs — Top queries, Zero-result queries, Click-through, and Parse failures — each answer a different question, and they are designed to be read together:
  • Top queries by volume is the ground truth for what your team is actually looking for. If a surface you rely on (the messages hub, say) top-ranks on queries none of your dashboards surface, that is a coverage signal: the quick filters or saved views need to catch up to real behaviour.
  • Zero-result is the most actionable panel. Every row is a query your team expected to match something and did not. Read it as a binary: either the data genuinely does not exist (nobody ever sent that campaign, so nothing is wrong) or the index is missing terms that should match (and the team keeps paying a search tax every day).
  • Click-through and zero-result are the relationship to read together. A query with a high zero-result count and a low CTR is the classic “the search index misses common terms” symptom — people type something plausible, get nothing, and give up. A query with low zero-result count and low CTR is a ranking or snippet problem, not an index problem — the results exist, but the snippet does not earn the click.
  • Parse failures is the lexicon and grammar signal. Every row is an input the search syntax rejected before it ever reached the index. A cluster of parse failures on phrases such as rma return label or ref: 1234 tells you the free-text-to-field mapping needs work — the team has settled on a convention (a return-merchandise-authorization code, an RMA dash format) that the grammar does not yet accept. This is the endpoint the operator points at when deciding whether to extend the keyword lexicon, add an alias, or fix the suggested-query examples in the search box.
Practical read pattern for the four panels on the 30d window: scan zero-result for repeated high-zero counts, then chase the same phrase through the click-through panel to see if it converts, then open the parse-failures panel to verify the input shape that is failing. Each panel on its own tells a partial story; all four together close the loop.

Operator playbook — working a zero-result spike

Worked example: a support director notices a spike in zero-result rma return label queries on the 30d window. The operator playbook is the same three-step loop every time — diagnose the mechanism (parse failure, index miss, or genuinely missing data), remediate the lexicon or the grammar, then confirm recovery in click-through across two windows. Step 1 — diagnose. Open /insights/search-analytics, set the window to 30d, and scan the Zero-result panel for the phrase. If rma return label appears with a high count, check the Parse failures panel in the same window. Two outcomes are common:
  • The phrase also shows up in Parse failures (with a representative raw sample like rma return label or rma:RMA-1234), meaning the search grammar rejected the input before it ever reached the index. The gap is in the grammar — the field prefix you need is not one the grammar recognizes, or the dash and slash characters in the convention are unsupported. Personal data is masked in the sample, so the row aggregates safely across customers.
  • The phrase does not appear in parse failures — the query parsed fine, and the index genuinely did not match it. That narrows the fix to the index and the keyword lexicon that maps raw terms to fields, rather than the syntax itself.
Step 2 — remediate the lexicon or the alias. When parse failures confirm the grammar is the gap, the remediation the operator makes is to the keyword lexicon and the alias list the ?q= DSL consults (the same DSL the messages hub search box uses; see Search message history for the full field reference). For the rma return label convention, the remediation is one of:
  • Register rma as an accepted field alias (alongside the surface it should search — typically the messages surface for a support note, or the contact surface for a return-owner lookup), so rma:RMA-1234 parses as a fielded clause.
  • Add rma and rma return label as aliases for the existing return-reference field, so a free-text search mentioning the phrase resolves to the right indexed column rather than no column at all.
  • If the phrase parses but keeps returning zero results, the lexicon is fine and the remediation is indexing the return-reference column on the surface the search hits (a conversation or message text field) rather than changing the grammar again.
The parse-failures panel is the reading answer — the worked example here remediates the keyword lexicon (a new alias entry for rma), because that is where the operator can close the gap without a platform change on their side beyond the tenant-level alias configuration. Step 3 — confirm recovery in click-through across two windows. Remediation without confirmation is guessing. After the alias change ships, run the confirmation loop in two windows to prove the fix:
  1. Reopen the Click-through panel on the same 30d window and locate rma return label. The CTR should now rise toward the baseline of other fielded searches on that surface. On the pre-fix window, the query sat near zero CTR because the runs returned nothing; post-fix, the same inputs produce results and clicks.
  2. Switch to the shorter 7d window to isolate the new traffic. The post-fix 7d window is the rolling confirmation: high CTR here, paired with a dropping zero-result count, proves the remediation is behaving. If the zero-result count stays flat on the new window even after the alias shipped, the alias did not register — recheck the operator-side change, or the index needs the new column indexed (Step 2, option three).
Treat the two-window comparison as the delivery gate for the fix. A single-window read on a quiet search phrase can confuse seasonality with success; two windows is the smallest honest confirmation.

Using the API directly

Most operators read the dashboard; reach for the API when you need the numbers off-console — a weekly CSV, a Slack alert on zero-result spikes, or a merge into your own BI layer. The four read endpoints take the same query parameters (from, to, surface, limit) and return a standard envelope with data.rows plus the resolved range. All roles that can log into the dashboard (owner, admin, developer, viewer) can read the analytics over the API — the analytics read surface inherits the same read posture as the dashboard does per role. Validation failures return 422 VALIDATION_ERROR; see Error codes. Top queries.
Zero-result. The actionable panel — each row is a query your team expected to match and did not.
Click-through. Pass a narrower window to catch recent changes; scope with surface to compare engagement across panels.
Parse failures. The lexicon upgrade signal — query_text_sample shows one representative raw input, with personal data (emails, phones, tokens) masked before it is returned.
Record a click. Attribute a click so the click-through panel ties impressions to opened results. The call is idempotent and silent on unknown ids — fire-and-forget from the client.
recorded: false indicates the analytics-row id did not match a search in your tenant or it aged out of retention — the endpoint reports this as 200 rather than 404 so clients can stay fire-and-forget without ever confirming an id exists in another tenant.

See also