diff --git a/api/config/api_urls.py b/api/config/api_urls.py
index a40ff3047a33b3b66dc8ef1bf2342c7c082cfc0e..2631309eb324ea75536b8d18433775e36986cd4c 100644
--- a/api/config/api_urls.py
+++ b/api/config/api_urls.py
@@ -6,11 +6,12 @@ from rest_framework_jwt import views as jwt_views
 
 from funkwhale_api.activity import views as activity_views
 from funkwhale_api.common import views as common_views
+from funkwhale_api.common import routers as common_routers
 from funkwhale_api.music import views
 from funkwhale_api.playlists import views as playlists_views
 from funkwhale_api.subsonic.views import SubsonicViewSet
 
-router = routers.SimpleRouter()
+router = common_routers.OptionalSlashRouter()
 router.register(r"settings", GlobalPreferencesViewSet, basename="settings")
 router.register(r"activity", activity_views.ActivityViewSet, "activity")
 router.register(r"tags", views.TagViewSet, "tags")
@@ -79,8 +80,8 @@ v1_patterns += [
         r"^oauth/",
         include(("funkwhale_api.users.oauth.urls", "oauth"), namespace="oauth"),
     ),
-    url(r"^token/$", jwt_views.obtain_jwt_token, name="token"),
-    url(r"^token/refresh/$", jwt_views.refresh_jwt_token, name="token_refresh"),
+    url(r"^token/?$", jwt_views.obtain_jwt_token, name="token"),
+    url(r"^token/refresh/?$", jwt_views.refresh_jwt_token, name="token_refresh"),
 ]
 
 urlpatterns = [
diff --git a/api/funkwhale_api/common/routers.py b/api/funkwhale_api/common/routers.py
new file mode 100644
index 0000000000000000000000000000000000000000..11e9dec615394f44d52d2b92d9dd84bb1eb2e647
--- /dev/null
+++ b/api/funkwhale_api/common/routers.py
@@ -0,0 +1,7 @@
+from rest_framework.routers import SimpleRouter
+
+
+class OptionalSlashRouter(SimpleRouter):
+    def __init__(self):
+        super().__init__()
+        self.trailing_slash = "/?"
diff --git a/api/funkwhale_api/favorites/urls.py b/api/funkwhale_api/favorites/urls.py
index 28d0c867667d24c7daa8cd090b670e2060e5d147..51f3078038e882aeb90a41bf1f4a1c870e7d9d56 100644
--- a/api/funkwhale_api/favorites/urls.py
+++ b/api/funkwhale_api/favorites/urls.py
@@ -1,8 +1,8 @@
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"tracks", views.TrackFavoriteViewSet, "tracks")
 
 urlpatterns = router.urls
diff --git a/api/funkwhale_api/federation/api_urls.py b/api/funkwhale_api/federation/api_urls.py
index bd2258de961f8d0c80f8b2a3b73d5dd6a9a8ca07..896fa2430448bcb275e05688b9863dff0ed3528e 100644
--- a/api/funkwhale_api/federation/api_urls.py
+++ b/api/funkwhale_api/federation/api_urls.py
@@ -1,8 +1,8 @@
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import api_views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"fetches", api_views.FetchViewSet, "fetches")
 router.register(r"follows/library", api_views.LibraryFollowViewSet, "library-follows")
 router.register(r"inbox", api_views.InboxItemViewSet, "inbox")
diff --git a/api/funkwhale_api/history/urls.py b/api/funkwhale_api/history/urls.py
index 707e95cd7d3056ced030a6f52b584456134c19e3..c22e58062d38a3eb6c497467dc811e38dcbc212a 100644
--- a/api/funkwhale_api/history/urls.py
+++ b/api/funkwhale_api/history/urls.py
@@ -1,8 +1,8 @@
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"listenings", views.ListeningViewSet, "listenings")
 
 urlpatterns = router.urls
