diff --git a/docs/plugins/search.md b/docs/plugins/search.md
new file mode 100644
index 0000000000000000000000000000000000000000..05856f1703724d85759ff8752a2d5ebad04595b7
--- /dev/null
+++ b/docs/plugins/search.md
@@ -0,0 +1,49 @@
+### Search
+
+Search can be done by query or coordinates. To search, plugins can use the `triggerSearch` method in `window.minerva.map` object available globally.
+
+**Search by query:**
+If we want to search using a query, we need to provide an object with the following properties as an argument:
+
+- query: this should be the search string.
+- perfectSearch: this property indicates whether results should be a perfect match or not. Its value should be a boolean type. This property is optional, and by default, its value is `false`.
+- fitBounds: should the map be resized to the rectangle fitting all results. Its value should be a boolean type. This property is optional, and by default, its value is `false`.
+
+##### Example of search by query:
+
+```javascript
+window.minerva.map.triggerSearch({ query: 'NR4A2;SNCA;aspirin', perfectSearch: true });
+
+window.minerva.map.triggerSearch({ query: 'NR4A2;SNCA;aspirin;morphine;PINK1' });
+
+window.minerva.map.triggerSearch({ query: 'PINK1', fitBounds: true });
+```
+
+**Search by coordinates**:
+If we want to search using coordinates, we need to provide an object with the following properties as an argument:
+
+- coordinates: this property should indicate the x and y coordinates on the map. Its value should be an object type with x and y properties
+- modelId: this property should indicate submap identifier. Its value should be a number type
+- zoom: this property should indicate zoom level at which we want to trigger search. Its value should be a number type
+- fitBounds: should the map be resized to the rectangle fitting all results. Its value should be a boolean type. This property is optional, and by default, its value is `false`.
+
+##### Example of search by query:
+
+```javascript
+window.minerva.map.triggerSearch({ coordinates: { x: 947, y: 503 }, modelId: 60 });
+
+window.minerva.map.triggerSearch({
+  coordinates: { x: 1947, y: 5203 },
+  modelId: 52,
+  fitBounds: true,
+});
+
+window.minerva.map.triggerSearch({ coordinates: { x: 1947, y: 5203 }, modelId: 60, zoom: 5 });
+
+window.minerva.map.triggerSearch({
+  coordinates: { x: 1947, y: 5203 },
+  modelId: 51,
+  fitBounds: true,
+  zoom: 6,
+});
+```
diff --git a/index.d.ts b/index.d.ts
index 248f6f8ecb2d8aa13e21e2859fbfafacc263eba2..e093f9d7f2f5f9e64d9cdacb27aaa24c1e471471 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -1,3 +1,4 @@
+import { triggerSearch } from '@/services/pluginsManager/map/triggerSearch';
 import { MinervaConfiguration } from '@/services/pluginsManager/pluginsManager';
 
 type Plugin = {
@@ -22,6 +23,9 @@ declare global {
       plugins: {
         registerPlugin: RegisterPlugin;
       };
+      map: {
+        triggerSearch: typeof triggerSearch;
+      };
     };
   }
 }
