diff --git a/.gitlab-ci.yaml b/.gitlab-ci.yaml
index 3184bf82418b97b2a98082787df277f5f11648a8..b159fd5d96b56a9c0a62759e479d96b2ab8eed26 100644
--- a/.gitlab-ci.yaml
+++ b/.gitlab-ci.yaml
@@ -8,6 +8,7 @@ stages:
 - deploy
 - e2e-test
 - post-deploy
+- reports
 
 variables:
   GIT_DEPTH: 1
@@ -274,3 +275,135 @@ tags_api_smoketest_negative:
     reports:
       junit: api_smoketest_tags_api_negative.xml
 
+.hivemind_reports: &common_api_reports
+  stage: reports
+  environment: hive-4.pl.syncad.com
+  needs:
+    - job: hivemind_stop_server
+      artifacts: true
+
+  variables:
+    GIT_STRATEGY: none
+
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+      when: always
+    - if: '$CI_PIPELINE_SOURCE == "push"'
+      when: manual
+    - when: on_success
+
+  tags:
+      - hivemind
+
+  allow_failure: true
+
+bridge_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_bridge.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_bridge.html
+
+bridge_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_bridge_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_bridge_negative.html
+
+condenser_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_condenser_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_condenser_api.html
+
+condenser_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_condenser_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_condenser_api_negative.html
+
+database_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_database_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_database_api.html
+
+database_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_database_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_database_api_negative.html
+
+follow_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_follow_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_follow_api.html
+
+follow_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_follow_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_follow_api_negative.html
+
+tags_api_smoketest_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_tags_api.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_tags_api.html
+
+tags_api_smoketest_negative_report:
+  <<: *common_api_reports
+
+  script:
+    - scripts/xml_report_parser.py api_smoketest_tags_api_negative.xml
+
+  artifacts:
+    when: always
+    paths:
+      - api_smoketest_tags_api_negative.html
+
diff --git a/scripts/xml_report_parser.py b/scripts/xml_report_parser.py
new file mode 100755
index 0000000000000000000000000000000000000000..5322bded497b77b08b0952b329412953c66978a1
--- /dev/null
+++ b/scripts/xml_report_parser.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python3
+
+import xml.dom.minidom
+import os
+from sys import exit
+
+TIME_TRESHOLD = 1.
+
+if __name__ == '__main__':
+    above_treshold = False
+    import argparse
+    parser = argparse.ArgumentParser()
+    parser.add_argument("xml_file", type = str, help = "Path to report file in xml format")
+    args = parser.parse_args()
+    html_file, _ = os.path.splitext(args.xml_file)
+    html_file += ".html"
+    with open(html_file, "w") as ofile:
+        ofile.write("<html>\n")
+        ofile.write("  <head>\n")
+        ofile.write("    <style>\n")
+        ofile.write("      table, th, td {\n")
+        ofile.write("        border: 1px solid black;\n")
+        ofile.write("        border-collapse: collapse;\n")
+        ofile.write("      }\n")
+        ofile.write("      th, td {\n")
+        ofile.write("        padding: 15px;\n")
+        ofile.write("      }\n")
+        ofile.write("    </style>\n")
+        ofile.write("  </head>\n")
+        ofile.write("  <body>\n")
+        ofile.write("    <table>\n")
+        ofile.write("      <tr><th>Test name</th><th>Time [s]</th></tr>\n")
+        document = xml.dom.minidom.parse(args.xml_file)
+        tests_collection = document.documentElement
+        for test in tests_collection.getElementsByTagName("testcase"):
+            if test.hasAttribute("name") and test.hasAttribute("time"):
+                if float(test.getAttribute("time")) > TIME_TRESHOLD:
+                    ofile.write("      <tr><td>{}</td><td bgcolor=\"red\">{}</td></tr>\n".format(test.getAttribute("name"), test.getAttribute("time")))
+                    above_treshold = True
+                else:
+                    ofile.write("      <tr><td>{}</td><td>{}</td></tr>\n".format(test.getAttribute("name"), test.getAttribute("time")))
+        ofile.write("    </table>\n")
+        ofile.write("  </body>\n")
+        ofile.write("</html>\n")
+    if above_treshold:
+        exit(1)
+    exit(0)