diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 3bc77c33513e..caf7ef92073a 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -6827,6 +6827,7 @@ const CONST = {
             FROM: 'from',
             CARD: 'card',
             WITHDRAWAL_ID: 'withdrawal-id',
+            MERCHANT: 'merchant',
         },
         get TYPE_CUSTOM_COLUMNS() {
             return {
@@ -6903,6 +6904,11 @@ const CONST = {
                     EXPENSES: this.TABLE_COLUMNS.GROUP_EXPENSES,
                     TOTAL: this.TABLE_COLUMNS.GROUP_TOTAL,
                 },
+                MERCHANT: {
+                    MERCHANT: this.TABLE_COLUMNS.GROUP_MERCHANT,
+                    EXPENSES: this.TABLE_COLUMNS.GROUP_EXPENSES,
+                    TOTAL: this.TABLE_COLUMNS.GROUP_TOTAL,
+                },
             };
         },
         get TYPE_DEFAULT_COLUMNS() {
@@ -6944,6 +6950,7 @@ const CONST = {
                     this.TABLE_COLUMNS.GROUP_EXPENSES,
                     this.TABLE_COLUMNS.GROUP_TOTAL,
                 ],
+                MERCHANT: [this.TABLE_COLUMNS.GROUP_MERCHANT, this.TABLE_COLUMNS.GROUP_EXPENSES, this.TABLE_COLUMNS.GROUP_TOTAL],
             };
         },
         BOOLEAN: {
@@ -7039,6 +7046,7 @@ const CONST = {
             GROUP_BANK_ACCOUNT: 'groupBankAccount',
             GROUP_WITHDRAWN: 'groupWithdrawn',
             GROUP_WITHDRAWAL_ID: 'groupWithdrawalID',
+            GROUP_MERCHANT: 'groupMerchant',
         },
         SYNTAX_OPERATORS: {
             AND: 'and',
@@ -7219,6 +7227,7 @@ const CONST = {
                 [this.TABLE_COLUMNS.GROUP_BANK_ACCOUNT]: 'group-bank-account',
                 [this.TABLE_COLUMNS.GROUP_WITHDRAWN]: 'group-withdrawn',
                 [this.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID]: 'group-withdrawal-id',
+                [this.TABLE_COLUMNS.GROUP_MERCHANT]: 'group-merchant',
             };
         },
         NOT_MODIFIER: 'Not',
@@ -7260,6 +7269,7 @@ const CONST = {
             UNAPPROVED_CARD: 'unapprovedCard',
             RECONCILIATION: 'reconciliation',
             TOP_SPENDERS: 'topSpenders',
+            TOP_MERCHANTS: 'topMerchants',
         },
         GROUP_PREFIX: 'group_',
         ANIMATION: {
diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts
index 8f1b59636da5..c5d8fbe655b1 100644
--- a/src/components/Search/types.ts
+++ b/src/components/Search/types.ts
@@ -122,7 +122,8 @@ type SearchCustomColumnIds =
     | ValueOf<typeof CONST.SEARCH.TYPE_CUSTOM_COLUMNS.EXPENSE_REPORT>
     | ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.CARD>
     | ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.FROM>
-    | ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.WITHDRAWAL_ID>;
+    | ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.WITHDRAWAL_ID>
+    | ValueOf<typeof CONST.SEARCH.GROUP_CUSTOM_COLUMNS.MERCHANT>;
 
 type SearchContextData = {
     currentSearchHash: number;
diff --git a/src/components/SelectionListWithSections/Search/MerchantListItemHeader.tsx b/src/components/SelectionListWithSections/Search/MerchantListItemHeader.tsx
new file mode 100644
index 000000000000..ad6a3b26c20d
--- /dev/null
+++ b/src/components/SelectionListWithSections/Search/MerchantListItemHeader.tsx
@@ -0,0 +1,169 @@
+import React from 'react';
+import {View} from 'react-native';
+import Checkbox from '@components/Checkbox';
+import Icon from '@components/Icon';
+import * as Expensicons from '@components/Icon/Expensicons';
+import type {SearchColumnType} from '@components/Search/types';
+import type {ListItem, TransactionMerchantGroupListItemType} from '@components/SelectionListWithSections/types';
+import TextWithTooltip from '@components/TextWithTooltip';
+import useLocalize from '@hooks/useLocalize';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import CONST from '@src/CONST';
+import ExpandCollapseArrowButton from './ExpandCollapseArrowButton';
+import TextCell from './TextCell';
+import TotalCell from './TotalCell';
+
+type MerchantListItemHeaderProps<TItem extends ListItem> = {
+    /** The merchant group currently being looked at */
+    merchant: TransactionMerchantGroupListItemType;
+
+    /** Callback to fire when a checkbox is pressed */
+    onCheckboxPress?: (item: TItem) => void;
+
+    /** Whether this section items disabled for selection */
+    isDisabled?: boolean | null;
+
+    /** Whether selecting multiple transactions at once is allowed */
+    canSelectMultiple: boolean | undefined;
+
+    /** Whether all transactions are selected */
+    isSelectAllChecked?: boolean;
+
+    /** Whether only some transactions are selected */
+    isIndeterminate?: boolean;
+
+    /** Callback for when the down arrow is clicked */
+    onDownArrowClick?: () => void;
+
+    /** Whether the down arrow is expanded */
+    isExpanded?: boolean;
+
+    /** The visible columns for the header */
+    columns?: SearchColumnType[];
+};
+
+function MerchantListItemHeader<TItem extends ListItem>({
+    merchant: merchantItem,
+    onCheckboxPress,
+    isDisabled,
+    canSelectMultiple,
+    isSelectAllChecked,
+    isIndeterminate,
+    isExpanded,
+    onDownArrowClick,
+    columns,
+}: MerchantListItemHeaderProps<TItem>) {
+    const styles = useThemeStyles();
+    const StyleUtils = useStyleUtils();
+    const theme = useTheme();
+    const {isLargeScreenWidth} = useResponsiveLayout();
+    const {translate} = useLocalize();
+    const formattedMerchant = merchantItem.formattedMerchant ?? merchantItem.merchant;
+
+    const columnComponents = {
+        [CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT]: (
+            <View
+                key={CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT}
+                style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.MERCHANT)}
+            >
+                <View style={[styles.gap1, styles.flexShrink1]}>
+                    <TextWithTooltip
+                        text={formattedMerchant}
+                        style={[styles.optionDisplayName, styles.sidebarLinkTextBold, styles.pre, styles.fontWeightNormal]}
+                    />
+                </View>
+            </View>
+        ),
+        [CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES]: (
+            <View
+                key={CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES}
+                style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.EXPENSES)}
+            >
+                <TextCell text={String(merchantItem.count)} />
+            </View>
+        ),
+        [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: (
+            <View
+                key={CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL}
+                style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TOTAL, false, false, false, false, false, false, false, true)}
+            >
+                <TotalCell
+                    total={merchantItem.total}
+                    currency={merchantItem.currency}
+                />
+            </View>
+        ),
+    };
+
+    return (
+        <View>
+            <View style={[styles.pv1Half, styles.pl3, styles.flexRow, styles.alignItemsCenter, isLargeScreenWidth ? styles.gap3 : styles.justifyContentStart]}>
+                <View style={[styles.flexRow, styles.alignItemsCenter, styles.mnh40, styles.flex1, styles.gap3]}>
+                    {!!canSelectMultiple && (
+                        <Checkbox
+                            onPress={() => onCheckboxPress?.(merchantItem as unknown as TItem)}
+                            isChecked={isSelectAllChecked}
+                            isIndeterminate={isIndeterminate}
+                            disabled={!!isDisabled || merchantItem.isDisabledCheckbox}
+                            accessibilityLabel={translate('common.select')}
+                            style={isLargeScreenWidth && styles.mr1}
+                        />
+                    )}
+                    {!isLargeScreenWidth && (
+                        <View style={[styles.flexRow, styles.flex1, styles.gap3]}>
+                            <View>
+                                <Icon
+                                    src={Expensicons.Receipt}
+                                    fill={theme.icon}
+                                    width={36}
+                                    height={36}
+                                />
+                            </View>
+                            <View style={[styles.gap1, styles.flexShrink1]}>
+                                <TextWithTooltip
+                                    text={formattedMerchant}
+                                    style={[styles.optionDisplayName, styles.sidebarLinkTextBold, styles.pre, styles.fontWeightNormal]}
+                                />
+                            </View>
+                        </View>
+                    )}
+                    {isLargeScreenWidth && (
+                        <>
+                            <View style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.AVATAR)}>
+                                <Icon
+                                    src={Expensicons.Receipt}
+                                    fill={theme.icon}
+                                    width={36}
+                                    height={36}
+                                />
+                            </View>
+
+                            {columns?.map((column) => columnComponents[column as keyof typeof columnComponents])}
+                        </>
+                    )}
+                </View>
+                {!isLargeScreenWidth && (
+                    <View style={[[styles.flexShrink0, styles.mr3, styles.gap1]]}>
+                        <TotalCell
+                            total={merchantItem.total}
+                            currency={merchantItem.currency}
+                        />
+                        {!!onDownArrowClick && (
+                            <ExpandCollapseArrowButton
+                                isExpanded={isExpanded}
+                                onPress={onDownArrowClick}
+                            />
+                        )}
+                    </View>
+                )}
+            </View>
+        </View>
+    );
+}
+
+MerchantListItemHeader.displayName = 'MerchantListItemHeader';
+
+export default MerchantListItemHeader;
diff --git a/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx b/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx
index bf5d45394495..fd6ff6b16c16 100644
--- a/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx
+++ b/src/components/SelectionListWithSections/Search/TransactionGroupListItem.tsx
@@ -17,6 +17,7 @@ import type {
     TransactionGroupListItemType,
     TransactionListItemType,
     TransactionMemberGroupListItemType,
+    TransactionMerchantGroupListItemType,
     TransactionReportGroupListItemType,
     TransactionWithdrawalIDGroupListItemType,
 } from '@components/SelectionListWithSections/types';