diff --git a/src/services/pluginsManager/map/triggerSearch/displaySearchDrawerWithSelectedDefaultTab.ts b/src/services/pluginsManager/map/triggerSearch/displaySearchDrawerWithSelectedDefaultTab.ts
new file mode 100644
index 0000000000000000000000000000000000000000..135025d1ddafca34db9642d2d33d863d74c0e5c6
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/displaySearchDrawerWithSelectedDefaultTab.ts
@@ -0,0 +1,16 @@
+import { getDefaultSearchTab } from '@/components/FunctionalArea/TopBar/SearchBar/SearchBar.utils';
+import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors';
+import { openSearchDrawerWithSelectedTab, selectTab } from '@/redux/drawer/drawer.slice';
+import { store } from '@/redux/store';
+
+export const displaySearchDrawerWithSelectedDefaultTab = (searchValues: string[]): void => {
+  const { dispatch, getState } = store;
+  const isDrawerOpen = isDrawerOpenSelector(getState());
+  const defaultSearchTab = getDefaultSearchTab(searchValues);
+
+  if (!isDrawerOpen) {
+    dispatch(openSearchDrawerWithSelectedTab(defaultSearchTab));
+  } else {
+    dispatch(selectTab(defaultSearchTab));
+  }
+};
diff --git a/src/services/pluginsManager/map/triggerSearch/index.ts b/src/services/pluginsManager/map/triggerSearch/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e33cfdc81a3dd7b8d8d8aaf5ec89614628b480c7
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/index.ts
@@ -0,0 +1 @@
+export { triggerSearch } from './triggerSearch';
diff --git a/src/services/pluginsManager/map/triggerSearch/searchByCoordinates.ts b/src/services/pluginsManager/map/triggerSearch/searchByCoordinates.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bdd6a539ddbfc7eca3934a8700061acc54d8de24
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/searchByCoordinates.ts
@@ -0,0 +1,27 @@
+import { handleDataReset } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleDataReset';
+import { handleSearchResultAction } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleSearchResultAction';
+import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
+import { store } from '@/redux/store';
+import { getElementsByPoint } from '@/utils/search/getElementsByCoordinates';
+import { Coordinates } from './triggerSearch.types';
+
+export const searchByCoordinates = async (
+  coordinates: Coordinates,
+  modelId: number,
+): Promise<void> => {
+  const { dispatch } = store;
+  // side-effect below is to prevent complications with data update - old data may conflict with new data
+  // so we need to reset all the data before updating
+  dispatch(handleDataReset);
+
+  const searchResults = await getElementsByPoint({
+    point: coordinates,
+    currentModelId: modelId,
+  });
+
+  if (!searchResults || searchResults?.length === SIZE_OF_EMPTY_ARRAY) {
+    return;
+  }
+
+  handleSearchResultAction({ searchResults, dispatch });
+};
diff --git a/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts b/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01d0603743171cebd905be553cf7d61aa6824fc1
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
@@ -0,0 +1,14 @@
+import { getSearchValuesArrayAndTrimToSeven } from '@/components/FunctionalArea/TopBar/SearchBar/SearchBar.utils';
+import { getSearchData } from '@/redux/search/search.thunks';
+import { store } from '@/redux/store';
+import { displaySearchDrawerWithSelectedDefaultTab } from './displaySearchDrawerWithSelectedDefaultTab';
+
+export const searchByQuery = (query: string, perfectSearch: boolean | undefined): void => {
+  const { dispatch } = store;
+  const searchValues = getSearchValuesArrayAndTrimToSeven(query);
+  const isPerfectMatch = !!perfectSearch;
+
+  dispatch(getSearchData({ searchQueries: searchValues, isPerfectMatch }));
+
+  displaySearchDrawerWithSelectedDefaultTab(searchValues);
+};
diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c7408ebf99514fc11abc6bd75118bae26192724
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts
@@ -0,0 +1,205 @@
+/* eslint-disable no-magic-numbers */
+import { mockNetworkNewAPIResponse, mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { apiPath } from '@/redux/apiPath';
+import { HttpStatusCode } from 'axios';
+import { drugsFixture } from '@/models/fixtures/drugFixtures';
+import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
+import { RootState, store } from '@/redux/store';
+import { ELEMENT_SEARCH_RESULT_MOCK_ALIAS } from '@/models/mocks/elementSearchResultMock';
+import { bioEntityResponseFixture } from '@/models/fixtures/bioEntityContentsFixture';
+import { waitFor } from '@testing-library/react';
+import { handleSearchResultAction } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleSearchResultAction';
+import { triggerSearch } from './triggerSearch';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+const mockedAxiosOldClient = mockNetworkResponse();
+const SEARCH_QUERY = 'park7';
+const point = { x: 545.8013, y: 500.9926 };
+const modelId = 1000;
+
+jest.mock('../../../../redux/store');
+jest.mock(
+  '../../../../components/Map/MapViewer/utils/listeners/mapSingleClick/handleSearchResultAction',
+);
+
+describe('triggerSearch', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+  describe('search by query', () => {
+    it('should throw error if query param is wrong type', async () => {
+      const invalidParams = {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        query: 123 as any,
+      };
+
+      await expect(triggerSearch(invalidParams)).rejects.toThrowError(
+        'Invalid query type. The query should be of string type',
+      );
+    });
+    it('should search for provided query and open drawer when it is not open', async () => {
+      const getState = jest.spyOn(store, 'getState').mockImplementation(
+        () =>
+          ({
+            drawer: {
+              isOpen: false,
+            },
+          }) as RootState,
+      );
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: SEARCH_QUERY,
+            isPerfectMatch: false,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getDrugsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, drugsFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, chemicalsFixture);
+
+      await expect(
+        triggerSearch({
+          query: SEARCH_QUERY,
+        }),
+      ).resolves.toBe(undefined);
+
+      expect(store.dispatch).toHaveBeenCalledTimes(2);
+
+      expect(store.dispatch).not.toHaveBeenCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/selectTab',
+      });
+
+      expect(store.dispatch).toHaveBeenLastCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/openSearchDrawerWithSelectedTab',
+      });
+      getState.mockRestore();
+    });
+    it('should search for provided query and select default tab when drawer is already open', async () => {
+      const getState = jest.spyOn(store, 'getState').mockImplementation(
+        () =>
+          ({
+            drawer: {
+              isOpen: true,
+            },
+          }) as RootState,
+      );
+
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: SEARCH_QUERY,
+            isPerfectMatch: false,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getDrugsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, drugsFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, chemicalsFixture);
+
+      await expect(
+        triggerSearch({
+          query: SEARCH_QUERY,
+        }),
+      ).resolves.toBe(undefined);
+
+      expect(getState).toHaveBeenCalled();
+      expect(store.dispatch).toHaveBeenCalledTimes(2);
+      expect(store.dispatch).not.toHaveBeenLastCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/openSearchDrawerWithSelectedTab',
+      });
+      expect(store.dispatch).toHaveBeenLastCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/selectTab',
+      });
+
+      getState.mockRestore();
+    });
+  });
+  describe('search by coordinations', () => {
+    it('should throw error if coordinates param is wrong type', async () => {
+      const invalidParams = {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        coordinates: {} as any,
+        modelId: 53,
+      };
+
+      await expect(triggerSearch(invalidParams)).rejects.toThrowError(
+        'Invalid coordinates type or values',
+      );
+    });
+    it('should throw error if model id param is wrong type', async () => {
+      const invalidParams = {
+        coordinates: { x: 992, y: 993 },
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        modelId: '53' as any,
+      };
+
+      await expect(triggerSearch(invalidParams)).rejects.toThrowError(
+        'Invalid model id type. The model should be of number type',
+      );
+    });
+    it('should search result with proper data', async () => {
+      mockedAxiosOldClient
+        .onGet(apiPath.getSingleBioEntityContentsStringWithCoordinates(point, modelId))
+        .reply(HttpStatusCode.Ok, [ELEMENT_SEARCH_RESULT_MOCK_ALIAS]);
+
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
+            isPerfectMatch: true,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+      const params = {
+        coordinates: point,
+        modelId,
+      };
+
+      await expect(triggerSearch(params)).resolves.toBe(undefined);
+
+      await waitFor(() => {
+        expect(handleSearchResultAction).toHaveBeenCalledWith({
+          searchResults: [ELEMENT_SEARCH_RESULT_MOCK_ALIAS],
+          dispatch: store.dispatch,
+        });
+      });
+    });
+    it('should not search result if there is no bio entity with specific coordinates', async () => {
+      mockedAxiosOldClient
+        .onGet(apiPath.getSingleBioEntityContentsStringWithCoordinates(point, modelId))
+        .reply(HttpStatusCode.Ok, []);
+
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
+            isPerfectMatch: true,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+      const params = {
+        coordinates: point,
+        modelId,
+      };
+
+      await expect(triggerSearch(params)).resolves.toBe(undefined);
+
+      expect(handleSearchResultAction).not.toHaveBeenCalled();
+    });
+  });
+});
diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a747095a9d1fdf446b603ea06e0760b40c9a906
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.ts
@@ -0,0 +1,28 @@
+import { SearchParams } from './triggerSearch.types';
+import { searchByQuery } from './searchByQuery';
+import { searchByCoordinates } from './searchByCoordinates';
+
+export async function triggerSearch(params: SearchParams): Promise<void> {
+  if ('query' in params) {
+    if (typeof params.query !== 'string') {
+      throw new Error('Invalid query type. The query should be of string type');
+    }
+    searchByQuery(params.query, params.perfectSearch);
+  } else {
+    const areCoordinatesInvalidType =
+      typeof params.coordinates !== 'object' || params.coordinates === null;
+    const areCoordinatesMissingKeys = !('x' in params.coordinates) || !('y' in params.coordinates);
+    const areCoordinatesValuesInvalid =
+      typeof params.coordinates.x !== 'number' || typeof params.coordinates.y !== 'number';
+
+    if (areCoordinatesInvalidType || areCoordinatesMissingKeys || areCoordinatesValuesInvalid) {
+      throw new Error('Invalid coordinates type or values');
+    }
+
+    if (typeof params.modelId !== 'number') {
+      throw new Error('Invalid model id type. The model should be of number type');
+    }
+
+    searchByCoordinates(params.coordinates, params.modelId);
+  }
+}
diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.types.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa6cd67ad8cc1ad8db58bc7ca2cf7ddd15dba6ab
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.types.ts
@@ -0,0 +1,19 @@
+export type SearchByQueryParams = {
+  query: string;
+  perfectSearch?: boolean;
+  fitBounds?: boolean;
+};
+
+export type Coordinates = {
+  x: number;
+  y: number;
+};
+
+export type SearchByCoordinatesParams = {
+  coordinates: Coordinates;
+  modelId: number;
+  fitBounds?: boolean;
+  zoom?: number;
+};
+
+export type SearchParams = SearchByCoordinatesParams | SearchByQueryParams;
diff --git a/src/services/pluginsManager/pluginsManager.ts b/src/services/pluginsManager/pluginsManager.ts
index 83a734c7fd958fc03b15259866441bd644517ae9..1d6c0bc0e2f58d5dcc3ce3182e01d48ea5d97676 100644
--- a/src/services/pluginsManager/pluginsManager.ts
+++ b/src/services/pluginsManager/pluginsManager.ts
@@ -4,6 +4,7 @@ import { store } from '@/redux/store';
 import md5 from 'crypto-js/md5';
 import type { PluginsManagerType } from './pluginsManager.types';
 import { configurationMapper } from './pluginsManager.utils';
+import { triggerSearch } from './map/triggerSearch';
 import { PluginsEventBus } from './pluginsEventBus';
 
 export const PluginsManager: PluginsManagerType = {
@@ -20,6 +21,9 @@ export const PluginsManager: PluginsManagerType = {
       plugins: {
         registerPlugin: PluginsManager.registerPlugin,
       },
+      map: {
+        triggerSearch,
+      },
     };
 
     const unsubscribe = store.subscribe(() => {