diff --git a/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
index b055626934eea..6a549ac6f8e0a 100644
--- a/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
@@ -17,14 +17,16 @@
  * under the License.
  */
 
-import { IndexPatternsContract } from 'src/plugins/data/public';
-import { SavedObjectsClientContract } from 'kibana/public';
+import type { IndexPatternsContract, ISearchStart } from 'src/plugins/data/public';
+import type { SavedObjectsClientContract } from 'kibana/public';
 import { createGetterSetter } from '../../../kibana_utils/public';
 
 export const [getIndexPatterns, setIndexPatterns] = createGetterSetter<IndexPatternsContract>(
   'IndexPatterns'
 );
 
+export const [getDataSearch, setDataSearch] = createGetterSetter<ISearchStart>('Search');
+
 export const [getSavedObjectsClient, setSavedObjectsClient] = createGetterSetter<
   SavedObjectsClientContract
 >('SavedObjectsClient');
diff --git a/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
index 975d12a152d89..1f0ac8b2b9392 100644
--- a/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
@@ -23,6 +23,7 @@ import { TimeRange, Filter, esQuery, Query } from '../../../data/public';
 import { TimelionVisDependencies } from '../plugin';
 import { getTimezone } from './get_timezone';
 import { TimelionVisParams } from '../timelion_vis_fn';
+import { getDataSearch } from '../helpers/plugin_services';
 
 interface Stats {
   cacheCount: number;
@@ -93,6 +94,7 @@ export function getTimelionRequestHandler({
 
     // parse the time range client side to make sure it behaves like other charts
     const timeRangeBounds = timefilter.calculateBounds(timeRange);
+    const sessionId = getDataSearch().session.getSessionId();
 
     try {
       return await http.post('/api/timelion/run', {
@@ -109,6 +111,7 @@ export function getTimelionRequestHandler({
             interval: visParams.interval,
             timezone,
           },
+          sessionId,
         }),
       });
     } catch (e) {
diff --git a/src/plugins/vis_type_timelion/public/plugin.ts b/src/plugins/vis_type_timelion/public/plugin.ts
index bb8fb6b298a07..d74c127dce881 100644
--- a/src/plugins/vis_type_timelion/public/plugin.ts
+++ b/src/plugins/vis_type_timelion/public/plugin.ts
@@ -36,7 +36,7 @@ import { VisualizationsSetup } from '../../visualizations/public';
 
 import { getTimelionVisualizationConfig } from './timelion_vis_fn';
 import { getTimelionVisDefinition } from './timelion_vis_type';
-import { setIndexPatterns, setSavedObjectsClient } from './helpers/plugin_services';
+import { setIndexPatterns, setSavedObjectsClient, setDataSearch } from './helpers/plugin_services';
 import { ConfigSchema } from '../config';
 
 import { getArgValueSuggestions } from './helpers/arg_value_suggestions';
@@ -104,6 +104,7 @@ export class TimelionVisPlugin
   public start(core: CoreStart, plugins: TimelionVisStartDependencies) {
     setIndexPatterns(plugins.data.indexPatterns);
     setSavedObjectsClient(core.savedObjects.client);
+    setDataSearch(plugins.data.search);
 
     return {
       getArgValueSuggestions,
diff --git a/src/plugins/vis_type_timelion/server/routes/run.ts b/src/plugins/vis_type_timelion/server/routes/run.ts
index 19bb5238f9de0..5766705d9873d 100644
--- a/src/plugins/vis_type_timelion/server/routes/run.ts
+++ b/src/plugins/vis_type_timelion/server/routes/run.ts
@@ -75,6 +75,7 @@ export function runRoute(
               to: schema.maybe(schema.string()),
             })
           ),
+          sessionId: schema.maybe(schema.string()),
         }),
       },
     },
diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
index 8be3cf5171c65..e10b3f7e438db 100644
--- a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
+++ b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
@@ -26,36 +26,39 @@ import createDateAgg from './lib/create_date_agg';
 import esResponse from '../fixtures/es_response';
 
 import _ from 'lodash';
-import { expect } from 'chai';
 import sinon from 'sinon';
 import invoke from '../helpers/invoke_series_fn.js';
 import { UI_SETTINGS } from '../../../../data/server';
 
