diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index b09fc6d52a9485468e5e8e97eededc004072eb9d..ce6a6427cff92ebc00f21e38aa309276dd377eaa 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -69,6 +69,7 @@ BUILD_EXT_REFERENCES_CUSTOM_RESOLVER=caosdb_default_person_reference
 BUILD_MODULE_EXT_EDITMODE_WYSIWYG_TEXT=DISABLED
 BUILD_MODULE_EXT_PROPERTY_DISPLAY=DISABLED
 
+# A button that allows to download query results as XLSX.
 BUILD_MODULE_EXT_EXPORT_TO_XLSX=ENABLED
 
 ### Put long text property values into a details tag. "DISABLED" means disabled.
diff --git a/src/core/js/ext_export_to_xlsx.js b/src/core/js/ext_export_to_xlsx.js
index 277d29609f8f56913210c2cc5bf2dde2247cba0d..14c07b2a79e84db80d955e99ad4e99f7ccb3ee96 100644
--- a/src/core/js/ext_export_to_xlsx.js
+++ b/src/core/js/ext_export_to_xlsx.js
@@ -1,9 +1,9 @@
 /*
  * This file is a part of the LinkAhead Project.
  *
- * Copyright (C) 2020,2021 IndiScale GmbH <info@indiscale.com>
- * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
- * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+ * Copyright (C) 2025 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2025 Henrik tom Wörden <h.tomwoerden@indiscale.com>
+ * Copyright (C) 2025 Daniel Hornung <d.hornung@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify it under
  * the terms of the GNU Affero General Public License as published by the Free
@@ -73,9 +73,9 @@ var ext_export_to_xlsx = function ($, logger) {
         const bookmarks_row = $("#caosdb-f-add-query-to-bookmarks-row");
         bookmarks_row.find("button").hide();
         bookmarks_row.find("p").show();
- 
-        const xls_result = await connection.runScript("xlsx.py",
-            {"-p0": { "query": query_string}});
+
+        const xls_result = await connection.runScript("ext_query_to_xlsx/query_to_xlsx.py",
+                                                      {"-Oquery": query_string});
 
         const code = xls_result.getElementsByTagName("script")[0].getAttribute("code");
         if (parseInt(code) > 0) {
diff --git a/src/server_side_scripting/ext_query_to_xlsx/query_to_xlsx.py b/src/server_side_scripting/ext_query_to_xlsx/query_to_xlsx.py
new file mode 100755
index 0000000000000000000000000000000000000000..6f1f8d1891927d0db8d7b54ce7f3e769a4043d33
--- /dev/null
+++ b/src/server_side_scripting/ext_query_to_xlsx/query_to_xlsx.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+
+# This file is a part of the LinkAhead project.
+#
+# Copyright (C) 2025 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2025 Daniel Hornung <d.hornung@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""Export query results as an xslx file.
+"""
+
+import argparse
+import os
+from pathlib import Path
+from tempfile import NamedTemporaryFile
+
+import linkahead as db
+from caosadvancedtools.table_json_conversion import export_import_xlsx
+
+
+def _parse_arguments():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('-a', '--auth-token', required=False,
+                        help=("An authentication token. If not provided LinkAhead's "
+                              "pylib will search for other methods of "
+                              "authentication if necessary."))
+    parser.add_argument('--query', help="The query for which the results shall be exported.",
+                        type=str)
+    return parser.parse_args()
+
+
+def main():
+    args = _parse_arguments()
+
+    if hasattr(args, "auth_token") and args.auth_token:
+        try:
+            db.configure_connection(auth_token=args.auth_token)
+        except db.LinkAheadConnectionError:
+            # Try good defaults
+            db.configure_connection(auth_token=args.auth_token,
+                                    url="https://localhost:10443",
+                                    ssl_insecure=True,
+                                    )
+
+    tempdir = os.environ["SHARED_DIR"]
+    outfile = NamedTemporaryFile(delete=False, suffix=".xlsx", dir=tempdir)
+    outfile_last_two = os.path.join(*(Path(outfile.name).parts[-2:]))  # Just the last two parts.
+
+    entities = db.execute_query(args.query, unique=False)
+    export_import_xlsx.export_container_to_xlsx(records=entities,
+                                                include_referenced_entities=True,
+                                                xlsx_data_filepath=outfile.name,
+                                                )
+
+    print(outfile_last_two)
+
+
+if __name__ == "__main__":
+    main()