Search Analytics Module — Admin
Read-only reporting over customer search activity: window-scoped KPIs, top searched terms, zero-result terms (catalog/synonym gaps), a volume time series, and a raw event drill-down.
Read-only reporting over customer search activity: window-scoped KPIs, top searched terms, zero-result terms (catalog/synonym gaps), a volume time series, and a raw event drill-down.
Source:
api-modules/search-analytics/src/controllers/admin-search-analytics.controller.ts.Optional plugin. Capture works by subscribing to the fire-and-forget
search.performeddomain event emitted by@sc/search; removingSearchAnalyticsModule.forRoot()fromapp.module.tsdisables tracking and these endpoints entirely (storefront search is unaffected).
Capture semantics
A search_event row is recorded when a customer hits GET /store/product-search with a non-empty q on page 1. Specifically:
- Filter-only browsing (no
q) is not recorded — that's PLP traffic, not search intent. - Paging through results (
page > 1) is not recorded — one search submission = one event. - The
/suggestionsautocomplete endpoint is never recorded. - Internal
ProductSearchServiceconsumers (dynamic product listings, admin previews) are never recorded. - The write is asynchronous (event listener) — it adds no latency to the search response, and an insert failure is logged and dropped, never surfaced to the customer.
Each event stores: the raw + normalized (lowercased, whitespace-collapsed) query, the filters that accompanied it (jsonb, only the keys sent), the sort, the result total, a zero-result flag, and — when a better-auth session cookie was present — the (possibly anonymous) userId + userIsAnonymous. Events are retained indefinitely.
Conventions
Authentication
All endpoints require a Better-Auth admin session and a role granting the matching permission.
| Endpoint | Permission |
|---|---|
GET /admin/search-analytics/* | searchAnalytics: view |
Windows
window is one of today (hourly trend buckets), 7d, 30d (daily buckets), all_time (monthly buckets). Defaults to 30d. Bounds are resolved against the store timezone (STORE_TZ_OFFSET_MINUTES).
Response envelope
Successful responses are wrapped by ResponseInterceptor:
{
"data": <payload>,
"message": "Success",
"statusCode": 200,
"metadata": { /* pagination, where applicable */ }
}Endpoints
GET /admin/search-analytics/summary
Window-scoped KPIs.
Query: window.
// data
{
"window": "30d",
"totalSearches": 27123,
"zeroResultSearches": 812,
"uniqueTerms": 4031,
"uniqueSearchers": 1922 // distinct identified users; sessionless searches excluded
}GET /admin/search-analytics/top-searches
Most-searched terms, grouped on the normalized query, ordered by search count. Paginated.
Query: window, limit (1–100, default 20), offset (default 0).
// data: TopSearchRow[]
[
{
"query": "vitamin c serum",
"searches": 412,
"avgResults": 38,
"lastSearchedAt": "2026-06-10T08:11:02.000Z"
}
]
// metadata: { total, limit, offset, hasMore } — total = distinct terms in the windowGET /admin/search-analytics/zero-results
Same shape as top-searches, restricted to terms whose searches returned nothing. The actionable merchandising list (missing products, Typesense synonym gaps).
GET /admin/search-analytics/trend
Search volume time series for charting. Bucket granularity follows the window (see Windows). Not paginated — fixed-cardinality series.
Query: window.
// data: SearchTrendBucket[]
[
{ "bucket": "2026-06-09T00:00:00.000Z", "searches": 903, "zeroResults": 27 }
]GET /admin/search-analytics/events
Raw event drill-down, newest first. Paginated (limit default 100/max 500, offset).
Query: limit, offset, q (substring match on the normalized query), zeroOnly (true/false), from, to (ISO datetimes).
// data: SearchEventRow[]
[
{
"id": "5f0c…",
"query": "Vitamin C",
"normalizedQuery": "vitamin c",
"filters": { "brands": ["some-brand"] }, // null when none
"sortBy": null,
"totalResults": 38,
"zeroResult": false,
"userId": "usr_…", // null when no session cookie was present
"userIsAnonymous": false, // true = better-auth anonymous (guest) session
"createdAt": "2026-06-10T08:11:02.000Z"
}
]Rewards Module — Admin
HTTP surface for platform staff managing the rewards/loyalty plugin: per-customer search, balance + history inspection, the global master ledger log, and manual credit/debit adjustments. All endpoints sit…
Search Module — Admin
HTTP surface for the platform-admin operational control of the Typesense product search index: trigger a bulk reindex, poll its status, and recreate the collection — surfaced as action buttons on the Product Search settings page.