diff --git a/README.md b/README.md
index a1dc8143d547b90f528336ea21f8e2967cf6a9a8..2126281ae58f8e4e44c888a768d572c9834850fc 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,16 @@ This project tries to make CELCAT usable by:
 - **TODO**: Providing a way to sync ics with a CalDAV server, to enable all calendar clients to synchronize from it
 
 ### Usage
-`fetch-ics <COURSE-REQUESTS-FILE.csv> [-o <OUTPUT-FILE.ics>]`
+```
+Usage: fetch-celcat [OPTIONS] COURSE_REQUEST_FILE
+
+Options:
+  --json, --json-raw TEXT  If set, raw CELCAT events are written as JSON to this file.
+  --csv-raw TEXT           If set, raw (unfiltered) events are written as CSV to this file.
+  --csv TEXT               If set, filteret events are written as CSV to this file.
+  --ics TEXT               If set, filtered events are written as ICS to this file.
+  --help                   Show this message and exit.
+```
 
 The course request input file defines which courses you are interested in.
 For example, if you give the following courses:
diff --git a/flake.nix b/flake.nix
index 5a44b9dbdcbbead3234937bf3a9a578ddc663549..b7f62eea3e2f8c96f8478683758fe5706c1a3c86 100644
--- a/flake.nix
+++ b/flake.nix
@@ -9,7 +9,7 @@
       let pkgs = nixpkgs.legacyPackages.${system};
       in rec {
         packages = import ./default.nix { inherit pkgs; };
-        apps.fetch-ics = flake-utils.lib.mkApp { drv = packages.lflex_celcat_survival; exePath = "/bin/fetch-ics"; };
+        apps.fetch-celcat = flake-utils.lib.mkApp { drv = packages.lflex_celcat_survival; exePath = "/bin/fetch-celcat"; };
         defaultPackage = packages.lflex_celcat_survival;
       }
     );
diff --git a/lflex_celcat_survival/cmd/fetch_celcat.py b/lflex_celcat_survival/cmd/fetch_celcat.py
new file mode 100755
index 0000000000000000000000000000000000000000..49898ef42501488f3eb489f6d2932862f3ca4007
--- /dev/null
+++ b/lflex_celcat_survival/cmd/fetch_celcat.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+import click
+import logging
+import lflex_celcat_survival as lcs
+
+@click.command()
+@click.argument('course_request_file')
+@click.option('--json', '--json-raw', default=None, help='If set, raw CELCAT events are written as JSON to this file.')
+@click.option('--csv-raw', default=None, help='If set, raw (unfiltered) events are written as CSV to this file.')
+@click.option('--csv', default=None, help='If set, filteret events are written as CSV to this file.')
+@click.option('--ics', default=None, help='If set, filtered events are written as ICS to this file.')
+def main(course_request_file, json, csv_raw, csv, ics):
+    logging.basicConfig(level=logging.INFO)
+
+    req = lcs.course_request.CourseRequest(course_request_file)
+    if all(o is None for o in [json, csv_raw, csv, ics]):
+        logging.warning('No option set, doing nothing.')
+        return
+
+    celcat_raw_response = req.do_request()
+    if json is not None:
+        with open(json, 'w') as f:
+            f.write(celcat_raw_response)
+    if all(o is None for o in [csv_raw, csv, ics]):
+        return
+
+    celcat_events = lcs.events.CelcatEvents(celcat_raw_response)
+    if csv_raw is not None:
+        celcat_events.df.to_csv(csv_raw, index=False)
+    if all(o is None for o in [csv, ics]):
+        return
+
+    filtered_celcat_events = lcs.events.FilteredCelcatEvents(req, celcat_events)
+    filtered_celcat_events.check_expected_nb_timeslots()
+    if csv is not None:
+        filtered_celcat_events.df.to_csv(csv, index=False)
+    if all(o is None for o in [ics]):
+        return
+
+    calendar = lcs.ics.course_df_to_ics(filtered_celcat_events.df)
+    if ics is not None:
+        with open(ics, 'w') as f:
+            f.write(str(calendar))
+
+if __name__ == "__main__":
+    main()
diff --git a/lflex_celcat_survival/cmd/fetch_ics.py b/lflex_celcat_survival/cmd/fetch_ics.py
deleted file mode 100755
index 2fc03e03a1b9a41e3d62c2dbcce17a33c5286cf5..0000000000000000000000000000000000000000
--- a/lflex_celcat_survival/cmd/fetch_ics.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env python3
-import click
-import logging
-import lflex_celcat_survival as lcs
-
-@click.command()
-@click.argument('course_request_file')
-@click.option('--output-file', '-o', default=None, help='Where to write the generated ICS (stdout if unset).')
-def main(course_request_file, output_file):
-    logging.basicConfig(level=logging.INFO)
-    req = lcs.course_request.CourseRequest(course_request_file)
-    celcat_raw_response = req.do_request()
-
-    celcat_events = lcs.events.CelcatEvents(celcat_raw_response)
-    filtered_celcat_events = lcs.events.FilteredCelcatEvents(req, celcat_events)
-    filtered_celcat_events.check_expected_nb_timeslots()
-
-    calendar = lcs.ics.course_df_to_ics(filtered_celcat_events.df)
-
-    if output_file is None:
-        print(calendar)
-    else:
-        with open(output_file, 'w') as f:
-            f.write(str(calendar))
-
-if __name__ == "__main__":
-    main()
diff --git a/pyproject.toml b/pyproject.toml
index 77e0127c8e109d6c17becc8a18c225b86458a99d..b88149d46ba393bc9b4e5d3393842dd980df0cb5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,4 +29,4 @@ dependencies = [
 ]
 
 [project.scripts]
-fetch-ics = "lflex_celcat_survival.cmd.fetch_ics:main"
+fetch-celcat = "lflex_celcat_survival.cmd.fetch_celcat:main"