-function stubRequestAndServer(response, indexPatternSavedObjects = []) {
-  return {
-    getStartServices: sinon
-      .stub()
-      .returns(
-        Promise.resolve([
-          {},
-          { data: { search: { search: () => from(Promise.resolve(response)) } } },
-        ])
-      ),
-    savedObjectsClient: {
-      find: function () {
-        return Promise.resolve({
-          saved_objects: indexPatternSavedObjects,
-        });
-      },
-    },
-  };
-}
-
 describe('es', () => {
   let tlConfig;
+  let dataSearchStub;
+  let mockResponse;
+
+  beforeEach(() => {
+    dataSearchStub = {
+      data: {
+        search: { search: jest.fn(() => from(Promise.resolve(mockResponse))) },
+      },
+    };
+  });
+
+  function stubRequestAndServer(response, indexPatternSavedObjects = []) {
+    mockResponse = response;
+    return {
+      getStartServices: sinon.stub().returns(Promise.resolve([{}, dataSearchStub])),
+      savedObjectsClient: {
+        find: function () {
+          return Promise.resolve({
+            saved_objects: indexPatternSavedObjects,
+          });
+        },
+      },
+    };
+  }
 
   describe('seriesList processor', () => {
-    it('throws an error then the index is missing', () => {
+    test('throws an error then the index is missing', () => {
       tlConfig = stubRequestAndServer({
         rawResponse: {
           _shards: { total: 0 },
@@ -64,14 +67,29 @@ describe('es', () => {
       return invoke(es, [5], tlConfig)
         .then(expect.fail)
         .catch((e) => {
-          expect(e).to.be.an('error');
+          expect(e instanceof Error).toBeTruthy();
         });
     });
 
-    it('returns a seriesList', () => {
+    test('should call data search with sessionId', async () => {
+      tlConfig = {
+        ...stubRequestAndServer({ rawResponse: esResponse }),
+        request: {
+          body: {
+            sessionId: 1,
+          },
+        },
+      };
+
+      await invoke(es, [5], tlConfig);
+
+      expect(dataSearchStub.data.search.search.mock.calls[0][1]).toHaveProperty('sessionId', 1);
+    });
+
+    test('returns a seriesList', () => {
       tlConfig = stubRequestAndServer({ rawResponse: esResponse });
       return invoke(es, [5], tlConfig).then((r) => {
-        expect(r.output.type).to.eql('seriesList');
+        expect(r.output.type).toEqual('seriesList');
       });
     });
   });
@@ -89,51 +107,51 @@ describe('es', () => {
       agg = createDateAgg(config, tlConfig);
     });
 
-    it('creates a date_histogram with meta.type of time_buckets', () => {
-      expect(agg.time_buckets.meta.type).to.eql('time_buckets');
-      expect(agg.time_buckets.date_histogram).to.be.an('object');
+    test('creates a date_histogram with meta.type of time_buckets', () => {
+      expect(agg.time_buckets.meta.type).toEqual('time_buckets');
+      expect(typeof agg.time_buckets.date_histogram).toBe('object');
     });
 
-    it('has extended_bounds that match tlConfig', () => {
-      expect(agg.time_buckets.date_histogram.extended_bounds.min).to.equal(tlConfig.time.from);
-      expect(agg.time_buckets.date_histogram.extended_bounds.max).to.equal(tlConfig.time.to);
+    test('has extended_bounds that match tlConfig', () => {
+      expect(agg.time_buckets.date_histogram.extended_bounds.min).toEqual(tlConfig.time.from);
+      expect(agg.time_buckets.date_histogram.extended_bounds.max).toEqual(tlConfig.time.to);
     });
 
-    it('sets the timezone', () => {
-      expect(agg.time_buckets.date_histogram.time_zone).to.equal('Etc/UTC');
+    test('sets the timezone', () => {
+      expect(agg.time_buckets.date_histogram.time_zone).toEqual('Etc/UTC');
     });
 
-    it('sets the field', () => {
-      expect(agg.time_buckets.date_histogram.field).to.equal('@timestamp');
+    test('sets the field', () => {
+      expect(agg.time_buckets.date_histogram.field).toEqual('@timestamp');
     });
 
-    it('sets the interval for calendar_interval correctly', () => {
-      expect(agg.time_buckets.date_histogram).to.have.property('calendar_interval', '1y');
+    test('sets the interval for calendar_interval correctly', () => {
+      expect(agg.time_buckets.date_histogram).toHaveProperty('calendar_interval', '1y');
     });
 
-    it('sets the interval for fixed_interval correctly', () => {
+    test('sets the interval for fixed_interval correctly', () => {
       const a = createDateAgg({ timefield: '@timestamp', interval: '24h' }, tlConfig);
-      expect(a.time_buckets.date_histogram).to.have.property('fixed_interval', '24h');
+      expect(a.time_buckets.date_histogram).toHaveProperty('fixed_interval', '24h');
     });
 
-    it('sets min_doc_count to 0', () => {
-      expect(agg.time_buckets.date_histogram.min_doc_count).to.equal(0);
+    test('sets min_doc_count to 0', () => {
+      expect(agg.time_buckets.date_histogram.min_doc_count).toEqual(0);
     });
 
     describe('metric aggs', () => {
       const emptyScriptedFields = [];
 
-      it('adds a metric agg for each metric', () => {
+      test('adds a metric agg for each metric', () => {
         config.metric = ['sum:beer', 'avg:bytes', 'percentiles:bytes'];
         agg = createDateAgg(config, tlConfig, emptyScriptedFields);
-        expect(agg.time_buckets.aggs['sum(beer)']).to.eql({ sum: { field: 'beer' } });
-        expect(agg.time_buckets.aggs['avg(bytes)']).to.eql({ avg: { field: 'bytes' } });
-        expect(agg.time_buckets.aggs['percentiles(bytes)']).to.eql({
+        expect(agg.time_buckets.aggs['sum(beer)']).toEqual({ sum: { field: 'beer' } });
+        expect(agg.time_buckets.aggs['avg(bytes)']).toEqual({ avg: { field: 'bytes' } });
+        expect(agg.time_buckets.aggs['percentiles(bytes)']).toEqual({
           percentiles: { field: 'bytes' },
         });
       });
 
-      it('adds a scripted metric agg for each scripted metric', () => {
+      test('adds a scripted metric agg for each scripted metric', () => {
         config.metric = ['avg:scriptedBytes'];
         const scriptedFields = [
           {
@@ -143,7 +161,7 @@ describe('es', () => {
           },
         ];
         agg = createDateAgg(config, tlConfig, scriptedFields);
-        expect(agg.time_buckets.aggs['avg(scriptedBytes)']).to.eql({
+        expect(agg.time_buckets.aggs['avg(scriptedBytes)']).toEqual({
           avg: {
             script: {
               source: 'doc["bytes"].value',
@@ -153,11 +171,11 @@ describe('es', () => {
         });
       });
 
-      it('has a special `count` metric that uses a script', () => {
+      test('has a special `count` metric that uses a script', () => {
         config.metric = ['count'];
         agg = createDateAgg(config, tlConfig, emptyScriptedFields);
-        expect(agg.time_buckets.aggs.count.bucket_script).to.be.an('object');
-        expect(agg.time_buckets.aggs.count.bucket_script.buckets_path).to.eql('_count');
+        expect(typeof agg.time_buckets.aggs.count.bucket_script).toBe('object');
+        expect(agg.time_buckets.aggs.count.bucket_script.buckets_path).toEqual('_count');
       });
     });
   });
@@ -176,43 +194,43 @@ describe('es', () => {
       };
     });
 
-    it('sets the index on the request', () => {
+    test('sets the index on the request', () => {
       config.index = 'beer';
       const request = fn(config, tlConfig, emptyScriptedFields);
 
-      expect(request.params.index).to.equal('beer');
+      expect(request.params.index).toEqual('beer');
     });
 
-    it('always sets body.size to 0', () => {
+    test('always sets body.size to 0', () => {
       const request = fn(config, tlConfig, emptyScriptedFields);
 
-      expect(request.params.body.size).to.equal(0);
+      expect(request.params.body.size).toEqual(0);
     });
 
-    it('creates a filters agg that contains each of the queries passed', () => {
+    test('creates a filters agg that contains each of the queries passed', () => {
       config.q = ['foo', 'bar'];
       const request = fn(config, tlConfig, emptyScriptedFields);
 
-      expect(request.params.body.aggs.q.meta.type).to.equal('split');
+      expect(request.params.body.aggs.q.meta.type).toEqual('split');
 
       const filters = request.params.body.aggs.q.filters.filters;
-      expect(filters.foo.query_string.query).to.eql('foo');
-      expect(filters.bar.query_string.query).to.eql('bar');
+      expect(filters.foo.query_string.query).toEqual('foo');
+      expect(filters.bar.query_string.query).toEqual('bar');
     });
 
     describe('timeouts', () => {
-      it('sets the timeout on the request', () => {
+      test('sets the timeout on the request', () => {
         config.index = 'beer';
         const request = fn(config, tlConfig, emptyScriptedFields, 30000);
 
-        expect(request.params.timeout).to.equal('30000ms');
+        expect(request.params.timeout).toEqual('30000ms');
       });
 
-      it('sets no timeout if elasticsearch.shardTimeout is set to 0', () => {
+      test('sets no timeout if elasticsearch.shardTimeout is set to 0', () => {
         config.index = 'beer';
         const request = fn(config, tlConfig, emptyScriptedFields, 0);
 
-        expect(request.params).to.not.have.property('timeout');
+        expect(request.params).not.toHaveProperty('timeout');
       });
     });
 
@@ -227,20 +245,20 @@ describe('es', () => {
         sandbox.restore();
       });
 
-      it('sets ignore_throttled=true on the request', () => {
+      test('sets ignore_throttled=true on the request', () => {
         config.index = 'beer';
         tlConfig.settings[UI_SETTINGS.SEARCH_INCLUDE_FROZEN] = false;
         const request = fn(config, tlConfig, emptyScriptedFields);
 
-        expect(request.params.ignore_throttled).to.equal(true);
+        expect(request.params.ignore_throttled).toEqual(true);
       });
 
-      it('sets no timeout if elasticsearch.shardTimeout is set to 0', () => {
+      test('sets no timeout if elasticsearch.shardTimeout is set to 0', () => {
         tlConfig.settings[UI_SETTINGS.SEARCH_INCLUDE_FROZEN] = true;
         config.index = 'beer';
         const request = fn(config, tlConfig, emptyScriptedFields);
 
-        expect(request.params.ignore_throttled).to.equal(false);
+        expect(request.params.ignore_throttled).toEqual(false);
       });
     });
 
@@ -271,24 +289,24 @@ describe('es', () => {
         });
       });
 
-      it('adds the contents of body.extended.es.filter to a filter clause of the bool', () => {
+      test('adds the contents of body.extended.es.filter to a filter clause of the bool', () => {
         config.kibana = true;
         const request = fn(config, tlConfig, emptyScriptedFields);
         const filter = request.params.body.query.bool.filter.bool;
-        expect(filter.must.length).to.eql(1);
-        expect(filter.must_not.length).to.eql(2);
+        expect(filter.must.length).toEqual(1);
+        expect(filter.must_not.length).toEqual(2);
       });
 
-      it('does not include filters if config.kibana = false', () => {
+      test('does not include filters if config.kibana = false', () => {
         config.kibana = false;
         const request = fn(config, tlConfig, emptyScriptedFields);
-        expect(request.params.body.query.bool.filter).to.eql(undefined);
+        expect(request.params.body.query.bool.filter).toEqual(undefined);
       });
 
-      it('adds a time filter to the bool querys must clause', () => {
+      test('adds a time filter to the bool querys must clause', () => {
         let request = fn(config, tlConfig, emptyScriptedFields);
-        expect(request.params.body.query.bool.must.length).to.eql(1);
-        expect(request.params.body.query.bool.must[0]).to.eql({
+        expect(request.params.body.query.bool.must.length).toEqual(1);
+        expect(request.params.body.query.bool.must[0]).toEqual({
           range: {
             '@timestamp': {
               format: 'strict_date_optional_time',
@@ -300,27 +318,27 @@ describe('es', () => {
 
         config.kibana = true;
         request = fn(config, tlConfig, emptyScriptedFields);
-        expect(request.params.body.query.bool.must.length).to.eql(1);
+        expect(request.params.body.query.bool.must.length).toEqual(1);
       });
     });
 
     describe('config.split', () => {
-      it('adds terms aggs, in order, under the filters agg', () => {
+      test('adds terms aggs, in order, under the filters agg', () => {
         config.split = ['beer:5', 'wine:10'];
         const request = fn(config, tlConfig, emptyScriptedFields);
 
         const aggs = request.params.body.aggs.q.aggs;
 
-        expect(aggs.beer.meta.type).to.eql('split');
-        expect(aggs.beer.terms.field).to.eql('beer');
-        expect(aggs.beer.terms.size).to.eql(5);
+        expect(aggs.beer.meta.type).toEqual('split');
+        expect(aggs.beer.terms.field).toEqual('beer');
+        expect(aggs.beer.terms.size).toEqual(5);
 
-        expect(aggs.beer.aggs.wine.meta.type).to.eql('split');
-        expect(aggs.beer.aggs.wine.terms.field).to.eql('wine');
-        expect(aggs.beer.aggs.wine.terms.size).to.eql(10);
+        expect(aggs.beer.aggs.wine.meta.type).toEqual('split');
+        expect(aggs.beer.aggs.wine.terms.field).toEqual('wine');
+        expect(aggs.beer.aggs.wine.terms.size).toEqual(10);
       });
 
-      it('adds scripted terms aggs, in order, under the filters agg', () => {
+      test('adds scripted terms aggs, in order, under the filters agg', () => {
         config.split = ['scriptedBeer:5', 'scriptedWine:10'];
         const scriptedFields = [
           {
@@ -338,19 +356,19 @@ describe('es', () => {
 
         const aggs = request.params.body.aggs.q.aggs;
 
-        expect(aggs.scriptedBeer.meta.type).to.eql('split');
-        expect(aggs.scriptedBeer.terms.script).to.eql({
+        expect(aggs.scriptedBeer.meta.type).toEqual('split');
+        expect(aggs.scriptedBeer.terms.script).toEqual({
           source: 'doc["beer"].value',
           lang: 'painless',
         });
-        expect(aggs.scriptedBeer.terms.size).to.eql(5);
+        expect(aggs.scriptedBeer.terms.size).toEqual(5);
 
-        expect(aggs.scriptedBeer.aggs.scriptedWine.meta.type).to.eql('split');
-        expect(aggs.scriptedBeer.aggs.scriptedWine.terms.script).to.eql({
+        expect(aggs.scriptedBeer.aggs.scriptedWine.meta.type).toEqual('split');
+        expect(aggs.scriptedBeer.aggs.scriptedWine.terms.script).toEqual({
           source: 'doc["wine"].value',
           lang: 'painless',
         });
-        expect(aggs.scriptedBeer.aggs.scriptedWine.terms.size).to.eql(10);
+        expect(aggs.scriptedBeer.aggs.scriptedWine.terms.size).toEqual(10);
       });
     });
   });
@@ -364,14 +382,14 @@ describe('es', () => {
     describe('timeBucketsToPairs', () => {
       const fn = aggResponse.timeBucketsToPairs;
 
-      it('Should convert a single metric agg', () => {
+      test('Should convert a single metric agg', () => {
         const buckets = [
           { key: 1000, count: { value: 3 } },
           { key: 2000, count: { value: 14 } },
           { key: 3000, count: { value: 15 } },
         ];
 
-        expect(fn(buckets)).to.eql({
+        expect(fn(buckets)).toEqual({
           count: [
             [1000, 3],
             [2000, 14],
@@ -380,14 +398,14 @@ describe('es', () => {
         });
       });
 
-      it('Should convert multiple metric aggs', () => {
+      test('Should convert multiple metric aggs', () => {
         const buckets = [
           { key: 1000, count: { value: 3 }, max: { value: 92 } },
           { key: 2000, count: { value: 14 }, max: { value: 65 } },
           { key: 3000, count: { value: 15 }, max: { value: 35 } },
         ];
 
-        expect(fn(buckets)).to.eql({
+        expect(fn(buckets)).toEqual({
           count: [
             [1000, 3],
             [2000, 14],
@@ -401,7 +419,7 @@ describe('es', () => {
         });
       });
 
-      it('Should convert percentiles metric aggs', () => {
+      test('Should convert percentiles metric aggs', () => {
         const buckets = [
           {
             key: 1000,
@@ -417,7 +435,7 @@ describe('es', () => {
           },
         ];
 
-        expect(fn(buckets)).to.eql({
+        expect(fn(buckets)).toEqual({
           'percentiles:50.0': [
             [1000, NaN],
             [2000, 25],
@@ -442,8 +460,8 @@ describe('es', () => {
       });
     });
 
-    it('should throw an error', () => {
-      expect(aggResponse.default(esResponse.aggregations, config)).to.eql([
+    test('should throw an error', () => {
+      expect(aggResponse.default(esResponse.aggregations, config)).toEqual([
         {
           data: [
             [1000, 264],
diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/index.js b/src/plugins/vis_type_timelion/server/series_functions/es/index.js
index fc3250f0d4726..75ca1d74f0bf8 100644
--- a/src/plugins/vis_type_timelion/server/series_functions/es/index.js
+++ b/src/plugins/vis_type_timelion/server/series_functions/es/index.js
@@ -129,7 +129,6 @@ export default new Datasource('es', {
     const esShardTimeout = tlConfig.esShardTimeout;
 
     const body = buildRequest(config, tlConfig, scriptedFields, esShardTimeout);
-
     const deps = (await tlConfig.getStartServices())[1];
 
     const resp = await deps.data.search
@@ -137,6 +136,7 @@ export default new Datasource('es', {
         body,
         {
           strategy: ES_SEARCH_STRATEGY,
+          sessionId: tlConfig.request?.body.sessionId,
         },
         tlConfig.context
       )