@@ -40,6 +41,7 @@ import {isActionLoadingSetSelector} from '@src/selectors/ReportMetaData';
 import type {ReportAction, ReportActions} from '@src/types/onyx';
 import CardListItemHeader from './CardListItemHeader';
 import MemberListItemHeader from './MemberListItemHeader';
+import MerchantListItemHeader from './MerchantListItemHeader';
 import ReportListItemHeader from './ReportListItemHeader';
 import TransactionGroupListExpandedItem from './TransactionGroupListExpanded';
 import WithdrawalIDListItemHeader from './WithdrawalIDListItemHeader';
@@ -195,8 +197,11 @@ function TransactionGroupListItem<TItem extends ListItem>({
         setIsExpanded(!isExpanded);
         if (isExpanded) {
             setTransactionsVisibleLimit(CONST.TRANSACTION.RESULTS_PAGE_SIZE);
+        } else {
+            // Fetch transactions when expanding the group
+            searchTransactions();
         }
-    }, [isExpanded]);
+    }, [isExpanded, searchTransactions]);
 
     const onPress = useCallback(() => {
         if (isExpenseReportType || transactions.length === 0) {
@@ -280,6 +285,19 @@ function TransactionGroupListItem<TItem extends ListItem>({
                         isExpanded={isExpanded}
                     />
                 ),
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: (
+                    <MerchantListItemHeader
+                        merchant={groupItem as TransactionMerchantGroupListItemType}
+                        onCheckboxPress={onCheckboxPress}
+                        isDisabled={isDisabledOrEmpty}
+                        columns={columns}
+                        canSelectMultiple={canSelectMultiple}
+                        isSelectAllChecked={isSelectAllChecked}
+                        isIndeterminate={isIndeterminate}
+                        onDownArrowClick={onExpandIconPress}
+                        isExpanded={isExpanded}
+                    />
+                ),
             };
 
             if (searchType === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT) {
diff --git a/src/components/SelectionListWithSections/SearchTableHeader.tsx b/src/components/SelectionListWithSections/SearchTableHeader.tsx
index 42b7cf9f8129..1426f396a5f8 100644
--- a/src/components/SelectionListWithSections/SearchTableHeader.tsx
+++ b/src/components/SelectionListWithSections/SearchTableHeader.tsx
@@ -22,6 +22,7 @@ type SearchHeaderIcons = {
     Profile?: IconAsset;
     CreditCard?: IconAsset;
     Bank?: IconAsset;
+    Receipt?: IconAsset;
 };
 
 const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [
@@ -337,6 +338,27 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI
                     translationKey: 'common.total',
                 },
             ];
+        case CONST.SEARCH.GROUP_BY.MERCHANT:
+            return [
+                {
+                    columnName: CONST.SEARCH.TABLE_COLUMNS.AVATAR,
+                    translationKey: undefined,
+                    icon: icons.Receipt,
+                    isColumnSortable: false,
+                },
+                {
+                    columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT,
+                    translationKey: 'common.merchant',
+                },
+                {
+                    columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES,
+                    translationKey: 'common.expenses',
+                },
+                {
+                    columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL,
+                    translationKey: 'common.total',
+                },
+            ];
         default:
             return [];
     }
@@ -407,8 +429,11 @@ function SearchTableHeader({
     const {isSmallScreenWidth, isMediumScreenWidth} = useResponsiveLayout();
     const displayNarrowVersion = isMediumScreenWidth || isSmallScreenWidth;
 
+    // Convert MERCHANT sortBy to GROUP_MERCHANT for merchant grouping so the sort arrow appears on the correct column
+    const displaySortBy = groupBy === CONST.SEARCH.GROUP_BY.MERCHANT && sortBy === CONST.SEARCH.TABLE_COLUMNS.MERCHANT ? CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT : sortBy;
+
     // Only load Profile icon when it's needed for EXPENSE_REPORT type or grouped transactions
-    const icons = useMemoizedLazyExpensifyIcons(type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT || !!groupBy ? ['Profile', 'Bank', 'CreditCard'] : []) satisfies SearchHeaderIcons;
+    const icons = useMemoizedLazyExpensifyIcons(type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT || !!groupBy ? ['Profile', 'Bank', 'CreditCard', 'Receipt'] : []) satisfies SearchHeaderIcons;
 
     const shouldShowColumn = useCallback(
         (columnName: SortableColumnName) => {
@@ -469,7 +494,7 @@ function SearchTableHeader({
             amountColumnSize={isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL}
             taxAmountColumnSize={isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL}
             shouldShowSorting={shouldShowSorting}
-            sortBy={sortBy}
+            sortBy={displaySortBy}
             sortOrder={sortOrder}
             // In GroupBy views, disable flex expansion for Total columns so Expenses column gets more space
             shouldRemoveTotalColumnFlex={!!groupBy && !isExpenseReportView}
@@ -479,7 +504,9 @@ function SearchTableHeader({
                 if (columnName === CONST.SEARCH.TABLE_COLUMNS.COMMENTS) {
                     return;
                 }
-                onSortPress(columnName, order);
+                // Convert GROUP_MERCHANT to MERCHANT for sorting to avoid crashes
+                const sortColumn = columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT ? CONST.SEARCH.TABLE_COLUMNS.MERCHANT : columnName;
+                onSortPress(sortColumn, order);
             }}
         />
     );
diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts
index 15464aebe076..5e2fae527edf 100644
--- a/src/components/SelectionListWithSections/types.ts
+++ b/src/components/SelectionListWithSections/types.ts
@@ -28,7 +28,7 @@ import type CONST from '@src/CONST';
 import type {PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, SearchResults, TransactionViolation, TransactionViolations} from '@src/types/onyx';
 import type {Attendee} from '@src/types/onyx/IOU';
 import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon';
-import type {SearchCardGroup, SearchDataTypes, SearchMemberGroup, SearchTask, SearchTransactionAction, SearchWithdrawalIDGroup} from '@src/types/onyx/SearchResults';
+import type {SearchCardGroup, SearchDataTypes, SearchMemberGroup, SearchMerchantGroup, SearchTask, SearchTransactionAction, SearchWithdrawalIDGroup} from '@src/types/onyx/SearchResults';
 import type {ReceiptErrors} from '@src/types/onyx/Transaction';
 import type Transaction from '@src/types/onyx/Transaction';
 import type ChildrenProps from '@src/types/utils/ChildrenProps';
@@ -489,6 +489,11 @@ type TransactionWithdrawalIDGroupListItemType = TransactionGroupListItemType & {
         formattedWithdrawalID?: string;
     };
 
+type TransactionMerchantGroupListItemType = TransactionGroupListItemType & {groupedBy: typeof CONST.SEARCH.GROUP_BY.MERCHANT} & SearchMerchantGroup & {
+        /** Final and formatted "merchant" value used for displaying and sorting */
+        formattedMerchant?: string;
+    };
+
 type ListItemProps<TItem extends ListItem> = CommonListItemProps<TItem> & {
     /** The section list item */
     item: TItem;
@@ -1128,6 +1133,7 @@ export type {
     TransactionGroupListItemType,
     TransactionReportGroupListItemType,
     TransactionMemberGroupListItemType,
+    TransactionMerchantGroupListItemType,
     TransactionCardGroupListItemType,
     TransactionWithdrawalIDGroupListItemType,
     Section,
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 336bf871e906..49304e3268ba 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -6888,6 +6888,7 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
                 [CONST.SEARCH.GROUP_BY.FROM]: 'Von',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Karte',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'Auszahlungs-ID',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Händler',
             },
             feed: 'Feed',
             withdrawalType: {
@@ -6927,6 +6928,7 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
             allMatchingItemsSelected: 'Alle passenden Elemente ausgewählt',
         },
         topSpenders: 'Top-Ausgaben',
+        topMerchants: 'Top-Händler',
     },
     genericErrorPage: {
         title: 'Oh je, etwas ist schiefgelaufen!',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 200e8335566f..9460bd108c0c 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -6708,6 +6708,7 @@ const translations = {
         unapprovedCard: 'Unapproved card',
         reconciliation: 'Reconciliation',
         topSpenders: 'Top spenders',
+        topMerchants: 'Top merchants',
         saveSearch: 'Save search',
         deleteSavedSearch: 'Delete saved search',
         deleteSavedSearchConfirm: 'Are you sure you want to delete this search?',
@@ -6772,6 +6773,7 @@ const translations = {
                 [CONST.SEARCH.GROUP_BY.FROM]: 'From',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Card',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'Withdrawal ID',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Merchant',
             },
             feed: 'Feed',
             withdrawalType: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index a988d3c80d67..b8a79e4d8f1f 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -6458,6 +6458,7 @@ ${amount} para ${merchant} - ${date}`,
         unapprovedCard: 'Tarjeta no aprobada',
         reconciliation: 'Conciliación',
         topSpenders: 'Mayores gastadores',
+        topMerchants: 'Principales comercios',
         saveSearch: 'Guardar búsqueda',
         savedSearchesMenuItemTitle: 'Guardadas',
         searchName: 'Nombre de la búsqueda',
@@ -6520,6 +6521,7 @@ ${amount} para ${merchant} - ${date}`,
                 [CONST.SEARCH.GROUP_BY.FROM]: 'De',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Tarjeta',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID de retiro',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Comerciante',
             },
             feed: 'Feed',
             withdrawalType: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index c80f7a1d5879..24c71d33a67e 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -6900,6 +6900,7 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
                 [CONST.SEARCH.GROUP_BY.FROM]: 'De',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Carte',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID de retrait',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Marchand',
             },
             feed: 'Flux',
             withdrawalType: {
@@ -6939,6 +6940,7 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
             allMatchingItemsSelected: 'Tous les éléments correspondants sont sélectionnés',
         },
         topSpenders: 'Plus gros dépensiers',
+        topMerchants: 'Principaux marchands',
     },
     genericErrorPage: {
         title: 'Oh oh, quelque chose s’est mal passé !',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index c466864413ba..c62e42da30a8 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6879,6 +6879,7 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
                 [CONST.SEARCH.GROUP_BY.FROM]: 'Da',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Carta',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID prelievo',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Esercente',
             },
             feed: 'Feed',
             withdrawalType: {
@@ -6918,6 +6919,7 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
             allMatchingItemsSelected: 'Tutti gli elementi corrispondenti selezionati',
         },
         topSpenders: 'Maggiori spenditori',
+        topMerchants: 'Principali esercenti',
     },
     genericErrorPage: {
         title: 'Uh-oh, qualcosa è andato storto!',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index cbe74fca2668..689cad184311 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -6820,6 +6820,7 @@ ${reportName}
                 [CONST.SEARCH.GROUP_BY.FROM]: '差出人',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'カード',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '出金ID',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: '加盟店',
             },
             feed: 'フィード',
             withdrawalType: {
@@ -6859,6 +6860,7 @@ ${reportName}
             allMatchingItemsSelected: '一致する項目をすべて選択済み',
         },
         topSpenders: 'トップ支出者',
+        topMerchants: 'トップ加盟店',
     },
     genericErrorPage: {
         title: 'おっと、問題が発生しました！',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index d07a6f98c087..6736a9850d73 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -6860,6 +6860,7 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
                 [CONST.SEARCH.GROUP_BY.FROM]: 'Van',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Kaart',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'Opname-ID',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Handelaar',
             },
             feed: 'Feed',
             withdrawalType: {
@@ -6899,6 +6900,7 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
             allMatchingItemsSelected: 'Alle overeenkomende items geselecteerd',
         },
         topSpenders: 'Grootste uitgaven',
+        topMerchants: 'Top handelaren',
     },
     genericErrorPage: {
         title: 'O jee, er is iets misgegaan!',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 08e2d8bb294c..0a08fb1bc823 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -6850,6 +6850,7 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
                 [CONST.SEARCH.GROUP_BY.FROM]: 'Od',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Karta',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID wypłaty',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Kupiec',
             },
             feed: 'Kanał',
             withdrawalType: {
@@ -6889,6 +6890,7 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
             allMatchingItemsSelected: 'Wybrano wszystkie pasujące elementy',
         },
         topSpenders: 'Najwięksi wydający',
+        topMerchants: 'Najlepsi handlowcy',
     },
     genericErrorPage: {
         title: 'Ups, coś poszło nie tak!',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index cfcc64ebc274..5af49cfd16d5 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -6852,6 +6852,7 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
                 [CONST.SEARCH.GROUP_BY.FROM]: 'De',
                 [CONST.SEARCH.GROUP_BY.CARD]: 'Cartão',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: 'ID de saque',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: 'Comerciante',
             },
             feed: 'Feed',
             withdrawalType: {
@@ -6891,6 +6892,7 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
             allMatchingItemsSelected: 'Todos os itens correspondentes selecionados',
         },
         topSpenders: 'Maiores gastadores',
+        topMerchants: 'Principais comerciantes',
     },
     genericErrorPage: {
         title: 'Opa, algo deu errado!',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index c38247687c9a..a488f7d14d04 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -6637,6 +6637,8 @@ ${reportName}
         unapprovedCash: '未批准的现金',
         unapprovedCard: '未批准的卡片',
         reconciliation: '对账',
+        topSpenders: '消费最多者',
+        topMerchants: '消费最多的商家',
         saveSearch: '保存搜索',
         deleteSavedSearch: '删除已保存的搜索',
         deleteSavedSearchConfirm: '您确定要删除此搜索吗？',
@@ -6701,6 +6703,7 @@ ${reportName}
                 [CONST.SEARCH.GROUP_BY.FROM]: '从',
                 [CONST.SEARCH.GROUP_BY.CARD]: '卡片',
                 [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: '提款 ID',
+                [CONST.SEARCH.GROUP_BY.MERCHANT]: '商户',
             },
             feed: '动态',
             withdrawalType: {
@@ -6739,7 +6742,6 @@ ${reportName}
             selectAllMatchingItems: '选择所有匹配的项目',
             allMatchingItemsSelected: '已选择所有匹配的项目',
         },
-        topSpenders: '最高支出者',
     },
     genericErrorPage: {
         title: '哎呀，出错了！',
diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts
index 22123ee77f50..aea25a524505 100644
--- a/src/libs/SearchUIUtils.ts
+++ b/src/libs/SearchUIUtils.ts
@@ -35,6 +35,7 @@ import type {
     TransactionGroupListItemType,
     TransactionListItemType,
     TransactionMemberGroupListItemType,
+    TransactionMerchantGroupListItemType,
     TransactionReportGroupListItemType,
     TransactionWithdrawalIDGroupListItemType,
 } from '@components/SelectionListWithSections/types';
@@ -54,6 +55,7 @@ import type {
     SearchCardGroup,
     SearchDataTypes,
     SearchMemberGroup,
+    SearchMerchantGroup,
     SearchTask,
     SearchTransactionAction,
     SearchWithdrawalIDGroup,
@@ -145,6 +147,7 @@ type ExpenseReportSorting = ColumnSortMapping<ExpenseReportListItemType>;
 type TransactionMemberGroupSorting = ColumnSortMapping<TransactionMemberGroupListItemType>;
 type TransactionCardGroupSorting = ColumnSortMapping<TransactionCardGroupListItemType>;
 type TransactionWithdrawalIDGroupSorting = ColumnSortMapping<TransactionWithdrawalIDGroupListItemType>;
+type TransactionMerchantGroupSorting = ColumnSortMapping<TransactionMerchantGroupListItemType>;
 
 type GetReportSectionsParams = {
     data: OnyxTypes.SearchResults['data'];
@@ -229,6 +232,14 @@ const transactionWithdrawalIDGroupColumnNamesToSortingProperty: TransactionWithd
     [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: 'total' as const,
 };
 
+const transactionMerchantGroupColumnNamesToSortingProperty: TransactionMerchantGroupSorting = {
+    [CONST.SEARCH.TABLE_COLUMNS.AVATAR]: null,
+    [CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: 'formattedMerchant' as const,
+    [CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT]: 'formattedMerchant' as const,
+    [CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES]: 'count' as const,
+    [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: 'total' as const,
+};
+
 const expenseStatusActionMapping = {
     [CONST.SEARCH.STATUS.EXPENSE.DRAFTS]: (expenseReport?: OnyxTypes.Report) =>
         expenseReport?.stateNum === CONST.REPORT.STATE_NUM.OPEN && expenseReport.statusNum === CONST.REPORT.STATUS_NUM.OPEN,
@@ -630,6 +641,32 @@ function getSuggestedSearches(
                 return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
             },
         },
+        [CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS]: {
+            key: CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS,
+            translationPath: 'search.topMerchants',
+            type: CONST.SEARCH.DATA_TYPES.EXPENSE,
+            icon: Expensicons.Receipt,
+            searchQuery: buildQueryStringFromFilterFormValues(
+                {
+                    type: CONST.SEARCH.DATA_TYPES.EXPENSE,
+                    groupBy: CONST.SEARCH.GROUP_BY.MERCHANT,
+                    dateOn: CONST.SEARCH.DATE_PRESETS.LAST_MONTH,
+                },
+                {
+                    sortBy: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL,
+                    sortOrder: CONST.SEARCH.SORT_ORDER.DESC,
+                },
+            ),
+            get searchQueryJSON() {
+                return buildSearchQueryJSON(this.searchQuery);
+            },
+            get hash() {
+                return this.searchQueryJSON?.hash ?? CONST.DEFAULT_NUMBER_ID;
+            },
+            get similarSearchHash() {
+                return this.searchQueryJSON?.similarSearchHash ?? CONST.DEFAULT_NUMBER_ID;
+            },
+        },
     };
 }
 
@@ -652,6 +689,7 @@ function getSuggestedSearchesVisibility(
     let shouldShowUnapprovedCardSuggestion = false;
     let shouldShowReconciliationSuggestion = false;
     let shouldShowTopSpendersSuggestion = false;
+    let shouldShowTopMerchantsSuggestion = false;
 
     const hasCardFeed = Object.values(cardFeedsByPolicy ?? {}).some((feeds) => feeds.length > 0);
 
@@ -688,6 +726,7 @@ function getSuggestedSearchesVisibility(
         const isEligibleForReconciliationSuggestion = isPaidPolicy && isAdmin && ((isPaymentEnabled && hasVBBA && hasReimburser) || isECardEnabled);
         const isAuditor = policy.role === CONST.POLICY.ROLE.AUDITOR;
         const isEligibleForTopSpendersSuggestion = isPaidPolicy && (isAdmin || isAuditor || isApprover);
+        const isEligibleForTopMerchantsSuggestion = isPaidPolicy;
 
         shouldShowSubmitSuggestion ||= isEligibleForSubmitSuggestion;
         shouldShowPaySuggestion ||= isEligibleForPaySuggestion;
@@ -698,6 +737,7 @@ function getSuggestedSearchesVisibility(
         shouldShowUnapprovedCardSuggestion ||= isEligibleForUnapprovedCardSuggestion;
         shouldShowReconciliationSuggestion ||= isEligibleForReconciliationSuggestion;
         shouldShowTopSpendersSuggestion ||= isEligibleForTopSpendersSuggestion;
+        shouldShowTopMerchantsSuggestion ||= isEligibleForTopMerchantsSuggestion;
 
         // We don't need to check the rest of the policies if we already determined that all suggestions should be displayed
         return (
@@ -709,7 +749,8 @@ function getSuggestedSearchesVisibility(
             shouldShowUnapprovedCashSuggestion &&
             shouldShowUnapprovedCardSuggestion &&
             shouldShowReconciliationSuggestion &&
-            shouldShowTopSpendersSuggestion
+            shouldShowTopSpendersSuggestion &&
+            shouldShowTopMerchantsSuggestion
         );
     });
 
@@ -726,6 +767,7 @@ function getSuggestedSearchesVisibility(
         [CONST.SEARCH.SEARCH_KEYS.UNAPPROVED_CARD]: shouldShowUnapprovedCardSuggestion,
         [CONST.SEARCH.SEARCH_KEYS.RECONCILIATION]: shouldShowReconciliationSuggestion,
         [CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS]: shouldShowTopSpendersSuggestion,
+        [CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS]: shouldShowTopMerchantsSuggestion,
     };
 }
 
@@ -865,6 +907,13 @@ function isTransactionWithdrawalIDGroupListItemType(item: ListItem): item is Tra
     return isTransactionGroupListItemType(item) && 'groupedBy' in item && item.groupedBy === CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID;
 }
 
+/**
+ * Type guard that checks if something is a TransactionMerchantGroupListItemType
+ */
+function isTransactionMerchantGroupListItemType(item: ListItem): item is TransactionMerchantGroupListItemType {
+    return isTransactionGroupListItemType(item) && 'groupedBy' in item && item.groupedBy === CONST.SEARCH.GROUP_BY.MERCHANT;
+}
+
 /**
  * Type guard that checks if something is a TransactionListItemType
  */
@@ -2066,6 +2115,41 @@ function getWithdrawalIDSections(data: OnyxTypes.SearchResults['data'], queryJSO
     return [withdrawalIDSectionsValues, withdrawalIDSectionsValues.length];
 }
 
+/**
+ * @private
+ * Organizes data into List Sections grouped by merchant for display, for the TransactionMerchantGroupListItemType of Search Results.
+ *
+ * Do not use directly, use only via `getSections()` facade.
+ */
+function getMerchantSections(data: OnyxTypes.SearchResults['data'], queryJSON: SearchQueryJSON | undefined): [TransactionMerchantGroupListItemType[], number] {
+    const merchantSections: Record<string, TransactionMerchantGroupListItemType> = {};
+
+    for (const key in data) {
+        if (isGroupEntry(key)) {
+            const merchantGroup = data[key] as SearchMerchantGroup;
+            let transactionsQueryJSON: SearchQueryJSON | undefined;
+            if (queryJSON && merchantGroup.merchant) {
+                const newFlatFilters = queryJSON.flatFilters.filter((filter) => filter.key !== CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT);
+                newFlatFilters.push({key: CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT, filters: [{operator: CONST.SEARCH.SYNTAX_OPERATORS.EQUAL_TO, value: merchantGroup.merchant}]});
+                const newQueryJSON: SearchQueryJSON = {...queryJSON, groupBy: undefined, flatFilters: newFlatFilters};
+                const newQuery = buildSearchQueryString(newQueryJSON);
+                transactionsQueryJSON = buildSearchQueryJSON(newQuery);
+            }
+
+            merchantSections[key] = {
+                groupedBy: CONST.SEARCH.GROUP_BY.MERCHANT,
+                transactions: [],
+                transactionsQueryJSON,
+                ...merchantGroup,
+                formattedMerchant: merchantGroup.merchant || '',
+            };
+        }
+    }
+
+    const merchantSectionsValues = Object.values(merchantSections);
+    return [merchantSectionsValues, merchantSectionsValues.length];
+}
+
 /**
  * Returns the appropriate list item component based on the type and status of the search data.
  */
@@ -2139,6 +2223,8 @@ function getSections({
                 return getCardSections(data, queryJSON, translate, cardFeeds);
             case CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID:
                 return getWithdrawalIDSections(data, queryJSON);
+            case CONST.SEARCH.GROUP_BY.MERCHANT:
+                return getMerchantSections(data, queryJSON);
         }
     }
 
@@ -2178,6 +2264,8 @@ function getSortedSections(
                 return getSortedCardData(data as TransactionCardGroupListItemType[], localeCompare, sortBy, sortOrder);
             case CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID:
                 return getSortedWithdrawalIDData(data as TransactionWithdrawalIDGroupListItemType[], localeCompare, sortBy, sortOrder);
+            case CONST.SEARCH.GROUP_BY.MERCHANT:
+                return getSortedMerchantData(data as TransactionMerchantGroupListItemType[], localeCompare, sortBy, sortOrder);
         }
     }
 
@@ -2534,6 +2622,21 @@ function getSortedWithdrawalIDData(data: TransactionWithdrawalIDGroupListItemTyp
     return getSortedData(data, localeCompare, transactionWithdrawalIDGroupColumnNamesToSortingProperty, (a, b) => localeCompare(a.debitPosted, b.debitPosted), sortBy, sortOrder);
 }
 
+/**
+ * @private
+ * Sorts merchant sections based on a specified column and sort order.
+ */
+function getSortedMerchantData(data: TransactionMerchantGroupListItemType[], localeCompare: LocaleContextProps['localeCompare'], sortBy?: SearchColumnType, sortOrder?: SortOrder) {
+    return getSortedData(
+        data,
+        localeCompare,
+        transactionMerchantGroupColumnNamesToSortingProperty,
+        (a, b) => localeCompare(a.formattedMerchant ?? '', b.formattedMerchant ?? ''),
+        sortBy,
+        sortOrder,
+    );
+}
+
 /**
  * @private
  * Sorts report actions sections based on a specified column and sort order.
@@ -2604,6 +2707,8 @@ function getCustomColumns(value?: SearchDataTypes | SearchGroupBy): SearchCustom
             return Object.values(CONST.SEARCH.GROUP_CUSTOM_COLUMNS.FROM);
         case CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID:
             return Object.values(CONST.SEARCH.GROUP_CUSTOM_COLUMNS.WITHDRAWAL_ID);
+        case CONST.SEARCH.GROUP_BY.MERCHANT:
+            return Object.values(CONST.SEARCH.GROUP_CUSTOM_COLUMNS.MERCHANT);
         default:
             return [];
     }
@@ -2629,12 +2734,14 @@ function getCustomColumnDefault(value?: SearchDataTypes | SearchGroupBy): Search
             return CONST.SEARCH.GROUP_DEFAULT_COLUMNS.FROM;
         case CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID:
             return CONST.SEARCH.GROUP_DEFAULT_COLUMNS.WITHDRAWAL_ID;
+        case CONST.SEARCH.GROUP_BY.MERCHANT:
+            return CONST.SEARCH.GROUP_DEFAULT_COLUMNS.MERCHANT;
         default:
             return [];
     }
 }
 
-function getSearchColumnTranslationKey(columnId: SearchCustomColumnIds): TranslationPaths {
+function getSearchColumnTranslationKey(columnId: SearchCustomColumnIds): TranslationPaths | undefined {
     // eslint-disable-next-line default-case
     switch (columnId) {
         case CONST.SEARCH.TABLE_COLUMNS.DATE:
@@ -2713,6 +2820,8 @@ function getSearchColumnTranslationKey(columnId: SearchCustomColumnIds): Transla
             return 'search.filters.feed';
         case CONST.SEARCH.TABLE_COLUMNS.EXPORTED_TO:
             return 'search.exportedTo';
+        case CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT:
+            return 'common.merchant';
     }
 }
 
@@ -2943,6 +3052,16 @@ function createTypeMenuSections(
             });
         }
 
+        if (suggestedSearchesVisibility[CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS]) {
+            insightsSection.menuItems.push({
+                ...suggestedSearches[CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS],
+                emptyState: {
+                    title: 'search.searchResults.emptyResults.title',
+                    subtitle: 'search.searchResults.emptyResults.subtitle',
+                },
+            });
+        }
+
         if (insightsSection.menuItems.length > 0) {
             typeMenuSections.push(insightsSection);
         }
@@ -3176,12 +3295,14 @@ function getColumnsToShow(
             [CONST.SEARCH.GROUP_BY.CARD]: CONST.SEARCH.GROUP_CUSTOM_COLUMNS.CARD,
             [CONST.SEARCH.GROUP_BY.FROM]: CONST.SEARCH.GROUP_CUSTOM_COLUMNS.FROM,
             [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: CONST.SEARCH.GROUP_CUSTOM_COLUMNS.WITHDRAWAL_ID,
+            [CONST.SEARCH.GROUP_BY.MERCHANT]: CONST.SEARCH.GROUP_CUSTOM_COLUMNS.MERCHANT,
         }[groupBy];
 
         const defaultCustomColumns = {
             [CONST.SEARCH.GROUP_BY.CARD]: CONST.SEARCH.GROUP_DEFAULT_COLUMNS.CARD,
             [CONST.SEARCH.GROUP_BY.FROM]: CONST.SEARCH.GROUP_DEFAULT_COLUMNS.FROM,
             [CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID]: CONST.SEARCH.GROUP_DEFAULT_COLUMNS.WITHDRAWAL_ID,
+            [CONST.SEARCH.GROUP_BY.MERCHANT]: CONST.SEARCH.GROUP_DEFAULT_COLUMNS.MERCHANT,
         }[groupBy];
 
         const filteredVisibleColumns = visibleColumns.filter((column) => Object.values(customColumns).includes(column as ValueOf<typeof customColumns>));
@@ -3237,6 +3358,23 @@ function getColumnsToShow(
 
             return result;
         }
+
+        if (groupBy === CONST.SEARCH.GROUP_BY.MERCHANT) {
+            const requiredColumns = new Set<SearchColumnType>([CONST.SEARCH.TABLE_COLUMNS.AVATAR, CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT]);
+            const result: SearchColumnType[] = [];
+
+            for (const col of requiredColumns) {
+                if (!columnsToShow.includes(col as SearchCustomColumnIds)) {
+                    result.push(col);
+                }
+            }
+
+            for (const col of columnsToShow) {
+                result.push(col);
+            }
+
+            return result;
+        }
     }
 
     const columns: ColumnVisibility = isExpenseReportView
@@ -3519,6 +3657,7 @@ export {
     isTransactionMemberGroupListItemType,
     isTransactionCardGroupListItemType,
     isTransactionWithdrawalIDGroupListItemType,
+    isTransactionMerchantGroupListItemType,
     isSearchResultsEmpty,
     isTransactionListItemType,
     isReportActionListItemType,
diff --git a/src/pages/Search/SearchColumnsPage.tsx b/src/pages/Search/SearchColumnsPage.tsx
index a0e4b181d35d..b691b6b74a36 100644
--- a/src/pages/Search/SearchColumnsPage.tsx
+++ b/src/pages/Search/SearchColumnsPage.tsx
@@ -61,6 +61,7 @@ function SearchColumnsPage() {
         CONST.SEARCH.TABLE_COLUMNS.GROUP_CARD,
         CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID,
         CONST.SEARCH.TABLE_COLUMNS.GROUP_FROM,
+        CONST.SEARCH.TABLE_COLUMNS.GROUP_MERCHANT,
     ]);
 
     const sortColumns = (columnsToSort: ColumnItem[]): ColumnItem[] => {
@@ -68,8 +69,10 @@ function SearchColumnsPage() {
         const unselected = columnsToSort
             .filter((col) => !col.isSelected)
             .sort((a, b) => {
-                const textA = translate(getSearchColumnTranslationKey(a.value));
-                const textB = translate(getSearchColumnTranslationKey(b.value));
+                const keyA = getSearchColumnTranslationKey(a.value);
+                const keyB = getSearchColumnTranslationKey(b.value);
+                const textA = keyA ? translate(keyA) : '';
+                const textB = keyB ? translate(keyB) : '';
                 return localeCompare(textA, textB);
             });
         return [...selected, ...unselected];
@@ -100,8 +103,9 @@ function SearchColumnsPage() {
             const isRequired = requiredColumns.has(columnId);
             const isEffectivelySelected = isRequired || isSelected;
             const isDragDisabled = !isEffectivelySelected;
+            const translationKey = getSearchColumnTranslationKey(columnId);
             return {
-                text: translate(getSearchColumnTranslationKey(columnId)),
+                text: translationKey ? translate(translationKey) : '',
                 value: columnId,
                 keyForList: columnId,
                 isSelected: isEffectivelySelected,
@@ -147,8 +151,10 @@ function SearchColumnsPage() {
                 const selected = prevColumns.filter((col) => col.isSelected);
                 const unselected = prevColumns.filter((col) => !col.isSelected && col.columnId !== updatedColumnId);
                 const unselectedSorted = unselected.sort((a, b) => {
-                    const textA = translate(getSearchColumnTranslationKey(a.columnId));
-                    const textB = translate(getSearchColumnTranslationKey(b.columnId));
+                    const keyA = getSearchColumnTranslationKey(a.columnId);
+                    const keyB = getSearchColumnTranslationKey(b.columnId);
+                    const textA = keyA ? translate(keyA) : '';
+                    const textB = keyB ? translate(keyB) : '';
                     return localeCompare(textA, textB);
                 });
                 return [...selected, {columnId: updatedColumnId, isSelected: true}, ...unselectedSorted];
diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts
index c711db8944e3..8c426d664ab4 100644
--- a/src/types/onyx/SearchResults.ts
+++ b/src/types/onyx/SearchResults.ts
@@ -172,6 +172,21 @@ type SearchWithdrawalIDGroup = {
     state: number;
 };
 
+/** Model of merchant grouped search result */
+type SearchMerchantGroup = {
+    /** Merchant name */
+    merchant: string;
+
+    /** Number of transactions */
+    count: number;
+
+    /** Total value of transactions */
+    total: number;
+
+    /** Currency of total value */
+    currency: string;
+};
+
 /** Model of search results */
 type SearchResults = {
     /** Current search results state */
@@ -186,7 +201,7 @@ type SearchResults = {
         PrefixedRecord<typeof ONYXKEYS.COLLECTION.POLICY, Policy> &
         PrefixedRecord<typeof ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, TransactionViolation[]> &
         PrefixedRecord<typeof ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, ReportNameValuePairs> &
-        PrefixedRecord<typeof CONST.SEARCH.GROUP_PREFIX, SearchMemberGroup | SearchCardGroup | SearchWithdrawalIDGroup>;
+        PrefixedRecord<typeof CONST.SEARCH.GROUP_PREFIX, SearchMemberGroup | SearchCardGroup | SearchWithdrawalIDGroup | SearchMerchantGroup>;
 
     /** Whether search data is being fetched from server */
     isLoading?: boolean;
@@ -197,4 +212,15 @@ type SearchResults = {
 
 export default SearchResults;
 
-export type {ListItemType, ListItemDataType, SearchTask, SearchTransactionAction, SearchDataTypes, SearchResultsInfo, SearchMemberGroup, SearchCardGroup, SearchWithdrawalIDGroup};
+export type {
+    ListItemType,
+    ListItemDataType,
+    SearchTask,
+    SearchTransactionAction,
+    SearchDataTypes,
+    SearchResultsInfo,
+    SearchMemberGroup,
+    SearchCardGroup,
+    SearchWithdrawalIDGroup,
+    SearchMerchantGroup,
+};
diff --git a/tests/unit/Search/MerchantListItemHeaderTest.tsx b/tests/unit/Search/MerchantListItemHeaderTest.tsx
new file mode 100644
index 000000000000..169524d16a8d
--- /dev/null
+++ b/tests/unit/Search/MerchantListItemHeaderTest.tsx
@@ -0,0 +1,205 @@
+import {render, screen} from '@testing-library/react-native';
+import React from 'react';
+import MerchantListItemHeader from '@components/SelectionListWithSections/Search/MerchantListItemHeader';
+import type {TransactionMerchantGroupListItemType} from '@components/SelectionListWithSections/types';
+import CONST from '@src/CONST';
+
+jest.mock('@components/Checkbox', () => {
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any, react/function-component-definition -- test mock
+    const MockCheckbox = ({onPress, isChecked, disabled, accessibilityLabel}: any) => {
+        // eslint-disable-next-line no-restricted-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- using RN primitives in test
+        const {Pressable, Text} = require('react-native');
+        return (
+            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test mock
+            <Pressable
+                // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test mock
+                onPress={onPress}
+                // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test mock
+                disabled={disabled}
+                // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test mock
+                accessibilityLabel={accessibilityLabel}
+                // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test mock
+                accessibilityState={{checked: isChecked, disabled}}
+                testID="checkbox"
+            >
+                <Text>{isChecked ? 'checked' : 'unchecked'}</Text>
+            </Pressable>
+        );
+    };
+    return MockCheckbox;
+});
+
+jest.mock('@components/Icon', () => () => null);
+
+jest.mock('@components/TextWithTooltip', () => {
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any, react/function-component-definition -- test mock
+    const MockTextWithTooltip = ({text}: any) => {
+        // eslint-disable-next-line no-restricted-imports, @typescript-eslint/no-unsafe-assignment -- test mock
+        const {Text} = require('react-native');
+        return <Text>{text}</Text>;
+    };
+    return MockTextWithTooltip;
+});
+
+jest.mock('@components/SelectionListWithSections/Search/TextCell', () => {
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any, react/function-component-definition -- test mock
+    const MockTextCell = ({text}: any) => {
+        // eslint-disable-next-line no-restricted-imports, @typescript-eslint/no-unsafe-assignment -- test mock
+        const {Text} = require('react-native');
+        return <Text testID="TextCell">{text}</Text>;
+    };
+    return MockTextCell;
+});
+
+jest.mock('@components/SelectionListWithSections/Search/TotalCell', () => {
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any, react/function-component-definition -- test mock
+    const MockTotalCell = ({total}: any) => {
+        // eslint-disable-next-line no-restricted-imports, @typescript-eslint/no-unsafe-assignment -- test mock
+        const {Text} = require('react-native');
+        return <Text testID="TotalCell">{`$${(total / 100).toFixed(2)}`}</Text>;
+    };
+    return MockTotalCell;
+});
+
+jest.mock('@components/SelectionListWithSections/Search/ExpandCollapseArrowButton', () => () => null);
+
+jest.mock('@hooks/useLocalize', () => () => ({
+    translate: (key: string) => key,
+}));
+
+jest.mock('@hooks/useResponsiveLayout', () => ({
+    // eslint-disable-next-line @typescript-eslint/naming-convention -- CJS interop
+    __esModule: true,
+    default: jest.fn(() => ({
+        isLargeScreenWidth: false,
+        isSmallScreenWidth: false,
+        isMediumScreenWidth: false,
+        shouldUseNarrowLayout: false,
+    })),
+}));
+
+jest.mock('@hooks/useTheme', () => () => ({
+    icon: '#000000',
+}));
+
+jest.mock('@hooks/useThemeStyles', () => () => ({}));
+
+jest.mock('@hooks/useStyleUtils', () => () => ({
+    getReportTableColumnStyles: () => ({}),
+    getIconWidthAndHeightStyle: () => ({width: 36, height: 36}),
+    getWidthAndHeightStyle: () => ({}),
+}));
+
+describe('MerchantListItemHeader', () => {
+    const mockMerchant: TransactionMerchantGroupListItemType = {
+        merchant: 'Test Merchant',
+        formattedMerchant: 'Test Merchant',
+        total: 10000,
+        currency: 'USD',
+        count: 5,
+        keyForList: 'test-merchant',
+        transactions: [],
+        groupedBy: 'merchant',
+    };
+
+    it('should render merchant name', () => {
+        render(
+            <MerchantListItemHeader
+                merchant={mockMerchant}
+                canSelectMultiple={false}
+            />,
+        );
+
+        expect(screen.getByText('Test Merchant')).toBeTruthy();
+    });
+
+    it('should render checkbox when canSelectMultiple is true', () => {
+        render(
+            <MerchantListItemHeader
+                merchant={mockMerchant}
+                canSelectMultiple
+            />,
+        );
+
+        expect(screen.getByLabelText('common.select')).toBeTruthy();
+    });
+
+    it('should not render checkbox when canSelectMultiple is false', () => {
+        render(
+            <MerchantListItemHeader
+                merchant={mockMerchant}
+                canSelectMultiple={false}
+            />,
+        );
+
+        expect(screen.queryByLabelText('common.select')).toBeNull();
+    });
+
+    it('should use formattedMerchant when available', () => {
+        const merchantWithFormatted: TransactionMerchantGroupListItemType = {
+            ...mockMerchant,
+            merchant: 'Raw Merchant',
+            formattedMerchant: 'Formatted Merchant',
+        };
+
+        render(
+            <MerchantListItemHeader
+                merchant={merchantWithFormatted}
+                canSelectMultiple={false}
+            />,
+        );
+
+        expect(screen.getByText('Formatted Merchant')).toBeTruthy();
+    });
+
+    it('should handle disabled state', () => {
+        render(
+            <MerchantListItemHeader
+                merchant={mockMerchant}
+                canSelectMultiple
+                isDisabled
+            />,
+        );
+
+        const checkbox = screen.getByLabelText('common.select');
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- test props check
+        expect(checkbox.props.accessibilityState?.disabled).toBe(true);
+    });
+
+    it('should handle checked state', () => {
+        render(
+            <MerchantListItemHeader
+                merchant={mockMerchant}
+                canSelectMultiple
+                isSelectAllChecked
+            />,
+        );
+
+        const checkbox = screen.getByLabelText('common.select');
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- test props check
+        expect(checkbox.props.accessibilityState?.checked).toBe(true);
+    });
+
+    it('should render expense count in large screen columns', () => {
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- test mock
+        const mockUseResponsiveLayout = jest.requireMock('@hooks/useResponsiveLayout').default;
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access -- test mock
+        mockUseResponsiveLayout.mockReturnValueOnce({
+            isLargeScreenWidth: true,
+            isSmallScreenWidth: false,
+            isMediumScreenWidth: false,
+            shouldUseNarrowLayout: false,
+        });
+
+        const columns = [CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES];
+
+        render(
+            <MerchantListItemHeader
+                merchant={mockMerchant}
+                canSelectMultiple={false}
+                columns={columns}
+            />,
+        );
+        expect(screen.getByText('5')).toBeTruthy();
+    });
+});
diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts
index dd885cd8b4ef..9898de251ecb 100644
--- a/tests/unit/Search/SearchUIUtilsTest.ts
+++ b/tests/unit/Search/SearchUIUtilsTest.ts
@@ -3325,4 +3325,114 @@ describe('SearchUIUtils', () => {
             expect(transactionThread).toBeTruthy();
         });
     });
+
+    describe('getSearchColumnTranslationKey', () => {
+        it('should return correct translation key for DATE column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.DATE)).toBe('common.date');
+        });
+
+        it('should return correct translation key for SUBMITTED column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.SUBMITTED)).toBe('common.submitted');
+        });
+
+        it('should return correct translation key for APPROVED column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.APPROVED)).toBe('search.filters.approved');
+        });
+
+        it('should return correct translation key for POSTED column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.POSTED)).toBe('search.filters.posted');
+        });
+
+        it('should return correct translation key for EXPORTED column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.EXPORTED)).toBe('search.filters.exported');
+        });
+
+        it('should return correct translation key for MERCHANT column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.MERCHANT)).toBe('common.merchant');
+        });
+
+        it('should return correct translation key for DESCRIPTION column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION)).toBe('common.description');
+        });
+
+        it('should return correct translation key for FROM column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.FROM)).toBe('common.from');
+        });
+
+        it('should return correct translation key for TO column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.TO)).toBe('common.to');
+        });
+
+        it('should return correct translation key for CATEGORY column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.CATEGORY)).toBe('common.category');
+        });
+
+        it('should return correct translation key for RECEIPT column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.RECEIPT)).toBe('common.receipt');
+        });
+
+        it('should return correct translation key for TAG column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.TAG)).toBe('common.tag');
+        });
+
+        it('should return correct translation key for ORIGINAL_AMOUNT column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT)).toBe('common.originalAmount');
+        });
+
+        it('should return correct translation key for REIMBURSABLE column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE)).toBe('common.reimbursable');
+        });
+
+        it('should return correct translation key for BILLABLE column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.BILLABLE)).toBe('common.billable');
+        });
+
+        it('should return correct translation key for ACTION column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.ACTION)).toBe('common.action');
+        });
+
+        it('should return correct translation key for TITLE column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.TITLE)).toBe('common.title');
+        });
+
+        it('should return correct translation key for STATUS column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.STATUS)).toBe('common.status');
+        });
+
+        it('should return correct translation key for EXCHANGE_RATE column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE)).toBe('common.exchangeRate');
+        });
+
+        it('should return correct translation key for POLICY_NAME column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.POLICY_NAME)).toBe('workspace.common.workspace');
+        });
+
+        it('should return correct translation key for CARD column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.CARD)).toBe('common.card');
+        });
+
+        it('should return correct translation key for REIMBURSABLE_TOTAL column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL)).toBe('common.reimbursableTotal');
+        });
+
+        it('should return correct translation key for NON_REIMBURSABLE_TOTAL column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL)).toBe('common.nonReimbursableTotal');
+        });
+
+        it('should return correct translation key for TAX_AMOUNT column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT)).toBe('common.tax');
+        });
+
+        it('should return correct translation key for TAX_RATE column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.TAX_RATE)).toBe('iou.taxRate');
+        });
+
+        it('should return correct translation key for REPORT_ID column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.REPORT_ID)).toBe('common.longReportID');
+        });
+
+        it('should return correct translation key for TOTAL_AMOUNT column', () => {
+            expect(SearchUIUtils.getSearchColumnTranslationKey(CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT)).toBe('iou.amount');
+        });
+    });
 });
