diff --git a/scipost_django/api/urls.py b/scipost_django/api/urls.py
index 81669f12db98ac7a553d99bcda9f2babf4bd5355..83fbc8ae39e4c5378970d948762be1b62718a111 100644
--- a/scipost_django/api/urls.py
+++ b/scipost_django/api/urls.py
@@ -8,12 +8,16 @@ from rest_framework import routers
 
 # journals
 from journals.api.viewsets import (
+    PublicationPublicAPIViewSet,
     PublicationPublicSearchAPIViewSet,
     PubFractionPublicAPIViewSet,
 )
 
 # submissions
-from submissions.api.viewsets import SubmissionPublicSearchAPIViewSet
+from submissions.api.viewsets import (
+    SubmissionPublicAPIViewSet,
+    SubmissionPublicSearchAPIViewSet,
+)
 
 # organizations
 from organizations.api.viewsets import (
@@ -39,14 +43,20 @@ app_name = "api"
 
 router = routers.SimpleRouter()
 
-# search (Vue) routes
+# search (Vue-based) routes
 router.register("search/publications", PublicationPublicSearchAPIViewSet)
 router.register("search/submissions", SubmissionPublicSearchAPIViewSet)
 
+#############################
+# publicly-accessible routes
+#############################
+
 # journals
+router.register("publications", PublicationPublicAPIViewSet)
 router.register("pubfractions", PubFractionPublicAPIViewSet)
 
 # submissions
+router.register("submissions", SubmissionPublicAPIViewSet)
 
 # organizations
 router.register("organizations", OrganizationPublicAPIViewSet)
diff --git a/scipost_django/comments/api/serializers/__init__.py b/scipost_django/comments/api/serializers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5958284822c67d728958fc5a35a44b5577a7350c
--- /dev/null
+++ b/scipost_django/comments/api/serializers/__init__.py
@@ -0,0 +1,5 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from .comment import CommentPublicSerializer
diff --git a/scipost_django/comments/api/serializers/comment.py b/scipost_django/comments/api/serializers/comment.py
new file mode 100644
index 0000000000000000000000000000000000000000..79433a8bae97c1cfb3a7b904fae52d892fafc3c7
--- /dev/null
+++ b/scipost_django/comments/api/serializers/comment.py
@@ -0,0 +1,35 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework import serializers
+
+from ...models import Comment
+
+
+class CommentPublicSerializer(serializers.ModelSerializer):
+    status = serializers.CharField(source="get_status_display")
+    author = serializers.SerializerMethodField()
+    nested_comments = serializers.SerializerMethodField()
+    url = serializers.URLField(source="get_absolute_url")
+
+    class Meta:
+        model = Comment
+        fields = [
+            "status",
+            "author",
+            "is_author_reply",
+            "file_attachment",
+            "date_submitted",
+            "doi_string",
+            "citation",
+            "url",
+            "nested_comments",
+        ]
+
+    def get_author(self, obj):
+        return obj.get_author_str()
+
+    def get_nested_comments(self, obj):
+        comments = obj.comments.vetted()
+        return CommentPublicSerializer(comments, many=True, read_only=True).data
diff --git a/scipost_django/journals/api/filtersets/__init__.py b/scipost_django/journals/api/filtersets/__init__.py
index 2fd7ba0e69d3ca10186b58577888415306d82095..4076babe7d0e0c4e953c14f8e5a74d8ad163f6c4 100644
--- a/scipost_django/journals/api/filtersets/__init__.py
+++ b/scipost_django/journals/api/filtersets/__init__.py
@@ -2,6 +2,9 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from .publication import PublicationPublicSearchAPIFilterSet
+from .publication import (
+    PublicationPublicAPIFilterSet,
+    PublicationPublicSearchAPIFilterSet,
+)
 
 from .pubfraction import PubFractionPublicAPIFilterSet
diff --git a/scipost_django/journals/api/filtersets/publication.py b/scipost_django/journals/api/filtersets/publication.py
index bf209fb71ead7d76c43102395770c3f0e48d3c17..4fe8d3f38caa8a6056c7a0f53e34073bf66989d4 100644
--- a/scipost_django/journals/api/filtersets/publication.py
+++ b/scipost_django/journals/api/filtersets/publication.py
@@ -7,6 +7,39 @@ from django_filters import rest_framework as df_filters
 from ...models import Publication
 
 
+class PublicationPublicAPIFilterSet(df_filters.FilterSet):
+    class Meta:
+        model = Publication
+        fields = {
+            "title": ["icontains", "contains", "istartswith", "iregex", "regex"],
+            "author_list": ["icontains", "contains", "iregex", "regex"],
+            "abstract": ["icontains", "contains", "iregex", "regex"],
+            "publication_date": [
+                "year",
+                "month",
+                "exact",
+                "year__gte",
+                "year__lte",
+                "year__range",
+                "gte",
+                "lte",
+                "range",
+            ],
+            "doi_label": [
+                "icontains",
+            ],
+            "acad_field__name": [
+                "icontains",
+            ],
+            "specialties__name": [
+                "icontains",
+            ],
+            "topics__name": [
+                "icontains",
+            ],
+        }
+
+
 class PublicationPublicSearchAPIFilterSet(df_filters.FilterSet):
     class Meta:
         model = Publication
diff --git a/scipost_django/journals/api/serializers/__init__.py b/scipost_django/journals/api/serializers/__init__.py
index 7991aad30aefeb50dffebfecbe44b519ff9def27..b564fb91b86cf50b362554f59538bc07bd2c77d6 100644
--- a/scipost_django/journals/api/serializers/__init__.py
+++ b/scipost_django/journals/api/serializers/__init__.py
@@ -2,6 +2,9 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from .publication import PublicationPublicSearchSerializer
+from .publication import (
+    PublicationPublicSerializer,
+    PublicationPublicSearchSerializer,
+)
 
 from .pubfraction import PubFractionPublicSerializer
diff --git a/scipost_django/journals/api/serializers/publication.py b/scipost_django/journals/api/serializers/publication.py
index 238c8af021b5f756e3a700883e24485b9e068afd..c202fc5a634af02e8725d012288e3747d1f85884 100644
--- a/scipost_django/journals/api/serializers/publication.py
+++ b/scipost_django/journals/api/serializers/publication.py
@@ -8,6 +8,26 @@ from api.serializers import DynamicFieldsModelSerializer
 from ...models import Publication
 
 
+class PublicationPublicSerializer(DynamicFieldsModelSerializer):
+    url = serializers.URLField(source="get_absolute_url")
+    accepted_submission = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Publication
+        fields = [
+            "url",
+            "title",
+            "author_list",
+            "abstract",
+            "doi_label",
+            "publication_date",
+            "accepted_submission",
+        ]
+
+    def get_accepted_submission(self, obj):
+        return obj.accepted_submission.get_absolute_url()
+
+
 class PublicationPublicSearchSerializer(DynamicFieldsModelSerializer):
     url = serializers.URLField(source="get_absolute_url")
 
diff --git a/scipost_django/journals/api/viewsets/__init__.py b/scipost_django/journals/api/viewsets/__init__.py
index 80af7492dc4a8b84fc19fd0d145000f5def60bf7..e7344e94a68969446a27740e883702d8f4d48aad 100644
--- a/scipost_django/journals/api/viewsets/__init__.py
+++ b/scipost_django/journals/api/viewsets/__init__.py
@@ -2,6 +2,9 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from .publication import PublicationPublicSearchAPIViewSet
+from .publication import (
+    PublicationPublicAPIViewSet,
+    PublicationPublicSearchAPIViewSet,
+)
 
 from .pubfraction import PubFractionPublicAPIViewSet
diff --git a/scipost_django/journals/api/viewsets/publication.py b/scipost_django/journals/api/viewsets/publication.py
index f7df91e8ece8f6b72906bc4dde7b121df33b7816..11f53b68bac2da909c31273af8d993a5e55f3cf9 100644
--- a/scipost_django/journals/api/viewsets/publication.py
+++ b/scipost_django/journals/api/viewsets/publication.py
@@ -11,10 +11,50 @@ from api.viewsets.mixins import FilteringOptionsActionMixin
 
 from journals.models import Publication
 from journals.regexes import PUBLICATION_DOI_LABEL_REGEX
-from journals.api.filtersets import PublicationPublicSearchAPIFilterSet
-from journals.api.serializers import PublicationPublicSearchSerializer
+from journals.api.filtersets import (
+    PublicationPublicAPIFilterSet,
+    PublicationPublicSearchAPIFilterSet,
+)
+from journals.api.serializers import (
+    PublicationPublicSerializer,
+    PublicationPublicSearchSerializer,
+)
 
 
+class PublicationPublicAPIViewSet(
+    FilteringOptionsActionMixin, ExtraFilteredReadOnlyModelViewSet
+):
+    queryset = Publication.objects.published()
+    permission_classes = [
+        AllowAny,
+    ]
+    serializer_class = PublicationPublicSerializer
+    lookup_field = "doi_label"
+    lookup_value_regex = PUBLICATION_DOI_LABEL_REGEX
+    search_fields = ["title", "author_list", "abstract", "doi_label"]
+    ordering_fields = [
+        "publication_date",
+    ]
+    filterset_class = PublicationPublicAPIFilterSet
+    extra_filters = {
+        "journal__name": {
+            "fields": [
+                "in_journal__name",
+                "in_issue__in_journal__name",
+                "in_issue__in_volume__in_journal__name",
+            ],
+            "lookups": ["icontains", "istartswith", "iexact", "exact"],
+        }
+    }
+    default_filtering_fields = [
+        "title__icontains",
+        "author_list__icontains",
+        "abstract__icontains",
+        "doi_label__icontains",
+    ]
+
+
+# For Vue-based search
 class PublicationPublicSearchAPIViewSet(
     FilteringOptionsActionMixin, ExtraFilteredReadOnlyModelViewSet
 ):
diff --git a/scipost_django/preprints/api/serializers/__init__.py b/scipost_django/preprints/api/serializers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7256c0db45cb9f7ce90cac103db837ce43c029ea
--- /dev/null
+++ b/scipost_django/preprints/api/serializers/__init__.py
@@ -0,0 +1,5 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from .preprint import PreprintPublicSerializer
diff --git a/scipost_django/preprints/api/serializers/preprint.py b/scipost_django/preprints/api/serializers/preprint.py
new file mode 100644
index 0000000000000000000000000000000000000000..106b817b1198824b0ff336cb39d14f7e4521d58e
--- /dev/null
+++ b/scipost_django/preprints/api/serializers/preprint.py
@@ -0,0 +1,18 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework import serializers
+
+from ...models import Preprint
+
+
+class PreprintPublicSerializer(serializers.ModelSerializer):
+    url = serializers.URLField(source="get_absolute_url")
+
+    class Meta:
+        model = Preprint
+        fields = [
+            "identifier_w_vn_nr",
+            "url",
+        ]
diff --git a/scipost_django/submissions/api/filtersets/__init__.py b/scipost_django/submissions/api/filtersets/__init__.py
index b0b80ee43a449337091951f2f33f68612d798851..7103ca96a194eef218712335eb231e6428737c8a 100644
--- a/scipost_django/submissions/api/filtersets/__init__.py
+++ b/scipost_django/submissions/api/filtersets/__init__.py
@@ -2,4 +2,7 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from .submission import SubmissionPublicSearchAPIFilterSet
+from .submission import (
+    SubmissionPublicAPIFilterSet,
+    SubmissionPublicSearchAPIFilterSet,
+)
diff --git a/scipost_django/submissions/api/filtersets/submission.py b/scipost_django/submissions/api/filtersets/submission.py
index 12209bd5f92ce910c66f155700a40a945fe44f43..e3410595c9996afe75af79b7bb798ab8331611dd 100644
--- a/scipost_django/submissions/api/filtersets/submission.py
+++ b/scipost_django/submissions/api/filtersets/submission.py
@@ -7,6 +7,36 @@ from django_filters import rest_framework as df_filters
 from submissions.models import Submission
 
 
+class SubmissionPublicAPIFilterSet(df_filters.FilterSet):
+    class Meta:
+        model = Submission
+        fields = {
+            "title": ["icontains", "contains", "istartswith", "iregex", "regex"],
+            "author_list": ["icontains", "contains", "iregex", "regex"],
+            "abstract": ["icontains", "contains", "iregex", "regex"],
+            "submission_date": [
+                "date__year",
+                "date__month",
+                "date__exact",
+                "date__year__gte",
+                "date__year__lte",
+                "date__year__range",
+                "date__gte",
+                "date__lte",
+                "date__range",
+            ],
+            "acad_field__name": [
+                "icontains",
+            ],
+            "specialties__name": [
+                "icontains",
+            ],
+            "topics__name": [
+                "icontains",
+            ],
+        }
+
+
 class SubmissionPublicSearchAPIFilterSet(df_filters.FilterSet):
     class Meta:
         model = Submission
diff --git a/scipost_django/submissions/api/serializers/__init__.py b/scipost_django/submissions/api/serializers/__init__.py
index 12c9478b87e117853dd2393c9db33beb02483753..19f7ff1056ee994fe3556b901e49f80578241418 100644
--- a/scipost_django/submissions/api/serializers/__init__.py
+++ b/scipost_django/submissions/api/serializers/__init__.py
@@ -2,4 +2,11 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from .submission import SubmissionPublicSearchSerializer
+from .report import ReportPublicSerializer
+
+from .submission_event import SubmissionEventPublicSerializer
+
+from .submission import (
+    SubmissionPublicSerializer,
+    SubmissionPublicSearchSerializer,
+)
diff --git a/scipost_django/submissions/api/serializers/report.py b/scipost_django/submissions/api/serializers/report.py
new file mode 100644
index 0000000000000000000000000000000000000000..11e1c5ee12e1eea6f9c81171792815d03d41aa96
--- /dev/null
+++ b/scipost_django/submissions/api/serializers/report.py
@@ -0,0 +1,38 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework import serializers
+
+from ...models import Report
+
+from comments.api.serializers import CommentPublicSerializer
+
+
+class ReportPublicSerializer(serializers.ModelSerializer):
+    author = serializers.SerializerMethodField()
+    url = serializers.URLField(source="get_absolute_url")
+    comments = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Report
+        fields = [
+            "status",
+            "report_type",
+            "report_nr",
+            "invited",
+            "author",
+            "doi_string",
+            "date_submitted",
+            "url",
+            "comments",
+        ]
+
+    def get_author(self, obj):
+        if not obj.anonymous:
+            return str(obj.author)
+        return "Anonymous"
+
+    def get_comments(self, obj):
+        comments = obj.comments.vetted()
+        return CommentPublicSerializer(comments, many=True, read_only=True).data
diff --git a/scipost_django/submissions/api/serializers/submission.py b/scipost_django/submissions/api/serializers/submission.py
index 41ea65172e6388cacb927a34fff7fafa27000d1b..2413a95ddda8441c497db269b62f62058c003244 100644
--- a/scipost_django/submissions/api/serializers/submission.py
+++ b/scipost_django/submissions/api/serializers/submission.py
@@ -4,7 +4,81 @@ __license__ = "AGPL v3"
 
 from rest_framework import serializers
 
-from submissions.models import Submission
+from submissions.models import Submission, Report
+from submissions.api.serializers import (
+    ReportPublicSerializer,
+    SubmissionEventPublicSerializer,
+)
+
+from comments.models import Comment
+from comments.api.serializers import CommentPublicSerializer
+from preprints.api.serializers import PreprintPublicSerializer
+
+
+class SubmissionPublicSerializer(serializers.ModelSerializer):
+    identifier = serializers.CharField(source="preprint.identifier_w_vn_nr")
+    url = serializers.URLField(source="get_absolute_url")
+    publication = serializers.SerializerMethodField()
+    acad_field = serializers.StringRelatedField()
+    specialties = serializers.StringRelatedField(many=True)
+    topics = serializers.StringRelatedField(many=True)
+    approaches = serializers.StringRelatedField()
+    submission_date = serializers.CharField(source="submission_date_ymd")
+    is_resubmission_of = serializers.SerializerMethodField()
+    submitted_to = serializers.StringRelatedField()
+    thread_sequence_order = serializers.IntegerField()
+    preprint = PreprintPublicSerializer()
+    reports = serializers.SerializerMethodField()
+    comments = serializers.SerializerMethodField()
+    events = SubmissionEventPublicSerializer(many=True, read_only=True)
+
+    class Meta:
+        model = Submission
+        fields = [
+            "title",
+            "author_list",
+            "abstract",
+            "identifier",
+            "url",
+            "publication",
+            "acad_field",
+            "specialties",
+            "topics",
+            "approaches",
+            "status",
+            "is_current",
+            "submission_date",
+            "original_submission_date",
+            "submitted_to",
+            "proceedings",
+            "is_resubmission_of",
+            "thread_hash",
+            "thread_sequence_order",
+            "code_repository_url",
+            "data_repository_url",
+            "preprint",
+            "reports",
+            "comments",
+            "events",
+        ]
+
+    def get_publication(self, obj):
+        if hasattr(obj, "publication") and obj.publication.is_published:
+            return obj.publication.get_absolute_url()
+        return None
+
+    def get_is_resubmission_of(self, obj):
+        if obj.is_resubmission_of:
+            return obj.is_resubmission_of.get_absolute_url()
+        return None
+
+    def get_reports(self, obj):
+        reports = obj.reports.accepted()
+        return ReportPublicSerializer(reports, many=True, read_only=True).data
+
+    def get_comments(self, obj):
+        comments = obj.comments.vetted()
+        return CommentPublicSerializer(comments, many=True, read_only=True).data
 
 
 class SubmissionPublicSearchSerializer(serializers.ModelSerializer):
diff --git a/scipost_django/submissions/api/serializers/submission_event.py b/scipost_django/submissions/api/serializers/submission_event.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6523582b720f3c1ce95f32a2b0e76d6d75c6296
--- /dev/null
+++ b/scipost_django/submissions/api/serializers/submission_event.py
@@ -0,0 +1,19 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+from rest_framework import serializers
+
+from ...models import SubmissionEvent
+
+
+class SubmissionEventPublicSerializer(serializers.ModelSerializer):
+    event = serializers.CharField(source="get_event_display")
+
+    class Meta:
+        model = SubmissionEvent
+        fields = [
+            "created",
+            "event",
+            "submission",
+        ]
diff --git a/scipost_django/submissions/api/viewsets/__init__.py b/scipost_django/submissions/api/viewsets/__init__.py
index 220db5cda37db34303930072003f96a89f843332..fa2550d10bd43dd1972787c52f7be81709aecbb6 100644
--- a/scipost_django/submissions/api/viewsets/__init__.py
+++ b/scipost_django/submissions/api/viewsets/__init__.py
@@ -2,4 +2,7 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
 __license__ = "AGPL v3"
 
 
-from .submission import SubmissionPublicSearchAPIViewSet
+from .submission import (
+    SubmissionPublicAPIViewSet,
+    SubmissionPublicSearchAPIViewSet,
+)
diff --git a/scipost_django/submissions/api/viewsets/submission.py b/scipost_django/submissions/api/viewsets/submission.py
index 52cb196fce3b19d90bdf3d9a4281dfbc12297c0c..915a8c5e805e9a13e3cd40860dffc7bf67ed5f75 100644
--- a/scipost_django/submissions/api/viewsets/submission.py
+++ b/scipost_django/submissions/api/viewsets/submission.py
@@ -8,8 +8,34 @@ from rest_framework.permissions import AllowAny
 from api.viewsets.mixins import FilteringOptionsActionMixin
 
 from submissions.models import Submission
-from submissions.api.filtersets import SubmissionPublicSearchAPIFilterSet
-from submissions.api.serializers import SubmissionPublicSearchSerializer
+from submissions.api.filtersets import (
+    SubmissionPublicAPIFilterSet,
+    SubmissionPublicSearchAPIFilterSet,
+)
+from submissions.api.serializers import (
+    SubmissionPublicSerializer,
+    SubmissionPublicSearchSerializer,
+)
+
+
+class SubmissionPublicAPIViewSet(
+    FilteringOptionsActionMixin, viewsets.ReadOnlyModelViewSet
+):
+    queryset = Submission.objects.public()
+    permission_classes = [
+        AllowAny,
+    ]
+    serializer_class = SubmissionPublicSerializer
+    search_fields = ["title", "author_list", "abstract"]
+    ordering_fields = [
+        "submission_date",
+    ]
+    filterset_class = SubmissionPublicAPIFilterSet
+    default_filtering_fields = [
+        "title__icontains",
+        "author_list__icontains",
+        "abstract__icontains",
+    ]
 
 
 class SubmissionPublicSearchAPIViewSet(