diff --git a/api/funkwhale_api/instance/urls.py b/api/funkwhale_api/instance/urls.py
index 05682b1e762ace43a2f9f14168f9ca42530ef439..eff731b26453f86586c513903fdcafde2cda3df7 100644
--- a/api/funkwhale_api/instance/urls.py
+++ b/api/funkwhale_api/instance/urls.py
@@ -1,12 +1,12 @@
 from django.conf.urls import url
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-admin_router = routers.SimpleRouter()
+admin_router = routers.OptionalSlashRouter()
 admin_router.register(r"admin/settings", views.AdminSettings, "admin-settings")
 
 urlpatterns = [
-    url(r"^nodeinfo/2.0/$", views.NodeInfo.as_view(), name="nodeinfo-2.0"),
-    url(r"^settings/$", views.InstanceSettings.as_view(), name="settings"),
+    url(r"^nodeinfo/2.0/?$", views.NodeInfo.as_view(), name="nodeinfo-2.0"),
+    url(r"^settings/?$", views.InstanceSettings.as_view(), name="settings"),
 ] + admin_router.urls
diff --git a/api/funkwhale_api/manage/urls.py b/api/funkwhale_api/manage/urls.py
index 2d5da59e3ed6b8656c3438fb9121819e18079d47..2af18f5b776dae045e6f097ad8a49bbce99179be 100644
--- a/api/funkwhale_api/manage/urls.py
+++ b/api/funkwhale_api/manage/urls.py
@@ -1,28 +1,28 @@
 from django.conf.urls import include, url
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-federation_router = routers.SimpleRouter()
+federation_router = routers.OptionalSlashRouter()
 federation_router.register(r"domains", views.ManageDomainViewSet, "domains")
 
-library_router = routers.SimpleRouter()
+library_router = routers.OptionalSlashRouter()
 library_router.register(r"albums", views.ManageAlbumViewSet, "albums")
 library_router.register(r"artists", views.ManageArtistViewSet, "artists")
 library_router.register(r"libraries", views.ManageLibraryViewSet, "libraries")
 library_router.register(r"tracks", views.ManageTrackViewSet, "tracks")
 library_router.register(r"uploads", views.ManageUploadViewSet, "uploads")
 
-moderation_router = routers.SimpleRouter()
+moderation_router = routers.OptionalSlashRouter()
 moderation_router.register(
     r"instance-policies", views.ManageInstancePolicyViewSet, "instance-policies"
 )
 
-users_router = routers.SimpleRouter()
+users_router = routers.OptionalSlashRouter()
 users_router.register(r"users", views.ManageUserViewSet, "users")
 users_router.register(r"invitations", views.ManageInvitationViewSet, "invitations")
 
-other_router = routers.SimpleRouter()
+other_router = routers.OptionalSlashRouter()
 other_router.register(r"accounts", views.ManageActorViewSet, "accounts")
 
 urlpatterns = [
diff --git a/api/funkwhale_api/moderation/urls.py b/api/funkwhale_api/moderation/urls.py
index 05d2e7a9223774db0329f66ae295255cb88d9b7a..cd3e7bc2d9b2475ec65df6f8e1fd2fbfe77cb9da 100644
--- a/api/funkwhale_api/moderation/urls.py
+++ b/api/funkwhale_api/moderation/urls.py
@@ -1,8 +1,8 @@
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"content-filters", views.UserFilterViewSet, "content-filters")
 
 urlpatterns = router.urls
diff --git a/api/funkwhale_api/musicbrainz/urls.py b/api/funkwhale_api/musicbrainz/urls.py
index d14447f14a62a73a494e9c6752d67274824f18ed..f4ced5b005dffdf03184a4269be0469d0d0cdc48 100644
--- a/api/funkwhale_api/musicbrainz/urls.py
+++ b/api/funkwhale_api/musicbrainz/urls.py
@@ -1,9 +1,9 @@
 from django.conf.urls import url
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"search", views.SearchViewSet, "search")
 urlpatterns = [
     url(
diff --git a/api/funkwhale_api/radios/urls.py b/api/funkwhale_api/radios/urls.py
index 8b9fd52c8a440d7ec41a8afa1e703f8b2a823143..4890b953f1bdce37c26a67e83bf535703edce1ce 100644
--- a/api/funkwhale_api/radios/urls.py
+++ b/api/funkwhale_api/radios/urls.py
@@ -1,8 +1,8 @@
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"sessions", views.RadioSessionViewSet, "sessions")
 router.register(r"radios", views.RadioViewSet, "radios")
 router.register(r"tracks", views.RadioSessionTrackViewSet, "tracks")
diff --git a/api/funkwhale_api/users/api_urls.py b/api/funkwhale_api/users/api_urls.py
index 267ee2d69ad6dc1dd9d1b874072e415a9bdda41d..89930f57be2bad970f17e0a46ef3c3bcbe3e1468 100644
--- a/api/funkwhale_api/users/api_urls.py
+++ b/api/funkwhale_api/users/api_urls.py
@@ -1,8 +1,8 @@
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"users", views.UserViewSet, "users")
 
 urlpatterns = router.urls
