diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md
index 2473c9cfdde8d..cc0cb538be611 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md
@@ -19,6 +19,7 @@ export interface ISearchOptions
 |  [isRestore](./kibana-plugin-plugins-data-public.isearchoptions.isrestore.md) | <code>boolean</code> | Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) |
 |  [isStored](./kibana-plugin-plugins-data-public.isearchoptions.isstored.md) | <code>boolean</code> | Whether the session is already saved (i.e. sent to background) |
 |  [legacyHitsTotal](./kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md) | <code>boolean</code> | Request the legacy format for the total number of hits. If sending <code>rest_total_hits_as_int</code> to something other than <code>true</code>, this should be set to <code>false</code>. |
+|  [requestResponder](./kibana-plugin-plugins-data-public.isearchoptions.requestresponder.md) | <code>RequestResponder</code> |  |
 |  [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) | <code>string</code> | A session ID, grouping multiple search requests into a single session. |
 |  [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | <code>string</code> | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. |
 
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.requestresponder.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.requestresponder.md
new file mode 100644
index 0000000000000..b4431b9467b71
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.requestresponder.md
@@ -0,0 +1,11 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) &gt; [requestResponder](./kibana-plugin-plugins-data-public.isearchoptions.requestresponder.md)
+
+## ISearchOptions.requestResponder property
+
+<b>Signature:</b>
+
+```typescript
+requestResponder?: RequestResponder;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
index cfaad01c029ea..259009c1c5668 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
@@ -53,7 +53,6 @@ search: {
             timeRange: import("../common").TimeRange | undefined;
         } | undefined;
     };
-    getRequestInspectorStats: typeof getRequestInspectorStats;
     getResponseInspectorStats: typeof getResponseInspectorStats;
     tabifyAggResponse: typeof tabifyAggResponse;
     tabifyGetColumns: typeof tabifyGetColumns;
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md
index cc50d3f017971..d384b9659dbcd 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md
@@ -9,9 +9,9 @@ Returns body contents of the search request, often referred as query DSL.
 <b>Signature:</b>
 
 ```typescript
-getSearchRequestBody(): Promise<any>;
+getSearchRequestBody(): any;
 ```
 <b>Returns:</b>
 
-`Promise<any>`
+`any`
 
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md
index 7fd4dd5b8e566..413a59be3d427 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md
@@ -19,6 +19,7 @@ export interface ISearchOptions
 |  [isRestore](./kibana-plugin-plugins-data-server.isearchoptions.isrestore.md) | <code>boolean</code> | Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) |
 |  [isStored](./kibana-plugin-plugins-data-server.isearchoptions.isstored.md) | <code>boolean</code> | Whether the session is already saved (i.e. sent to background) |
 |  [legacyHitsTotal](./kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md) | <code>boolean</code> | Request the legacy format for the total number of hits. If sending <code>rest_total_hits_as_int</code> to something other than <code>true</code>, this should be set to <code>false</code>. |
+|  [requestResponder](./kibana-plugin-plugins-data-server.isearchoptions.requestresponder.md) | <code>RequestResponder</code> |  |
 |  [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) | <code>string</code> | A session ID, grouping multiple search requests into a single session. |
 |  [strategy](./kibana-plugin-plugins-data-server.isearchoptions.strategy.md) | <code>string</code> | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. |
 
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.requestresponder.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.requestresponder.md
new file mode 100644
index 0000000000000..7440f5a9d26cf
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.requestresponder.md
@@ -0,0 +1,11 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) &gt; [requestResponder](./kibana-plugin-plugins-data-server.isearchoptions.requestresponder.md)
+
+## ISearchOptions.requestResponder property
+
+<b>Signature:</b>
+
+```typescript
+requestResponder?: RequestResponder;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md
index 0911c3e86964d..930f7710f9a00 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.search.md
@@ -36,8 +36,6 @@ search: {
         toAbsoluteDates: typeof toAbsoluteDates;
         calcAutoIntervalLessThan: typeof calcAutoIntervalLessThan;
     };
-    getRequestInspectorStats: typeof getRequestInspectorStats;
-    getResponseInspectorStats: typeof getResponseInspectorStats;
     tabifyAggResponse: typeof tabifyAggResponse;
     tabifyGetColumns: typeof tabifyGetColumns;
 }
diff --git a/examples/search_examples/public/search/app.tsx b/examples/search_examples/public/search/app.tsx
index c87bf21e0e71c..3bac445581ae7 100644
--- a/examples/search_examples/public/search/app.tsx
+++ b/examples/search_examples/public/search/app.tsx
@@ -204,8 +204,8 @@ export const SearchExamplesApp = ({
         });
       }
 
-      setRequest(await searchSource.getSearchRequestBody());
-      const res = await searchSource.fetch();
+      setRequest(searchSource.getSearchRequestBody());
+      const res = await searchSource.fetch$().toPromise();
       setResponse(res);
 
       const message = <EuiText>Searched {res.hits.total} documents.</EuiText>;
