--- a
+++ b/qiita_db/handlers/analysis.py
@@ -0,0 +1,94 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2014--, The Qiita Development Team.
+#
+# Distributed under the terms of the BSD 3-clause License.
+#
+# The full license is in the file LICENSE, distributed with this software.
+# -----------------------------------------------------------------------------
+
+from tornado import gen
+from tornado.web import HTTPError
+from json import dumps
+
+import qiita_db as qdb
+from .oauth2 import OauthBaseHandler, authenticate_oauth
+
+
+def _get_analysis(a_id):
+    """Returns the analysis with the given `a_id` if it exists
+
+    Parameters
+    ----------
+    a_id : str
+        The analysis id
+
+    Returns
+    -------
+    qiita_db.analysis.Analysis
+        The requested analysis
+
+    Raises
+    ------
+    HTTPError
+        If the analysis does not exist, with error code 404
+        If there is a problem instantiating the analysis, with error code 500
+    """
+    try:
+        a_id = int(a_id)
+        a = qdb.analysis.Analysis(a_id)
+    except qdb.exceptions.QiitaDBUnknownIDError:
+        raise HTTPError(404)
+    except Exception as e:
+        raise HTTPError(500, reason='Error instantiating analysis %s: %s'
+                        % (a_id, str(e)))
+    return a
+
+
+class APIAnalysisMetadataHandler(OauthBaseHandler):
+    @authenticate_oauth
+    async def get(self, analysis_id):
+        """Retrieves the analysis metadata
+
+        Parameters
+        ----------
+        analysis_id : str
+            The id of the analysis whose information is being retrieved
+
+        Returns
+        -------
+        dict
+            The contents of the analysis keyed by sample id
+
+        Notes
+        -----
+        This response needed to be broken in chunks because we were hitting
+        the max size of a respose: 2G; based on: https://bit.ly/3CPvyjd
+        """
+        chunk_len = 1024 * 1024 * 1  # 1 MiB
+
+        response = None
+        with qdb.sql_connection.TRN:
+            a = _get_analysis(analysis_id)
+            mf_fp = qdb.util.get_filepath_information(
+                a.mapping_file)['fullpath']
+            if mf_fp is not None:
+                df = qdb.metadata_template.util.load_template_to_dataframe(
+                    mf_fp, index='#SampleID')
+                response = dumps(df.to_dict(orient='index'))
+
+        if response is not None:
+            crange = range(chunk_len, len(response)+chunk_len, chunk_len)
+            for i, (win) in enumerate(crange):
+                # sending the chunk and flushing
+                chunk = response[i*chunk_len:win]
+                self.write(chunk)
+                await self.flush()
+
+                # cleaning chuck and pause the coroutine so other handlers
+                # can run, note that this is required/important based on the
+                # original implementation in https://bit.ly/3CPvyjd
+                del chunk
+                await gen.sleep(0.000000001)  # 1 nanosecond
+
+        else:
+            self.write(None)