diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 94d5cb6e7f63888570cb3db31bfcf57467c8e650..4e4236f19cb708b07d3faa2c6a15e7238331907c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -14,6 +14,7 @@ Added
 
 * Support for configuring the Untis school ID
 * Add Ukrainian locale (contributed by Sergiy Gorichenko from Fre(i)e Software GmbH).
+* Import exams from Untis.
 
 Fixed
 ~~~~~
diff --git a/README.rst b/README.rst
index d1f6c454c66b982c298bb41668b42f11b308aff0..10164b7f4f2f85c31d268c9e71ca535e9f97528e 100644
--- a/README.rst
+++ b/README.rst
@@ -14,6 +14,7 @@ Features
 * Import breaks
 * Import classes
 * Import events
+* Import exams
 * Import exported Untis database via MySQL import
 * Import exported Untis XML files
 * Import holidays
diff --git a/aleksis/apps/untis/model_extensions.py b/aleksis/apps/untis/model_extensions.py
index 0baf5a886796a34fb989cc67065b15a10f1dbef2..ca4366eb7d25d234612b41ca2224e5ddbb09559f 100644
--- a/aleksis/apps/untis/model_extensions.py
+++ b/aleksis/apps/untis/model_extensions.py
@@ -74,3 +74,6 @@ chronos_models.Holiday.field(
 chronos_models.ExtraLesson.field(
     import_ref_untis=IntegerField(verbose_name=_("UNTIS import reference"), null=True, blank=True)
 )
+chronos_models.Exam.field(
+    import_ref_untis=IntegerField(verbose_name=_("UNTIS import reference"), null=True, blank=True)
+)
diff --git a/aleksis/apps/untis/util/mysql/importers/exams.py b/aleksis/apps/untis/util/mysql/importers/exams.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd9d788cb761383a7ab6b9f52af3b2f7b04cee89
--- /dev/null
+++ b/aleksis/apps/untis/util/mysql/importers/exams.py
@@ -0,0 +1,166 @@
+import logging
+
+from django.utils.translation import gettext as _
+
+from calendarweek import CalendarWeek
+from tqdm import tqdm
+
+from aleksis.apps.chronos import models as chronos_models
+from aleksis.apps.chronos.models import ExtraLesson, Lesson, ValidityRange
+
+from .... import models as mysql_models
+from ..util import (
+    TQDM_DEFAULTS,
+    connect_untis_fields,
+    date_to_untis_date,
+    get_first_period,
+    get_last_period,
+    move_weekday_to_range,
+    run_default_filter,
+    untis_date_to_date,
+    untis_split_second,
+)
+
+logger = logging.getLogger(__name__)
+
+
+def import_exams(
+    validity_range: ValidityRange,
+    time_periods_ref,
+    subjects_ref,
+    teachers_ref,
+    rooms_ref,
+):
+    ref = {}
+
+    # Get absences
+    exams = (
+        run_default_filter(validity_range, mysql_models.Exam.objects, filter_term=False)
+        .filter(
+            date__lte=date_to_untis_date(validity_range.date_end),
+            date__gte=date_to_untis_date(validity_range.date_start),
+        )
+        .order_by("exam_id")
+    )
+
+    existing_exams = []
+    for exam in tqdm(exams, desc="Import exams", **TQDM_DEFAULTS):
+        import_ref = exam.exam_id
+
+        logger.info("Import exam {}".format(import_ref))
+
+        # Build values
+        title = exam.name
+        comment = exam.text
+
+        day = untis_date_to_date(exam.date)
+        period_from = exam.lessonfrom
+        period_to = exam.lessonto
+
+        weekday = day.weekday()
+        week = CalendarWeek.from_date(day)
+
+        # Check min/max weekdays
+        weekday = move_weekday_to_range(time_periods_ref, weekday)
+
+        # Check min/max periods
+        first_period = get_first_period(time_periods_ref, weekday)
+        last_period = get_last_period(time_periods_ref, weekday)
+
+        if period_from == 0:
+            period_from = first_period
+        if period_to == 0:
+            period_to = last_period
+
+        time_period_from = time_periods_ref[weekday][period_from]
+        time_period_to = time_periods_ref[weekday][period_to]
+
+        # Get groups, teachers and rooms
+        raw_exams = connect_untis_fields(exam, "examelement", 10)
+        first = True
+        lesson = None
+        subject = None
+        exams = []
+        for raw_exam in raw_exams:
+            el = untis_split_second(raw_exam, remove_empty=False)
+            if first:
+                lesson_id = int(el[0])
+                subject_id = int(el[1])
+                lesson = Lesson.objects.get(validity=validity_range, lesson_id_untis=lesson_id)
+                subject = subjects_ref[subject_id]
+            first = False
+            period = int(el[4])
+            period = time_periods_ref[weekday][period]
+            teacher_id = int(el[5])
+            room_id = int(el[6])
+            teacher = teachers_ref[teacher_id]
+            room = rooms_ref[room_id]
+            exams.append((period, teacher, room))
+
+        if not lesson or not subject:
+            logger.warning(f"Skip exam {import_ref} due to missing data.")
+            continue
+
+        new_exam, created = chronos_models.Exam.objects.update_or_create(
+            import_ref_untis=import_ref,
+            defaults={
+                "date": day,
+                "lesson": lesson,
+                "period_from": time_period_from,
+                "period_to": time_period_to,
+                "title": title,
+                "comment": comment,
+                "school_term": validity_range.school_term,
+            },
+        )
+
+        if created:
+            logger.info("  New exam created")
+
+        extra_lesson_pks = []
+        for exam in exams:
+            period, teacher, room = exam
+            comment = new_exam.title or _("Exam")
+            extra_lesson, __ = ExtraLesson.objects.get_or_create(
+                exam=new_exam,
+                period=period,
+                defaults={
+                    "room": room,
+                    "week": week.week,
+                    "year": week.year,
+                    "comment": comment,
+                    "subject": subject,
+                },
+            )
+            if (
+                extra_lesson.room != room
+                or extra_lesson.week != week.week
+                or extra_lesson.year != week.year
+                or extra_lesson.comment != comment
+                or extra_lesson.subject != subject
+            ):
+                extra_lesson.room = room
+                extra_lesson.week = week.week
+                extra_lesson.year = week.year
+                extra_lesson.comment = comment
+                extra_lesson.subject = subject
+                extra_lesson.save()
+
+            extra_lesson.groups.set(lesson.groups.all())
+            extra_lesson.teachers.set([teacher])
+
+            extra_lesson_pks.append(extra_lesson.pk)
+
+        # Delete no-longer necessary extra lessons
+        ExtraLesson.objects.filter(exam=new_exam).exclude(pk__in=extra_lesson_pks).delete()
+
+        existing_exams.append(import_ref)
+        ref[import_ref] = new_exam
+
+        # Delete all no longer existing exams
+        for e in chronos_models.Exam.objects.within_dates(
+            validity_range.date_start, validity_range.date_end
+        ):
+            if e.import_ref_untis and e.import_ref_untis not in existing_exams:
+                logger.info("exam {} deleted".format(e.id))
+                e.delete()
diff --git a/aleksis/apps/untis/util/mysql/main.py b/aleksis/apps/untis/util/mysql/main.py
index 8b59bb6482d008e6a48baa66aecc375943b8a0c9..3e66968866a33611f1b89bbb7b439e71a285880a 100644
--- a/aleksis/apps/untis/util/mysql/main.py
+++ b/aleksis/apps/untis/util/mysql/main.py
@@ -21,6 +21,7 @@ from .importers.common_data import (
     import_time_periods,
 )
 from .importers.events import import_events
+from .importers.exams import import_exams
 from .importers.holidays import import_holidays
 from .importers.lessons import import_lessons
 from .importers.substitutions import import_substitutions
@@ -93,3 +94,6 @@ def untis_import_mysql(
 
             # Events
             import_events(validity_range, time_periods_ref, teachers_ref, classes_ref, rooms_ref)
+
+            # Exams
+            import_exams(validity_range, time_periods_ref, subjects_ref, teachers_ref, rooms_ref)
diff --git a/docs/admin/10_features.rst b/docs/admin/10_features.rst
index 8d6e0c2a73d58c43ff5960cc3674b2ef12149650..fac7e04750a8aaa46934e37785483a59eadf9f8d 100644
--- a/docs/admin/10_features.rst
+++ b/docs/admin/10_features.rst
@@ -25,6 +25,7 @@ information from Untis can be imported into AlekSIS:
 * Absences, absence reasons
 * Substitutions, extra lessons, cancellations
 * Events
+* Exams
 
 The Untis integration supports the versioning features of Untis. By default,
 the most recent version of each object is imported.
@@ -32,10 +33,10 @@ the most recent version of each object is imported.
 Currently, the following features are known not to be supported:
 
 * Students, student groups, student choices
-* Exams
-* Calendars
 * Prebookings
 * Statistical data
 * Special rooms (subject and group rooms)
 
+AlekSIS does not support so-called "day texts" from Untis. These are incompatible with AlekSIS' announcement feature, which can be used as a replacement.
+
 .. _Untis MultiUser: https://www.untis.at/produkte/untis-das-grundpaket/multiuser