diff --git a/src/plugins/data/common/search/aggs/buckets/terms.ts b/src/plugins/data/common/search/aggs/buckets/terms.ts
index 7d37dc83405b8..77c9c6e391c0a 100644
--- a/src/plugins/data/common/search/aggs/buckets/terms.ts
+++ b/src/plugins/data/common/search/aggs/buckets/terms.ts
@@ -8,7 +8,6 @@
 
 import { noop } from 'lodash';
 import { i18n } from '@kbn/i18n';
-import type { RequestAdapter } from 'src/plugins/inspector/common';
 
 import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
 import { BUCKET_TYPES } from './bucket_agg_types';
@@ -21,7 +20,6 @@ import { aggTermsFnName } from './terms_fn';
 import { AggConfigSerialized, BaseAggParams } from '../types';
 
 import { KBN_FIELD_TYPES } from '../../../../common';
-import { getRequestInspectorStats, getResponseInspectorStats } from '../../expressions';
 
 import {
   buildOtherBucketAgg,
@@ -103,36 +101,28 @@ export const getTermsBucketAgg = () =>
 
         nestedSearchSource.setField('aggs', filterAgg);
 
-        let request: ReturnType<RequestAdapter['start']> | undefined;
-        if (inspectorRequestAdapter) {
-          request = inspectorRequestAdapter.start(
-            i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', {
-              defaultMessage: 'Other bucket',
+        const requestResponder = inspectorRequestAdapter?.start(
+          i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', {
+            defaultMessage: 'Other bucket',
+          }),
+          {
+            description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', {
+              defaultMessage:
+                'This request counts the number of documents that fall ' +
+                'outside the criterion of the data buckets.',
             }),
-            {
-              description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', {
-                defaultMessage:
-                  'This request counts the number of documents that fall ' +
-                  'outside the criterion of the data buckets.',
-              }),
-              searchSessionId,
-            }
-          );
-          nestedSearchSource.getSearchRequestBody().then((body) => {
-            request!.json(body);
-          });
-          request.stats(getRequestInspectorStats(nestedSearchSource));
-        }
+            searchSessionId,
+          }
+        );
+
+        const response = await nestedSearchSource
+          .fetch$({
+            abortSignal,
+            sessionId: searchSessionId,
+            requestResponder,
+          })
+          .toPromise();
 