diff --git a/api/funkwhale_api/users/oauth/urls.py b/api/funkwhale_api/users/oauth/urls.py
index 832f9ca1ba6cfd64a673213961ed35942aef3c98..4230668e4a1463237ce0f72f90ac23d6d18f3e18 100644
--- a/api/funkwhale_api/users/oauth/urls.py
+++ b/api/funkwhale_api/users/oauth/urls.py
@@ -1,11 +1,11 @@
 from django.conf.urls import url
 from django.views.decorators.csrf import csrf_exempt
 
-from rest_framework import routers
+from funkwhale_api.common import routers
 
 from . import views
 
-router = routers.SimpleRouter()
+router = routers.OptionalSlashRouter()
 router.register(r"apps", views.ApplicationViewSet, "apps")
 router.register(r"grants", views.GrantViewSet, "grants")
 
diff --git a/api/funkwhale_api/users/rest_auth_urls.py b/api/funkwhale_api/users/rest_auth_urls.py
index 732a3bbbcead15c5603f16872a97c6703dcbecca..0421473ab14d6e92f628776e7fbd38f438ed30cf 100644
--- a/api/funkwhale_api/users/rest_auth_urls.py
+++ b/api/funkwhale_api/users/rest_auth_urls.py
@@ -8,12 +8,12 @@ from . import views
 urlpatterns = [
     url(r"^$", views.RegisterView.as_view(), name="rest_register"),
     url(
-        r"^verify-email/$",
+        r"^verify-email/?$",
         registration_views.VerifyEmailView.as_view(),
         name="rest_verify_email",
     ),
     url(
-        r"^change-password/$",
+        r"^change-password/?$",
         rest_auth_views.PasswordChangeView.as_view(),
         name="change_password",
     ),
@@ -28,7 +28,7 @@ urlpatterns = [
     # view from:
     # djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
     url(
-        r"^account-confirm-email/(?P<key>\w+)/$",
+        r"^account-confirm-email/(?P<key>\w+)/?$",
         TemplateView.as_view(),
         name="account_confirm_email",
     ),
diff --git a/api/tests/common/test_routers.py b/api/tests/common/test_routers.py
new file mode 100644
index 0000000000000000000000000000000000000000..3bd5c4e47f5f823ef75e5d07fd9ce9514d67ab4f
--- /dev/null
+++ b/api/tests/common/test_routers.py
@@ -0,0 +1,34 @@
+import pytest
+from django import urls
+
+
+@pytest.mark.parametrize(
+    "url",
+    [
+        "/api/v1/artists",
+        "/api/v1/albums",
+        "/api/v1/tracks",
+        "/api/v1/libraries",
+        "/api/v1/uploads",
+        "/api/v1/playlists",
+        "/api/v1/favorites/tracks",
+        "/api/v1/auth/registration/verify-email",
+        "/api/v1/auth/registration/change-password",
+        "/api/v1/auth/registration/account-confirm-email/key",
+        "/api/v1/history/listenings",
+        "/api/v1/radios/sessions",
+        "/api/v1/users/users/me",
+        "/api/v1/federation/follows/library",
+        "/api/v1/manage/accounts",
+        "/api/v1/oauth/apps",
+        "/api/v1/moderation/content-filters",
+        "/api/v1/token",
+        "/api/v1/token/refresh",
+        "/api/v1/instance/settings",
+        "/api/v1/instance/nodeinfo/2.0",
+    ],
+)
+@pytest.mark.parametrize("suffix", ["", "/"])
+def test_optional_trailing_slash(url, suffix):
+    match = urls.resolve(url + suffix)
+    assert match is not None
diff --git a/changes/changelog.d/877.enhancement b/changes/changelog.d/877.enhancement
new file mode 100644
index 0000000000000000000000000000000000000000..8f65920c88d5321705b41144dabc7ceb87df4258
--- /dev/null
+++ b/changes/changelog.d/877.enhancement
@@ -0,0 +1 @@
+Ensure API urls answer with and without a trailing slash (#877)