diff --git a/program/filters.py b/program/filters.py
index 823ed52e38ecd1435828b37db744f1816774815a..b588795aea8207b11544b48008529fd865d81ae6 100644
--- a/program/filters.py
+++ b/program/filters.py
@@ -39,17 +39,23 @@ class IntegerInFilter(filters.BaseInFilter):
 
 
 class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
-    categoryIds = IntegerInFilter(
+    order = filters.OrderingFilter(
+        fields=["name", "id", "is_active", "is_owner"],
+        help_text="Order shows by the given field(s).",
+    )
+    category_ids = IntegerInFilter(
+        field_name="category",
         help_text="Return only shows of the given category or categories.",
     )
-    categorySlug = filters.CharFilter(
-        field_name="category", help_text="Return only shows of the given category slug."
+    category_slug = filters.CharFilter(
+        field_name="category__slug",
+        help_text="Return only shows of the given category slug.",
     )
-    hostIds = IntegerInFilter(
+    host_ids = IntegerInFilter(
         field_name="hosts",
         help_text="Return only shows assigned to the given host(s).",
     )
-    isActive = filters.BooleanFilter(
+    is_active = filters.BooleanFilter(
         field_name="is_active",
         method="filter_active",
         help_text=(
@@ -57,37 +63,48 @@ class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
             "or past or upcoming shows if false."
         ),
     )
-    isPublic = filters.BooleanFilter(
+    is_public = filters.BooleanFilter(
         field_name="is_public",
         help_text="Return only shows that are public/non-public.",
     )
-    languageIds = IntegerInFilter(
+    language_ids = IntegerInFilter(
+        field_name="language",
         help_text="Return only shows of the given language(s).",
     )
-    musicFocusIds = IntegerInFilter(
+    music_focus_ids = IntegerInFilter(
         field_name="music_focus",
         help_text="Return only shows with given music focus(es).",
     )
-    musicFocusSlug = filters.CharFilter(
-        field_name="music_focus", help_text="Return only shows with the give music focus slug."
+    music_focus_slug = filters.CharFilter(
+        field_name="music_focus__slug",
+        help_text="Return only shows with the give music focus slug.",
     )
-    ownerIds = IntegerInFilter(
+    owner_ids = IntegerInFilter(
         field_name="owners",
         help_text="Return only shows that belong to the given owner(s).",
     )
-    topicIds = IntegerInFilter(
+    topic_ids = IntegerInFilter(
+        field_name="topic",
         help_text="Return only shows of the given topic(s).",
     )
-    topicSlug = filters.CharFilter(
-        field_name="topic", help_text="Return only shows of the given topic slug."
+    topic_slug = filters.CharFilter(
+        field_name="topic__slug",
+        help_text="Return only shows of the given topic slug.",
     )
-    typeId = IntegerInFilter(
+    type_id = IntegerInFilter(
+        field_name="type",
         help_text="Return only shows of a given type.",
     )
-    typeSlug = filters.CharFilter(
-        field_name="type", help_text="Return only shows of the given type slug."
+    type_slug = filters.CharFilter(
+        field_name="type__slug",
+        help_text="Return only shows of the given type slug.",
     )
 
+    def filter_queryset(self, queryset):
+        _id = getattr(self.request.user, "id", None)
+        queryset = queryset.annotate(is_owner=Q(owners=_id))
+        return super().filter_queryset(queryset)
+
     def filter_active(self, queryset: QuerySet, name: str, value: bool):
         # Filter currently running shows
         # Get currently running schedules to filter by first
@@ -116,31 +133,23 @@ class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
         else:
             return queryset.exclude(id__in=show_ids, is_active=True)
 
-    @property
-    def qs(self):
-        # allow pagination query parameters in the GET request
-        fields = self.Meta.fields + ["limit", "offset"]
-        if any([key for key in self.request.GET.keys() if key not in fields]):
-            return None
-        else:
-            return super().qs
-
     class Meta:
         model = models.Show
         fields = [
-            "categoryIds",
-            "categorySlug",
-            "hostIds",
-            "isActive",
-            "isPublic",
-            "languageIds",
-            "musicFocusIds",
-            "musicFocusSlug",
-            "ownerIds",
-            "topicIds",
-            "topicSlug",
-            "typeId",
-            "typeSlug",
+            "order",
+            "category_ids",
+            "category_slug",
+            "host_ids",
+            "is_active",
+            "is_public",
+            "language_ids",
+            "music_focus_ids",
+            "music_focus_slug",
+            "owner_ids",
+            "topic_ids",
+            "topic_slug",
+            "type_id",
+            "type_slug",
         ]
 
 
@@ -174,12 +183,12 @@ class TimeSlotFilterSet(filters.FilterSet):
         ),
     )
 
-    scheduleIds = IntegerInFilter(
+    schedule_ids = IntegerInFilter(
         field_name="schedule",
         help_text="Return only timeslots that belong to the specified schedule(s).",
     )
 
-    showIds = IntegerInFilter(
+    show_ids = IntegerInFilter(
         field_name="schedule__show",
         help_text="Return only timeslots that belong to the specified show(s).",
     )
@@ -241,6 +250,8 @@ class TimeSlotFilterSet(filters.FilterSet):
             "start",
             "end",
             "surrounding",
+            "schedule_ids",
+            "show_ids",
         ]
 
 
@@ -249,41 +260,38 @@ class NoteFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
         field_name="id",
         help_text="Return only notes matching the specified id(s).",
     )
-    ownerIds = IntegerInFilter(
+    owner_ids = IntegerInFilter(
         field_name="owner",
         help_text="Return only notes that belong to the specified owner(s).",
     )
-    showIds = IntegerInFilter(
+    show_ids = IntegerInFilter(
         field_name="timeslot__show",
         help_text="Return only notes that belong to the specified show(s).",
     )
-    showOwnerIds = IntegerInFilter(
+    show_owner_ids = IntegerInFilter(
         field_name="timeslot__show__owners",
         help_text="Return only notes by show the specified owner(s): all notes the user may edit.",
     )
-    timeslotIds = IntegerInFilter(
+    timeslot_ids = IntegerInFilter(
         field_name="timeslot",
         help_text="Return only notes that belong to the specified timeslot(s).",
     )
 
     class Meta:
         model = models.Note
-        help_texts = {
-            "ownerId": "Return only notes created by the specified user.",
-        }
         fields = [
             "ids",
-            "ownerIds",
-            "showIds",
-            "showOwnerIds",
-            "timeslotIds",
+            "owner_ids",
+            "show_ids",
+            "show_owner_ids",
+            "timeslot_ids",
         ]
 
 
 class ActiveFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
-    isActive = filters.BooleanFilter(field_name="is_active")
+    is_active = filters.BooleanFilter(field_name="is_active")
 
     class Meta:
         fields = [
-            "isActive",
+            "is_active",
         ]
diff --git a/program/views.py b/program/views.py
index ecdabf3e1ced2e7abafb26850ee47fa721db0a26..6012b1ef9e200fab23676f2e42c9d4726dcaf87a 100644
--- a/program/views.py
+++ b/program/views.py
@@ -32,11 +32,11 @@ from rest_framework.pagination import LimitOffsetPagination
 from rest_framework.response import Response
 
 from django.contrib.auth.models import User
-from django.core.exceptions import FieldError
 from django.http import Http404, HttpResponse
-from django.shortcuts import get_list_or_404, get_object_or_404
+from django.shortcuts import get_object_or_404
 from django.utils import timezone
 from django.utils.translation import gettext as _
+from django_filters.rest_framework import DjangoFilterBackend
 from program import filters
 from program.models import (
     Category,
@@ -374,43 +374,9 @@ class APIShowViewSet(DisabledObjectPermissionCheckMixin, viewsets.ModelViewSet):
     serializer_class = ShowSerializer
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
     pagination_class = LimitOffsetPagination
+    filter_backends = [DjangoFilterBackend, drf_filters.SearchFilter]
     filterset_class = filters.ShowFilterSet
-
-    def list(self, request, *args, **kwargs):
-        filter_kwargs = {}
-
-        for key, value in request.query_params.items():
-            #  map query parameters to filter names
-            if value == "":
-                pass
-            elif key == "host_ids" or key == "owner_ids":
-                if value.count(",") > 0:
-                    filter_kwargs[f"{key[:-4]}s__id__in"] = value.split(",")
-                else:
-                    filter_kwargs[f"{key[:-4]}s__id"] = value
-            elif key.endswith("_ids"):
-                if value.count(",") > 0:
-                    filter_kwargs[f"{key[:-4]}__id__in"] = value.split(",")
-                else:
-                    filter_kwargs[f"{key[:-4]}__id"] = value
-            elif key.endswith("_id"):
-                filter_kwargs[f"{key[:-3]}__id"] = value
-            elif key.endswith("_slug"):
-                filter_kwargs[f"{key[:-5]}__slug"] = value
-            else:
-                filter_kwargs[key] = value
-
-        try:
-            queryset = get_list_or_404(self.get_queryset(), **filter_kwargs)
-        except FieldError:
-            queryset = None
-
-        if page := self.paginate_queryset(queryset) is not None:
-            serializer = self.get_serializer(page, many=True)
-            return self.get_paginated_response(serializer.data)
-
-        serializer = self.get_serializer(queryset, many=True)
-        return Response(serializer.data)
+    search_fields = ["name", "slug", "short_description", "description"]
 
     def get_object(self):
         queryset = self.filter_queryset(self.get_queryset())
@@ -808,49 +774,6 @@ class APINoteViewSet(
     pagination_class = LimitOffsetPagination
     filterset_class = filters.NoteFilterSet
 
-    def list(self, request, *args, **kwargs):
-        filter_kwargs = {}
-
-        #  map query parameters to filter names
-        for key, value in request.query_params.items():
-            if value == "":
-                pass
-            elif key == "ids":
-                if value.count(",") > 0:
-                    filter_kwargs["id__in"] = value.split(",")
-                else:
-                    filter_kwargs["id"] = value
-            elif key == "show_ids":
-                if value.count(",") > 0:
-                    filter_kwargs["timeslot__show__in"] = value.split(",")
-                else:
-                    filter_kwargs["timeslot__show"] = value
-            elif key == "show_owner_ids":
-                if value.count(",") > 0:
-                    filter_kwargs["timeslot__show__owners__in"] = value.split(",")
-                else:
-                    filter_kwargs["timeslot__show__owners"] = value
-            elif key.endswith("_ids"):
-                if value.count(",") > 0:
-                    filter_kwargs[f"{key[:-4]}__in"] = value.split(",")
-                else:
-                    filter_kwargs[f"{key[:-4]}"] = value
-            else:
-                filter_kwargs[key] = value
-
-        try:
-            queryset = get_list_or_404(self.get_queryset(), **filter_kwargs)
-        except FieldError:
-            queryset = None
-
-        if page := self.paginate_queryset(queryset) is not None:
-            serializer = self.get_serializer(page, many=True)
-            return self.get_paginated_response(serializer.data)
-
-        serializer = self.get_serializer(queryset, many=True)
-
-        return Response(serializer.data)
-
     def get_serializer_context(self):
         # the serializer needs the request in the context
         context = super().get_serializer_context()