-        const response = await nestedSearchSource.fetch({
-          abortSignal,
-          sessionId: searchSessionId,
-        });
-        if (request) {
-          request
-            .stats(getResponseInspectorStats(response, nestedSearchSource))
-            .ok({ json: response });
-        }
         resp = mergeOtherBucketAggResponse(aggConfigs, resp, response, aggConfig, filterAgg());
       }
       if (aggConfig.params.missingBucket) {
diff --git a/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts
index 7580032b0dd85..c2566535916a8 100644
--- a/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts
+++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts
@@ -133,7 +133,7 @@ describe('esaggs expression function - public', () => {
   test('calls searchSource.fetch', async () => {
     await handleRequest(mockParams);
     const searchSource = await mockParams.searchSourceService.create();
-    expect(searchSource.fetch).toHaveBeenCalledWith({
+    expect(searchSource.fetch$).toHaveBeenCalledWith({
       abortSignal: mockParams.abortSignal,
       sessionId: mockParams.searchSessionId,
     });
diff --git a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts
index 72d9cc4095570..5620698a47538 100644
--- a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts
+++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts
@@ -22,7 +22,6 @@ import {
 import { IAggConfigs } from '../../aggs';
 import { ISearchStartSearchSource } from '../../search_source';
 import { tabifyAggResponse } from '../../tabify';
-import { getRequestInspectorStats, getResponseInspectorStats } from '../utils';
 
 /** @internal */
 export interface RequestHandlerParams {
@@ -41,6 +40,21 @@ export interface RequestHandlerParams {
   getNow?: () => Date;
 }
 
+function getRequestMainResponder(inspectorAdapters: Adapters, searchSessionId?: string) {
+  return inspectorAdapters.requests?.start(
+    i18n.translate('data.functions.esaggs.inspector.dataRequest.title', {
+      defaultMessage: 'Data',
+    }),
+    {
+      description: i18n.translate('data.functions.esaggs.inspector.dataRequest.description', {
+        defaultMessage:
+          'This request queries Elasticsearch to fetch the data for the visualization.',
+      }),
+      searchSessionId,
+    }
+  );
+}
+
 export const handleRequest = async ({
   abortSignal,
   aggs,
@@ -113,52 +127,19 @@ export const handleRequest = async ({
   requestSearchSource.setField('filter', filters);
   requestSearchSource.setField('query', query);
 
-  let request;
-  if (inspectorAdapters.requests) {
-    inspectorAdapters.requests.reset();
-    request = inspectorAdapters.requests.start(
-      i18n.translate('data.functions.esaggs.inspector.dataRequest.title', {
-        defaultMessage: 'Data',
-      }),
-      {
-        description: i18n.translate('data.functions.esaggs.inspector.dataRequest.description', {
-          defaultMessage:
-            'This request queries Elasticsearch to fetch the data for the visualization.',
-        }),
-        searchSessionId,
-      }
-    );
-    request.stats(getRequestInspectorStats(requestSearchSource));
-  }
-
-  try {
-    const response = await requestSearchSource.fetch({
-      abortSignal,
-      sessionId: searchSessionId,
-    });
-
-    if (request) {
-      request.stats(getResponseInspectorStats(response, searchSource)).ok({ json: response });
-    }
+  inspectorAdapters.requests?.reset();
+  const requestResponder = getRequestMainResponder(inspectorAdapters, searchSessionId);
 
-    (searchSource as any).rawResponse = response;
-  } catch (e) {
-    // Log any error during request to the inspector
-    if (request) {
-      request.error({ json: e });
-    }
-    throw e;
-  } finally {
-    // Add the request body no matter if things went fine or not
-    if (request) {
-      request.json(await requestSearchSource.getSearchRequestBody());
-    }
-  }
+  const response$ = await requestSearchSource.fetch$({
+    abortSignal,
+    sessionId: searchSessionId,
+    requestResponder,
+  });
 
   // Note that rawResponse is not deeply cloned here, so downstream applications using courier
   // must take care not to mutate it, or it could have unintended side effects, e.g. displaying
   // response data incorrectly in the inspector.
-  let response = (searchSource as any).rawResponse;
+  let response = await response$.toPromise();
   for (const agg of aggs.aggs) {
     if (agg.enabled && typeof agg.type.postFlightRequest === 'function') {
       response = await agg.type.postFlightRequest(
diff --git a/src/plugins/data/common/search/expressions/utils/index.ts b/src/plugins/data/common/search/expressions/utils/index.ts
index 2fa54d47445b3..a6ea8da6ac6e9 100644
--- a/src/plugins/data/common/search/expressions/utils/index.ts
+++ b/src/plugins/data/common/search/expressions/utils/index.ts
@@ -6,5 +6,4 @@
  * Side Public License, v 1.
  */
 
-export * from './courier_inspector_stats';
 export * from './function_wrapper';
diff --git a/src/plugins/data/common/search/search_source/index.ts b/src/plugins/data/common/search/search_source/index.ts
index 1cb04075dad7a..757e0de6ecb49 100644
--- a/src/plugins/data/common/search/search_source/index.ts
+++ b/src/plugins/data/common/search/search_source/index.ts
@@ -10,6 +10,7 @@ export { createSearchSource } from './create_search_source';
 export { injectReferences } from './inject_references';
 export { extractReferences } from './extract_references';
 export { parseSearchSourceJSON } from './parse_json';
+export { getResponseInspectorStats } from './inspect';
 export * from './fetch';
 export * from './legacy';
 export * from './search_source';
diff --git a/src/plugins/data/common/search/search_source/inspect/index.ts b/src/plugins/data/common/search/search_source/inspect/index.ts
new file mode 100644
index 0000000000000..d5947f8a18cc9
--- /dev/null
+++ b/src/plugins/data/common/search/search_source/inspect/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export * from './inspector_stats';
diff --git a/src/plugins/data/common/search/expressions/utils/courier_inspector_stats.ts b/src/plugins/data/common/search/search_source/inspect/inspector_stats.ts
similarity index 97%
rename from src/plugins/data/common/search/expressions/utils/courier_inspector_stats.ts
rename to src/plugins/data/common/search/search_source/inspect/inspector_stats.ts
index 99acbce8935c4..24507a7e13058 100644
--- a/src/plugins/data/common/search/expressions/utils/courier_inspector_stats.ts
+++ b/src/plugins/data/common/search/search_source/inspect/inspector_stats.ts
@@ -15,8 +15,8 @@
 
 import { i18n } from '@kbn/i18n';
 import type { estypes } from '@elastic/elasticsearch';
-import { ISearchSource } from 'src/plugins/data/public';
-import { RequestStatistics } from 'src/plugins/inspector/common';
+import type { ISearchSource } from 'src/plugins/data/public';
+import type { RequestStatistics } from 'src/plugins/inspector/common';
 
 /** @public */
 export function getRequestInspectorStats(searchSource: ISearchSource) {
diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts
index fd97a3d3381a9..3726e5d0c33e8 100644
--- a/src/plugins/data/common/search/search_source/search_source.test.ts
+++ b/src/plugins/data/common/search/search_source/search_source.test.ts
@@ -125,7 +125,7 @@ describe('SearchSource', () => {
           }),
         } as unknown) as IndexPattern);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.stored_fields).toEqual(['hello']);
         expect(request.script_fields).toEqual({ world: {} });
         expect(request.fields).toEqual(['@timestamp']);
@@ -144,7 +144,7 @@ describe('SearchSource', () => {
         searchSource.setField('fields', ['@timestamp']);
         searchSource.setField('fieldsFromSource', ['foo']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).not.toHaveProperty('docvalue_fields');
       });
 
@@ -160,7 +160,7 @@ describe('SearchSource', () => {
         // @ts-expect-error TS won't like using this field name, but technically it's possible.
         searchSource.setField('docvalue_fields', ['world']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).toHaveProperty('docvalue_fields');
         expect(request.docvalue_fields).toEqual(['world']);
       });
@@ -179,7 +179,7 @@ describe('SearchSource', () => {
         searchSource.setField('fields', ['c']);
         searchSource.setField('fieldsFromSource', ['a', 'b', 'd']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).toHaveProperty('docvalue_fields');
         expect(request._source.includes).toEqual(['c', 'a', 'b', 'd']);
         expect(request.docvalue_fields).toEqual([{ field: 'b', format: 'date_time' }]);
@@ -202,7 +202,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', [{ field: 'hello', format: 'strict_date_time' }]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).toHaveProperty('fields');
         expect(request.fields).toEqual([{ field: 'hello', format: 'strict_date_time' }]);
       });
@@ -218,7 +218,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['hello']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).toHaveProperty('fields');
         expect(request.fields).toEqual([{ field: 'hello', format: 'date_time' }]);
       });
@@ -239,7 +239,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', [{ field: 'hello', a: 'a', c: 'c' }]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).toHaveProperty('fields');
         expect(request.fields).toEqual([
           { field: 'hello', format: 'date_time', a: 'a', b: 'test', c: 'c' },
@@ -258,7 +258,7 @@ describe('SearchSource', () => {
         // @ts-expect-error TS won't like using this field name, but technically it's possible.
         searchSource.setField('script_fields', { world: {} });
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request).toHaveProperty('script_fields');
         expect(request.script_fields).toEqual({
           hello: {},
@@ -277,7 +277,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['hello', 'a', { field: 'c' }]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.script_fields).toEqual({ hello: {} });
         expect(request.stored_fields).toEqual(['a', 'c']);
       });
@@ -293,7 +293,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['hello', 'a', { foo: 'c' }]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.script_fields).toEqual({ hello: {} });
         expect(request.stored_fields).toEqual(['a']);
       });
@@ -309,23 +309,23 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fieldsFromSource', ['hello', 'a']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.script_fields).toEqual({ hello: {} });
         expect(request.stored_fields).toEqual(['a']);
       });
 
       test('defaults to * for stored fields when no fields are provided', async () => {
-        const requestA = await searchSource.getSearchRequestBody();
+        const requestA = searchSource.getSearchRequestBody();
         expect(requestA.stored_fields).toEqual(['*']);
 
         searchSource.setField('fields', ['*']);
-        const requestB = await searchSource.getSearchRequestBody();
+        const requestB = searchSource.getSearchRequestBody();
         expect(requestB.stored_fields).toEqual(['*']);
       });
 
       test('defaults to * for stored fields when no fields are provided with fieldsFromSource', async () => {
         searchSource.setField('fieldsFromSource', ['*']);
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.stored_fields).toEqual(['*']);
       });
     });
@@ -343,7 +343,7 @@ describe('SearchSource', () => {
         // @ts-expect-error Typings for excludes filters need to be fixed.
         searchSource.setField('source', { excludes: ['exclude-*'] });
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual(['@timestamp']);
       });
 
@@ -357,7 +357,7 @@ describe('SearchSource', () => {
           }),
         } as unknown) as IndexPattern);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual(['@timestamp']);
       });
 
