diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 34b0b69c87f7614e0e478e40bb21a96b508bf089..252cd5123efeb945a5e1a7cd4dd4ef824f2ee74d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -35,6 +35,7 @@ Added
 * Provide API endpoint for system status.
 * [Dev] UpdateIndicator Vue Component to display the status of interactive pages
 * [Dev] DeleteDialog Vue Component to unify item deletion in the new frontend
+* Use build-in mechanism in Apollo for GraphQL batch querying.
 
 
 Changed
diff --git a/aleksis/core/frontend/app/apollo.js b/aleksis/core/frontend/app/apollo.js
index 267997b9f93ba8d992a90093a93e650f32995811..519bc24aa5d7920d6433d4eebef1b38673753907 100644
--- a/aleksis/core/frontend/app/apollo.js
+++ b/aleksis/core/frontend/app/apollo.js
@@ -2,11 +2,12 @@
  * Configuration for Apollo provider, client, and caches.
  */
 
-import { ApolloClient, HttpLink, from } from "@/apollo-boost";
+import { ApolloClient, from } from "@/apollo-boost";
 
 import { RetryLink } from "@/apollo-link-retry";
 import { persistCache, LocalStorageWrapper } from "@/apollo3-cache-persist";
 import { InMemoryCache } from "@/apollo-cache-inmemory";
+import { BatchHttpLink } from "@/apollo-link-batch-http";
 
 // Cache for GraphQL query results in memory and persistent across sessions
 const cache = new InMemoryCache();
@@ -33,14 +34,17 @@ const links = [
   // Automatically retry failed queries
   new RetryLink(),
   // Finally, the HTTP link to the real backend (Django)
-  new HttpLink({
+  new BatchHttpLink({
     uri: getGraphqlURL(),
+    batchInterval: 200,
+    batchDebounce: true,
   }),
 ];
 
 /** Upstream Apollo GraphQL client */
 const apolloClient = new ApolloClient({
   cache,
+  shouldBatch: true,
   link: from(links),
 });
 
diff --git a/aleksis/core/frontend/components/celery_progress/CeleryProgressBottom.vue b/aleksis/core/frontend/components/celery_progress/CeleryProgressBottom.vue
index 410e57fda0eca36d3acaca7fc0318174fd71d318..912fb779241ef577c6f900abc78a26008df008f6 100644
--- a/aleksis/core/frontend/components/celery_progress/CeleryProgressBottom.vue
+++ b/aleksis/core/frontend/components/celery_progress/CeleryProgressBottom.vue
@@ -47,7 +47,7 @@ export default {
   apollo: {
     celeryProgressByUser: {
       query: gqlCeleryProgressButton,
-      pollInterval: 1000,
+      pollInterval: 30000,
     },
   },
 };
diff --git a/aleksis/core/frontend/components/notifications/NotificationList.vue b/aleksis/core/frontend/components/notifications/NotificationList.vue
index b64769cc6d3dacb130197a3efcea516ef3d89c83..1c51c00661816fe3e244e6d511a0e70c747dbca6 100644
--- a/aleksis/core/frontend/components/notifications/NotificationList.vue
+++ b/aleksis/core/frontend/components/notifications/NotificationList.vue
@@ -83,7 +83,7 @@ export default {
   apollo: {
     myNotifications: {
       query: gqlMyNotifications,
-      pollInterval: 1000,
+      pollInterval: 30000,
     },
   },
 };
diff --git a/aleksis/core/frontend/mixins/permissions.js b/aleksis/core/frontend/mixins/permissions.js
index 0f74b4ea857d1aed1ff93956515241538696c5eb..9d5a00f51a747e06d0d38461351d9bc63a1dc8ac 100644
--- a/aleksis/core/frontend/mixins/permissions.js
+++ b/aleksis/core/frontend/mixins/permissions.js
@@ -1,5 +1,3 @@
-import gqlPermissions from "../components/app/permissions.graphql";
-
 /**
  * Vue mixin containing permission checking code.
  */
diff --git a/aleksis/core/frontend/mixins/routes.js b/aleksis/core/frontend/mixins/routes.js
index 9adbdafc7426fcc19174f91266b978471f3a38e9..e458b23a6f65d22f9682178980ab8e15fe1e74d5 100644
--- a/aleksis/core/frontend/mixins/routes.js
+++ b/aleksis/core/frontend/mixins/routes.js
@@ -14,7 +14,7 @@ const routesMixin = {
   apollo: {
     dynamicRoutes: {
       query: gqlDynamicRoutes,
-      pollInterval: 10000,
+      pollInterval: 30000,
     },
   },
   watch: {
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index bf6f4ffde3d7274371a2acd1d8bfb78d2d53357b..656fcd30a69e245c02d7bf2a984517265c512da9 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -587,6 +587,7 @@ YARN_INSTALLED_APPS = [
     "@iconify/json@^2.1.30",
     "@mdi/font@^7.2.96",
     "apollo-boost@^0.4.9",
+    "apollo-link-batch-http@^1.2.14",
     "apollo-link-retry@^2.2.16",
     "apollo3-cache-persist@^0.14.1",
     "deepmerge@^4.2.2",
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index cc5d954523b331e02b02082014a911d76640ec00..7a22c7bb20a9d3c2d0d0e5af6bece161ac5dbdfa 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -27,7 +27,7 @@ urlpatterns = [
     path("__icons__/", include("dj_iconify.urls")),
     path(
         "graphql/",
-        csrf_exempt(views.LoggingGraphQLView.as_view(graphiql=True)),
+        csrf_exempt(views.LoggingGraphQLView.as_view(batch=True)),
         name="graphql",
     ),
     path("logo", force_maintenance_mode_off(views.LogoView.as_view()), name="logo"),