diff --git a/docs/development/core/server/kibana-plugin-server.callapioptions.md b/docs/development/core/server/kibana-plugin-server.callapioptions.md
index c4955929c7519..ffdf638b236bb 100644
--- a/docs/development/core/server/kibana-plugin-server.callapioptions.md
+++ b/docs/development/core/server/kibana-plugin-server.callapioptions.md
@@ -16,5 +16,6 @@ export interface CallAPIOptions
 
 |  Property | Type | Description |
 |  --- | --- | --- |
+|  [signal](./kibana-plugin-server.callapioptions.signal.md) | <code>AbortSignal</code> | A signal object that allows you to abort the request via an AbortController object. |
 |  [wrap401Errors](./kibana-plugin-server.callapioptions.wrap401errors.md) | <code>boolean</code> | Indicates whether <code>401 Unauthorized</code> errors returned from the Elasticsearch API should be wrapped into <code>Boom</code> error instances with properly set <code>WWW-Authenticate</code> header that could have been returned by the API itself. If API didn't specify that then <code>Basic realm=&quot;Authorization Required&quot;</code> is used as <code>WWW-Authenticate</code>. |
 
diff --git a/docs/development/core/server/kibana-plugin-server.callapioptions.signal.md b/docs/development/core/server/kibana-plugin-server.callapioptions.signal.md
new file mode 100644
index 0000000000000..402ed0ca8e34c
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.callapioptions.signal.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [CallAPIOptions](./kibana-plugin-server.callapioptions.md) &gt; [signal](./kibana-plugin-server.callapioptions.signal.md)
+
+## CallAPIOptions.signal property
+
+A signal object that allows you to abort the request via an AbortController object.
+
+<b>Signature:</b>
+
+```typescript
+signal?: AbortSignal;
+```
diff --git a/src/core/server/elasticsearch/cluster_client.test.ts b/src/core/server/elasticsearch/cluster_client.test.ts
index 30a1ac7a038ed..de28818072bcf 100644
--- a/src/core/server/elasticsearch/cluster_client.test.ts
+++ b/src/core/server/elasticsearch/cluster_client.test.ts
@@ -165,6 +165,25 @@ describe('#callAsInternalUser', () => {
     ).rejects.toStrictEqual(mockAuthenticationError);
   });
 
+  test('aborts the request and rejects if a signal is provided and aborted', async () => {
+    const controller = new AbortController();
+
+    // The ES client returns a promise with an additional `abort` method to abort the request
+    const mockValue: any = Promise.resolve();
+    mockValue.abort = jest.fn();
+    mockEsClientInstance.ping.mockReturnValue(mockValue);
+
+    const promise = clusterClient.callAsInternalUser('ping', undefined, {
+      wrap401Errors: false,
+      signal: controller.signal,
+    });
+
+    controller.abort();
+
+    expect(mockValue.abort).toHaveBeenCalled();
+    await expect(promise).rejects.toThrowErrorMatchingInlineSnapshot(`"Request was aborted"`);
+  });
+
   test('does not override WWW-Authenticate if returned by Elasticsearch', async () => {
     const mockAuthenticationError = new (errors.AuthenticationException as any)(
       'Authentication Exception',
diff --git a/src/core/server/elasticsearch/cluster_client.ts b/src/core/server/elasticsearch/cluster_client.ts
index 917bb1b93e37f..45e3f0a20c0c4 100644
--- a/src/core/server/elasticsearch/cluster_client.ts
+++ b/src/core/server/elasticsearch/cluster_client.ts
@@ -42,6 +42,10 @@ export interface CallAPIOptions {
    * then `Basic realm="Authorization Required"` is used as `WWW-Authenticate`.
    */
   wrap401Errors: boolean;
+  /**
+   * A signal object that allows you to abort the request via an AbortController object.
+   */
+  signal?: AbortSignal;
 }
 
 /**
@@ -57,7 +61,7 @@ async function callAPI(
   endpoint: string,
   clientParams: Record<string, unknown> = {},
   options: CallAPIOptions = { wrap401Errors: true }
-) {
+): Promise<any> {
   const clientPath = endpoint.split('.');
   const api: any = get(client, clientPath);
   if (!api) {
@@ -66,7 +70,16 @@ async function callAPI(
 
   const apiContext = clientPath.length === 1 ? client : get(client, clientPath.slice(0, -1));
   try {
-    return await api.call(apiContext, clientParams);
+    return await new Promise((resolve, reject) => {
+      const request = api.call(apiContext, clientParams);
+      if (options.signal) {
+        options.signal.addEventListener('abort', () => {
+          request.abort();
+          reject(new Error('Request was aborted'));
+        });
+      }
+      return request.then(resolve, reject);
+    });
   } catch (err) {
     if (!options.wrap401Errors || err.statusCode !== 401) {
       throw err;
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 88ebe1e54bdce..43712d63abb53 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -43,6 +43,7 @@ export function bootstrap({ configs, cliArgs, applyConfigOverrides, features, }:
 
 // @public
 export interface CallAPIOptions {
+    signal?: AbortSignal;
     wrap401Errors: boolean;
 }
 
diff --git a/src/legacy/core_plugins/elasticsearch/index.d.ts b/src/legacy/core_plugins/elasticsearch/index.d.ts
index 9a524ab06e7bf..b26964e92f257 100644
--- a/src/legacy/core_plugins/elasticsearch/index.d.ts
+++ b/src/legacy/core_plugins/elasticsearch/index.d.ts
@@ -206,6 +206,7 @@ export interface DeprecationAPIResponse {
 
 export interface CallClusterOptions {
   wrap401Errors?: boolean;
+  signal?: AbortSignal;
 }
 
 export interface CallClusterWithRequest {