@@ -372,7 +372,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['hello']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.script_fields).toEqual({ hello: {} });
       });
 
@@ -387,7 +387,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['hello', 'foo']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual(['hello']);
       });
 
@@ -402,7 +402,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['*']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual([{ field: 'field1' }, { field: 'field2' }]);
       });
 
@@ -417,7 +417,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', [{ field: '*', include_unmapped: 'true' }]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual([{ field: 'field1' }, { field: 'field2' }]);
       });
 
@@ -432,7 +432,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['timestamp', '*']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.script_fields).toEqual({ hello: {}, world: {} });
       });
     });
@@ -455,7 +455,7 @@ describe('SearchSource', () => {
           'bar-b',
         ]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request._source).toEqual({
           includes: ['@timestamp', 'bar-b'],
         });
@@ -473,7 +473,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['hello', '@timestamp', 'foo-a', 'bar']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual(['hello', '@timestamp', 'bar', 'date']);
         expect(request.script_fields).toEqual({ hello: {} });
         expect(request.stored_fields).toEqual(['@timestamp', 'bar']);
@@ -498,7 +498,7 @@ describe('SearchSource', () => {
           'runtime_field',
         ]);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request._source).toEqual({
           includes: ['@timestamp', 'bar'],
         });
@@ -520,7 +520,7 @@ describe('SearchSource', () => {
         searchSource.setField('fields', ['hello', '@timestamp', 'foo-a', 'bar']);
         searchSource.setField('fieldsFromSource', ['foo-b', 'date', 'baz']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request._source).toEqual({
           includes: ['@timestamp', 'bar', 'date', 'baz'],
         });
@@ -546,7 +546,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['*']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual([
           '*',
           { field: '@timestamp', format: 'strict_date_optional_time_nanos' },
@@ -574,7 +574,7 @@ describe('SearchSource', () => {
         } as unknown) as IndexPattern);
         searchSource.setField('fields', ['*']);
 
-        const request = await searchSource.getSearchRequestBody();
+        const request = searchSource.getSearchRequestBody();
         expect(request.fields).toEqual([
           { field: 'foo-bar' },
           { field: 'field1' },
@@ -592,14 +592,14 @@ describe('SearchSource', () => {
             expect(searchSource.getField('source')).toBe(undefined);
             searchSource.setField('index', indexPattern);
             expect(searchSource.getField('index')).toBe(indexPattern);
-            const request = await searchSource.getSearchRequestBody();
+            const request = searchSource.getSearchRequestBody();
             expect(request._source).toBe(mockSource);
           });
 
           test('removes created searchSource filter on removal', async () => {
             searchSource.setField('index', indexPattern);
             searchSource.setField('index', undefined);
-            const request = await searchSource.getSearchRequestBody();
+            const request = searchSource.getSearchRequestBody();
             expect(request._source).toBe(undefined);
           });
         });
@@ -609,7 +609,7 @@ describe('SearchSource', () => {
             searchSource.setField('index', indexPattern);
             searchSource.setField('index', indexPattern2);
             expect(searchSource.getField('index')).toBe(indexPattern2);
-            const request = await searchSource.getSearchRequestBody();
+            const request = searchSource.getSearchRequestBody();
             expect(request._source).toBe(mockSource2);
           });
 
@@ -617,7 +617,7 @@ describe('SearchSource', () => {
             searchSource.setField('index', indexPattern);
             searchSource.setField('index', indexPattern2);
             searchSource.setField('index', undefined);
-            const request = await searchSource.getSearchRequestBody();
+            const request = searchSource.getSearchRequestBody();
             expect(request._source).toBe(undefined);
           });
         });
@@ -808,7 +808,7 @@ describe('SearchSource', () => {
           docvalueFields: [],
         }),
       } as unknown) as IndexPattern);
