Unique Searchers
Also known as: Searchers, Site Search Users, Users Who Searched, Unique Search Users, Distinct Searchers
De-duplicated count of distinct users who ran at least one site search in the reporting window. Each user counts once however many searches they run; it is the numerator of Site Search Usage Rate.
Business Use Case
1️⃣ Merchandising tracks weekly Unique Searchers against Unique Visitors and flags a drop in search reach to Product.,2️⃣ Product compares Unique Searchers by device category after a search UI release and rolls back if mobile falls.,3️⃣ Site search owners use daily Unique Searchers to size ranking A/B tests and set their minimum run time.
Business Criticality
High
Journey / Navigation Area
Discovery
Owning Function
Merchandising
Scope
User
Formula
Calculation Notes
1️⃣ A user counts once per window however many searches they run; daily values do not sum to a weekly or monthly total.,2️⃣ Count the unified search event only; view_search_results belongs to Search Results Views and is not used here.,3️⃣ Exclude search events with an empty or whitespace-only search_term and refires (is_refire = true); a re-sent search alone does not qualify.,4️⃣ Product catalog search only: exclude store-locator and help-centre searches (search_type other than product).,5️⃣ Exclude traffic_type = 'internal' and known bot traffic; staff and QA searches inflate counts on low-traffic sites.,6️⃣ Identity is user_pseudo_id; cleared cookies and multiple devices inflate the count unless user_id stitching is applied and stated.,7️⃣ Autocomplete selections that load a results page count; queries typed but never submitted do not.
Technical Details
Events
| Platform | Event |
|---|---|
| Google Analytics 4 | search |
| Adobe | search |
Data Mappings
W3 Data Layer
{
"user": {
"profile": [
{
"profileInfo": {
"profileID": "{{user_pseudo_id}}"
}
}
]
},
"search": {
"searchTerm": "{{search_term}}",
"searchType": "product",
"searchResultsCount": "{{search_results_count}}",
"isRefire": false,
"trafficType": "external",
"searchScope": "catalog"
}
}GA4 Data Layer
{
"event": "search",
"search_term": "{{search_term}}",
"search_type": "product",
"search_results_count": "{{search_results_count}}",
"is_refire": false,
"search_source": "search_bar",
"search_scope": "catalog",
"page_type": "search_results",
"traffic_type": "external",
"session_id": "{{session_id}}",
"user_id": "{{user_id}}"
}Adobe Client Data Layer
{
"event": "search",
"searchTerm": "{{search_term}}",
"searchType": "product",
"searchResultsCount": "{{search_results_count}}",
"isRefire": false,
"searchSource": "search_bar",
"searchScope": "catalog",
"trafficType": "external",
"sessionId": "{{session_id}}",
"userId": "{{user_id}}"
}XDM Mapping
{
"eventType": "search",
"search": {
"term": "{{search_term}}",
"type": "product",
"resultsCount": "{{search_results_count}}",
"isRefire": false,
"scope": "catalog",
"trafficType": "external"
},
"endUserIDs": {
"userPseudoId": {
"id": "{{user_pseudo_id}}",
"authenticatedState": "ambiguous"
}
},
"_experience": {
"analytics": {
"customDimensions": {
"eVars": {
"eVar1": "{{search_term}}",
"eVar2": "product"
}
}
}
}
}SQL Queries
Google Analytics 4 (BigQuery)
-- ==========================================
-- 1. GOOGLE BIGQUERY SQL
-- ==========================================
-- Reporting window: trailing 90 complete days
DECLARE start_date DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY);
DECLARE end_date DATE DEFAULT DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY);
-- Known bot traffic is removed at collection by GA4 known-bot filtering; the export carries no bot flag
-- Pivot the search parameters of every search event in the window
WITH search_events AS (
SELECT
user_pseudo_id,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'search_term') AS search_term,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'search_type') AS search_type,
(SELECT COALESCE(value.string_value, CAST(value.int_value AS STRING)) FROM UNNEST(event_params) WHERE key = 'is_refire') AS is_refire,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'traffic_type') AS traffic_type
FROM `your-project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', start_date) AND FORMAT_DATE('%Y%m%d', end_date)
AND event_name = 'search'
)
-- Count each user once: non-blank product searches, no refires, no internal traffic
SELECT
COUNT(DISTINCT user_pseudo_id) AS unique_searchers
FROM search_events
WHERE NULLIF(TRIM(search_term), '') IS NOT NULL
AND search_type = 'product'
AND COALESCE(LOWER(is_refire), 'false') NOT IN ('true', '1')
AND COALESCE(traffic_type, '') != 'internal';Adobe Experience Platform (AEP Query Service)
-- ==========================================
-- 2. ADOBE AEP QUERY SERVICE SQL
-- ==========================================
-- Reporting window: trailing 90 complete days (same as start_date / end_date in the BigQuery query)
-- Known bot traffic is removed at collection by Adobe bot rules; the dataset carries no bot flag
-- Search events in the window with their search fields
WITH search_events AS (
SELECT
endUserIDs.userPseudoId.id AS user_pseudo_id,
search.term AS search_term,
search.type AS search_type,
search.isRefire AS is_refire,
search.trafficType AS traffic_type
FROM aep_web_events_dataset
WHERE timestamp >= CAST(DATE_SUB(CURRENT_DATE, 90) AS TIMESTAMP)
AND timestamp < CAST(CURRENT_DATE AS TIMESTAMP)
AND eventType = 'search'
)
-- Count each user once: non-blank product searches, no refires, no internal traffic
SELECT
COUNT(DISTINCT user_pseudo_id) AS unique_searchers
FROM search_events
WHERE NULLIF(TRIM(search_term), '') IS NOT NULL
AND search_type = 'product'
AND COALESCE(is_refire, FALSE) = FALSE
AND COALESCE(traffic_type, '') != 'internal';Source Data
Digital Analytics (GA4)
Dependencies
Prerequisite: List the dependencies required for this Metric
Events:
Report Attributes
Device Category Traffic Source Page Type Internal Search Term
Recommended Reporting Views
Segment Eligibility
true
Governance
Derived KPIs
Governance
GitHub
catalog/metrics/unique-searchers.yml