-      const request = await searchSource.getSearchRequestBody();
+      const request = searchSource.getSearchRequestBody();
       expect(request.stored_fields).toEqual(['geometry', 'prop1']);
       expect(request.docvalue_fields).toEqual(['prop1']);
       expect(request._source).toEqual(['geometry']);
diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts
index f11e7f06b6ab9..e1e7a8292d677 100644
--- a/src/plugins/data/common/search/search_source/search_source.ts
+++ b/src/plugins/data/common/search/search_source/search_source.ts
@@ -60,7 +60,7 @@
 
 import { setWith } from '@elastic/safer-lodash-set';
 import { uniqueId, keyBy, pick, difference, isFunction, isEqual, uniqWith, isObject } from 'lodash';
-import { map, switchMap, tap } from 'rxjs/operators';
+import { catchError, finalize, map, switchMap, tap } from 'rxjs/operators';
 import { defer, from } from 'rxjs';
 import { normalizeSortRequest } from './normalize_sort_request';
 import { fieldWildcardFilter } from '../../../../kibana_utils/common';
@@ -73,6 +73,7 @@ import type {
   SearchSourceFields,
 } from './types';
 import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from './fetch';
+import { getRequestInspectorStats, getResponseInspectorStats } from './inspect';
 
 import { getEsQueryConfig, buildEsQuery, Filter, UI_SETTINGS } from '../../../common';
 import { getHighlightRequest } from '../../../common/field_formats';
@@ -256,6 +257,9 @@ export class SearchSource {
   fetch$(options: ISearchOptions = {}) {
     const { getConfig } = this.dependencies;
     return defer(() => this.requestIsStarting(options)).pipe(
+      tap(() => {
+        options.requestResponder?.stats(getRequestInspectorStats(this));
+      }),
       switchMap(() => {
         const searchRequest = this.flatten();
         this.history = [searchRequest];
@@ -271,7 +275,17 @@ export class SearchSource {
         // TODO: Remove casting when https://github.com/elastic/elasticsearch-js/issues/1287 is resolved
         if ((response as any).error) {
           throw new RequestFailure(null, response);
+        } else {
+          options.requestResponder?.stats(getResponseInspectorStats(response, this));
+          options.requestResponder?.ok({ json: response });
         }
+      }),
+      catchError((e) => {
+        options.requestResponder?.error({ json: e });
+        throw e;
+      }),
+      finalize(() => {
+        options.requestResponder?.json(this.getSearchRequestBody());
       })
     );
   }
@@ -298,9 +312,8 @@ export class SearchSource {
   /**
    * Returns body contents of the search request, often referred as query DSL.
    */
-  async getSearchRequestBody() {
-    const searchRequest = await this.flatten();
-    return searchRequest.body;
+  getSearchRequestBody() {
+    return this.flatten().body;
   }
 
   /**
diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts
index d77a2ea62bb9a..37de8dc49d3c6 100644
--- a/src/plugins/data/common/search/types.ts
+++ b/src/plugins/data/common/search/types.ts
@@ -9,6 +9,7 @@
 import { Observable } from 'rxjs';
 import { IEsSearchRequest, IEsSearchResponse } from './es_search';
 import { IndexPattern } from '..';
+import type { RequestResponder } from '../../../inspector/common';
 
 export type ISearchGeneric = <
   SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
@@ -118,6 +119,8 @@ export interface ISearchOptions {
    */
 
   indexPattern?: IndexPattern;
+
+  requestResponder?: RequestResponder;
 }
 
 /**
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index d2683e248b7bf..e86b64d135d59 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -314,8 +314,6 @@ import {
   boundsDescendingRaw,
   getNumberHistogramIntervalByDatatableColumn,
   getDateHistogramMetaDataByDatatableColumn,
-  // expressions utils
-  getRequestInspectorStats,
   getResponseInspectorStats,
   // tabify
   tabifyAggResponse,
@@ -428,7 +426,6 @@ export const search = {
     getNumberHistogramIntervalByDatatableColumn,
     getDateHistogramMetaDataByDatatableColumn,
   },
-  getRequestInspectorStats,
   getResponseInspectorStats,
   tabifyAggResponse,
   tabifyGetColumns,
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 3c0739e8bc167..264428a40c438 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -1684,6 +1684,10 @@ export interface ISearchOptions {
     isRestore?: boolean;
     isStored?: boolean;
     legacyHitsTotal?: boolean;
+    // Warning: (ae-forgotten-export) The symbol "RequestResponder" needs to be exported by the entry point index.d.ts
+    //
+    // (undocumented)
+    requestResponder?: RequestResponder;
     sessionId?: string;
     strategy?: string;
 }
@@ -2306,7 +2310,6 @@ export const search: {
             timeRange: import("../common").TimeRange | undefined;
         } | undefined;
     };
-    getRequestInspectorStats: typeof getRequestInspectorStats;
     getResponseInspectorStats: typeof getResponseInspectorStats;
     tabifyAggResponse: typeof tabifyAggResponse;
     tabifyGetColumns: typeof tabifyGetColumns;
@@ -2442,7 +2445,7 @@ export class SearchSource {
     getId(): string;
     getOwnField<K extends keyof SearchSourceFields>(field: K): SearchSourceFields[K];
     getParent(): SearchSource | undefined;
-    getSearchRequestBody(): Promise<any>;
+    getSearchRequestBody(): any;
     getSerializedFields(recurse?: boolean): SearchSourceFields;
     // Warning: (ae-incompatible-release-tags) The symbol "history" is marked as @public, but its signature references "SearchRequest" which is marked as @internal
     //
@@ -2720,21 +2723,20 @@ export interface WaitUntilNextSessionCompletesOptions {
 // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts
 // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
 // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:425:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:426:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:429:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:430:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:433:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:404:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:404:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:404:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:416:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:417:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:424:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:427:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:428:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:431:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
 // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
 // src/plugins/data/public/search/session/session_service.ts:56:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts
 
diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts
index cbf09ef57d96a..fa54f45d2feb2 100644
--- a/src/plugins/data/server/index.ts
+++ b/src/plugins/data/server/index.ts
@@ -176,9 +176,6 @@ import {
   parseEsInterval,
   parseInterval,
   toAbsoluteDates,
-  // expressions utils
-  getRequestInspectorStats,
-  getResponseInspectorStats,
   // tabify
   tabifyAggResponse,
   tabifyGetColumns,
@@ -263,8 +260,6 @@ export const search = {
     toAbsoluteDates,
     calcAutoIntervalLessThan,
   },
-  getRequestInspectorStats,
-  getResponseInspectorStats,
   tabifyAggResponse,
   tabifyGetColumns,
 };
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 29bad29aa7ccb..f0a370639cbdc 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -56,7 +56,6 @@ import { PublicMethodsOf } from '@kbn/utility-types';
 import { RecursiveReadonly } from '@kbn/utility-types';
 import { RequestAdapter } from 'src/plugins/inspector/common';
 import { RequestHandlerContext } from 'src/core/server';
-import { RequestStatistics } from 'src/plugins/inspector/common';
 import { SavedObject } from 'kibana/server';
 import { SavedObject as SavedObject_2 } from 'src/core/server';
 import { SavedObjectsClientContract } from 'src/core/server';
@@ -1008,6 +1007,10 @@ export interface ISearchOptions {
     isRestore?: boolean;
     isStored?: boolean;
     legacyHitsTotal?: boolean;
+    // Warning: (ae-forgotten-export) The symbol "RequestResponder" needs to be exported by the entry point index.d.ts
+    //
+    // (undocumented)
+    requestResponder?: RequestResponder;
     sessionId?: string;
     strategy?: string;
 }
@@ -1333,8 +1336,6 @@ export const search: {
         toAbsoluteDates: typeof toAbsoluteDates;
         calcAutoIntervalLessThan: typeof calcAutoIntervalLessThan;
     };
-    getRequestInspectorStats: typeof getRequestInspectorStats;
-    getResponseInspectorStats: typeof getResponseInspectorStats;
     tabifyAggResponse: typeof tabifyAggResponse;
     tabifyGetColumns: typeof tabifyGetColumns;
 };
@@ -1516,20 +1517,18 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
 // src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts
 // src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
 // src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:243:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:244:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:253:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:254:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:259:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:260:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:268:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts
 // src/plugins/data/server/plugin.ts:81:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts
 // src/plugins/data/server/search/types.ts:114:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts
 
diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js
index 3be047859d3b0..45382af098644 100644
--- a/src/plugins/discover/public/application/angular/discover.js
+++ b/src/plugins/discover/public/application/angular/discover.js
@@ -25,8 +25,6 @@ import { discoverResponseHandler } from './response_handler';
 import {
   getAngularModule,
   getHeaderActionMenuMounter,
-  getRequestInspectorStats,
-  getResponseInspectorStats,
   getServices,
   getUrlTracker,
   redirectWhenMissing,
@@ -153,7 +151,6 @@ function discoverController($route, $scope) {
   const subscriptions = new Subscription();
   const refetch$ = new Subject();
 
-  let inspectorRequest;
   let isChangingIndexPattern = false;
   const savedSearch = $route.current.locals.savedObjects.savedSearch;
   const persistentSearchSource = savedSearch.searchSource;
@@ -417,12 +414,14 @@ function discoverController($route, $scope) {
 
     $scope.fetchStatus = fetchStatuses.LOADING;
     $scope.resultState = getResultState($scope.fetchStatus, $scope.rows);
-    logInspectorRequest({ searchSessionId });
+
     return $scope.volatileSearchSource
-      .fetch({
+      .fetch$({
         abortSignal: abortController.signal,
         sessionId: searchSessionId,
+        requestResponder: getRequestResponder({ searchSessionId }),
       })
+      .toPromise()
       .then(onResults)
       .catch((error) => {
         // If the request was aborted then no need to surface this error in the UI
@@ -439,10 +438,6 @@ function discoverController($route, $scope) {
   };
 
   function onResults(resp) {
-    inspectorRequest
-      .stats(getResponseInspectorStats(resp, $scope.volatileSearchSource))
-      .ok({ json: resp });
-
     if (getTimeField() && !$scope.state.hideChart) {
       const tabifiedData = tabifyAggResponse($scope.opts.chartAggConfigs, resp);
       $scope.volatileSearchSource.rawResponse = resp;
@@ -463,7 +458,7 @@ function discoverController($route, $scope) {
     $scope.fetchStatus = fetchStatuses.COMPLETE;
   }
 
-  function logInspectorRequest({ searchSessionId = null } = { searchSessionId: null }) {
+  function getRequestResponder({ searchSessionId = null } = { searchSessionId: null }) {
     inspectorAdapters.requests.reset();
     const title = i18n.translate('discover.inspectorRequestDataTitle', {
       defaultMessage: 'data',
@@ -471,11 +466,7 @@ function discoverController($route, $scope) {
     const description = i18n.translate('discover.inspectorRequestDescription', {
       defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.',
     });
-    inspectorRequest = inspectorAdapters.requests.start(title, { description, searchSessionId });
-    inspectorRequest.stats(getRequestInspectorStats($scope.volatileSearchSource));
-    $scope.volatileSearchSource.getSearchRequestBody().then((body) => {
-      inspectorRequest.json(body);
-    });
+    return inspectorAdapters.requests.start(title, { description, searchSessionId });
   }
 
   $scope.resetQuery = function () {
diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
index e7349ed22355a..237da72ae3a52 100644
--- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts
+++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
@@ -29,13 +29,7 @@ import searchTemplateGrid from './search_template_datagrid.html';
 import { ISearchEmbeddable, SearchInput, SearchOutput } from './types';
 import { SortOrder } from '../angular/doc_table/components/table_header/helpers';
 import { getSortForSearchSource } from '../angular/doc_table';
-import {
-  getRequestInspectorStats,
-  getResponseInspectorStats,
-  getServices,
-  IndexPattern,
-  ISearchSource,
-} from '../../kibana_services';
+import { getServices, IndexPattern, ISearchSource } from '../../kibana_services';
 import { SEARCH_EMBEDDABLE_TYPE } from './constants';
 import { SavedSearch } from '../..';
 import {
@@ -330,14 +324,11 @@ export class SearchEmbeddable
       defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.',
     });
 
-    const inspectorRequest = this.inspectorAdapters.requests!.start(title, {
+    const requestResponder = this.inspectorAdapters.requests!.start(title, {
       description,
       searchSessionId,
     });
-    inspectorRequest.stats(getRequestInspectorStats(searchSource));
-    searchSource.getSearchRequestBody().then((body: Record<string, unknown>) => {
-      inspectorRequest.json(body);
-    });
+
     this.searchScope.$apply(() => {
       this.searchScope!.isLoading = true;
     });
@@ -345,15 +336,15 @@ export class SearchEmbeddable
 
     try {
       // Make the request
-      const resp = await searchSource.fetch({
-        abortSignal: this.abortController.signal,
-        sessionId: searchSessionId,
-      });
+      const resp = await searchSource
+        .fetch$({
+          abortSignal: this.abortController.signal,
+          sessionId: searchSessionId,
+          requestResponder,
+        })
+        .toPromise();
       this.updateOutput({ loading: false, error: undefined });
 
-      // Log response to inspector
-      inspectorRequest.stats(getResponseInspectorStats(resp, searchSource)).ok({ json: resp });
-
       // Apply the changes to the angular scope
       this.searchScope.$apply(() => {
         this.searchScope!.hits = resp.hits.hits;
diff --git a/src/plugins/discover/public/kibana_services.ts b/src/plugins/discover/public/kibana_services.ts
index 27bcc00234939..e4b0035ed0e03 100644
--- a/src/plugins/discover/public/kibana_services.ts
+++ b/src/plugins/discover/public/kibana_services.ts
@@ -88,7 +88,7 @@ export const [getScopedHistory, setScopedHistory] = createGetterSetter<ScopedHis
   'scopedHistory'
 );
 
-export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search;
+export const { tabifyAggResponse } = search;
 export { unhashUrl, redirectWhenMissing } from '../../kibana_utils/public';
 export { formatMsg, formatStack, subscribeWithScope } from '../../kibana_legacy/public';
 
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx
index 7910e931e60e6..d601e4fa9a39a 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx
@@ -436,7 +436,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingle
       null // needs to be stripped server-side
     );
 
-    const dsl = await searchSource.getSearchRequestBody();
+    const dsl = searchSource.getSearchRequestBody();
 
     const risonDsl = rison.encode(dsl);
 
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx
index ac3a15d2ac490..82d7b09e9c3f2 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx
@@ -706,7 +706,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
       searchSource.setField('sort', this._buildEsSort());
     }
 
-    const dsl = await searchSource.getSearchRequestBody();
+    const dsl = searchSource.getSearchRequestBody();
     const risonDsl = rison.encode(dsl);
 
     const mvtUrlServicePath = getHttp().basePath.prepend(
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts
index 222c49abfa16a..2915eaec8ac77 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts
@@ -19,7 +19,6 @@ import { createExtentFilter } from '../../../../common/elasticsearch_util';
 import { copyPersistentState } from '../../../reducers/copy_persistent_state';
 import { DataRequestAbortError } from '../../util/data_request';
 import { expandToTileBoundaries } from '../../../../common/geo_tile_utils';
-import { search } from '../../../../../../../src/plugins/data/public';
 import { IVectorSource } from '../vector_source';
 import { TimeRange } from '../../../../../../../src/plugins/data/common';
 import {
@@ -35,10 +34,7 @@ import { IVectorStyle } from '../../styles/vector/vector_style';
 import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
 import { IField } from '../../fields/field';
 import { FieldFormatter } from '../../../../common/constants';
-import {
-  Adapters,
-  RequestResponder,
-} from '../../../../../../../src/plugins/inspector/common/adapters';
+import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters';
 import { isValidStringConfig } from '../../util/valid_string_config';
 
 export function isSearchSourceAbortError(error: Error) {
@@ -171,40 +167,23 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
     const abortController = new AbortController();
     registerCancelCallback(() => abortController.abort());
 
-    const inspectorAdapters = this.getInspectorAdapters();
-    let inspectorRequest: RequestResponder | undefined;
-    if (inspectorAdapters?.requests) {
-      inspectorRequest = inspectorAdapters.requests.start(requestName, {
-        id: requestId,
-        description: requestDescription,
-        searchSessionId,
-      });
-    }
+    const requestResponder = this.getInspectorAdapters()?.requests?.start(requestName, {
+      id: requestId,
+      description: requestDescription,
+      searchSessionId,
+    });
 
     let resp;
     try {
-      if (inspectorRequest) {
-        const requestStats = search.getRequestInspectorStats(searchSource);
-        inspectorRequest.stats(requestStats);
-        searchSource.getSearchRequestBody().then((body) => {
-          if (inspectorRequest) {
-            inspectorRequest.json(body);
-          }
-        });
-      }
-      resp = await searchSource.fetch({
-        abortSignal: abortController.signal,
-        sessionId: searchSessionId,
-        legacyHitsTotal: false,
-      });
-      if (inspectorRequest) {
-        const responseStats = search.getResponseInspectorStats(resp, searchSource);
-        inspectorRequest.stats(responseStats).ok({ json: resp });
-      }
+      resp = await searchSource
+        .fetch$({
+          abortSignal: abortController.signal,
+          sessionId: searchSessionId,
+          legacyHitsTotal: false,
+          requestResponder,
+        })
+        .toPromise();
     } catch (error) {
-      if (inspectorRequest) {
-        inspectorRequest.error(error);
-      }
       if (isSearchSourceAbortError(error)) {
         throw new DataRequestAbortError();
       }
diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts
index 85c5379a63b7f..01959ed08036d 100644
--- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts
+++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts
@@ -79,7 +79,7 @@ export class CsvGenerator {
     searchSource: ISearchSource,
     scrollSettings: CsvExportSettings['scroll']
   ) {
-    const searchBody = await searchSource.getSearchRequestBody();
+    const searchBody = searchSource.getSearchRequestBody();
     this.logger.debug(`executing search request`);
     const searchParams = {
       params: {