diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index 7eb409830d7d3531d38f3b7e3f2924213b89376e..d29480648e6b902118a8c77c04dd0ff063cec3b1 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -48,16 +48,18 @@ else:
 FUNKWHALE_URL = '{}://{}'.format(FUNKWHALE_PROTOCOL, FUNKWHALE_HOSTNAME)
 
 
+# XXX: deprecated, see #186
 FEDERATION_ENABLED = env.bool('FEDERATION_ENABLED', default=True)
 FEDERATION_HOSTNAME = env('FEDERATION_HOSTNAME', default=FUNKWHALE_HOSTNAME)
+# XXX: deprecated, see #186
 FEDERATION_COLLECTION_PAGE_SIZE = env.int(
     'FEDERATION_COLLECTION_PAGE_SIZE', default=50
 )
+# XXX: deprecated, see #186
 FEDERATION_MUSIC_NEEDS_APPROVAL = env.bool(
     'FEDERATION_MUSIC_NEEDS_APPROVAL', default=True
 )
-# how much minutes to wait before refetching actor data
-# when authenticating
+# XXX: deprecated, see #186
 FEDERATION_ACTOR_FETCH_DELAY = env.int(
     'FEDERATION_ACTOR_FETCH_DELAY', default=60 * 12)
 ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS')
@@ -366,7 +368,6 @@ CORS_ORIGIN_ALLOW_ALL = True
 #     'funkwhale.localhost',
 # )
 CORS_ALLOW_CREDENTIALS = True
-API_AUTHENTICATION_REQUIRED = env.bool("API_AUTHENTICATION_REQUIRED", True)
 REST_FRAMEWORK = {
     'DEFAULT_PERMISSION_CLASSES': (
         'rest_framework.permissions.IsAuthenticated',
@@ -433,7 +434,7 @@ ADMIN_URL = env('DJANGO_ADMIN_URL', default='^api/admin/')
 CSRF_USE_SESSIONS = True
 
 # Playlist settings
-# XXX: Deprecated, use playlists__max_tracks instead
+# XXX: deprecated, see #186
 PLAYLISTS_MAX_TRACKS = env.int('PLAYLISTS_MAX_TRACKS', default=250)
 
 ACCOUNT_USERNAME_BLACKLIST = [
@@ -453,6 +454,8 @@ EXTERNAL_REQUESTS_VERIFY_SSL = env.bool(
     'EXTERNAL_REQUESTS_VERIFY_SSL',
     default=True
 )
+# XXX: deprecated, see #186
+API_AUTHENTICATION_REQUIRED = env.bool("API_AUTHENTICATION_REQUIRED", True)
 
 MUSIC_DIRECTORY_PATH = env('MUSIC_DIRECTORY_PATH', default=None)
 # on Docker setup, the music directory may not match the host path,
diff --git a/api/funkwhale_api/common/dynamic_preferences_registry.py b/api/funkwhale_api/common/dynamic_preferences_registry.py
new file mode 100644
index 0000000000000000000000000000000000000000..2374de7c79fe841ae63d8c18354335cf6fd022b2
--- /dev/null
+++ b/api/funkwhale_api/common/dynamic_preferences_registry.py
@@ -0,0 +1,20 @@
+from dynamic_preferences import types
+from dynamic_preferences.registries import global_preferences_registry
+
+from funkwhale_api.common import preferences
+
+common = types.Section('common')
+
+
+@global_preferences_registry.register
+class APIAutenticationRequired(
+        preferences.DefaultFromSettingMixin, types.BooleanPreference):
+    section = common
+    name = 'api_authentication_required'
+    verbose_name = 'API Requires authentication'
+    setting = 'API_AUTHENTICATION_REQUIRED'
+    help_text = (
+        'If disabled, anonymous users will be able to query the API'
+        'and access music data (as well as other data exposed in the API '
+        'without specific permissions)'
+    )
diff --git a/api/funkwhale_api/common/permissions.py b/api/funkwhale_api/common/permissions.py
index c99c275c1f636b292c71ab1abb427daa658566fb..cab4b699d2a518cffc52aaf0cbad4473dcc28a6f 100644
--- a/api/funkwhale_api/common/permissions.py
+++ b/api/funkwhale_api/common/permissions.py
@@ -5,11 +5,13 @@ from django.http import Http404
 
 from rest_framework.permissions import BasePermission, DjangoModelPermissions
 
+from funkwhale_api.common import preferences
+
 
 class ConditionalAuthentication(BasePermission):
 
     def has_permission(self, request, view):
-        if settings.API_AUTHENTICATION_REQUIRED:
+        if preferences.get('common__api_authentication_required'):
             return request.user and request.user.is_authenticated
         return True
 
diff --git a/api/funkwhale_api/music/permissions.py b/api/funkwhale_api/music/permissions.py
index 61fc65bebf523f916483e67e2c7a311588bfc6de..77f95c477eb47e5cfbbdc9a13b31b2e9393cf767 100644
--- a/api/funkwhale_api/music/permissions.py
+++ b/api/funkwhale_api/music/permissions.py
@@ -2,6 +2,7 @@ from django.conf import settings
 
 from rest_framework.permissions import BasePermission
 
+from funkwhale_api.common import preferences
 from funkwhale_api.federation import actors
 from funkwhale_api.federation import models
 
@@ -12,6 +13,9 @@ class Listen(BasePermission):
         if not settings.PROTECT_AUDIO_FILES:
             return True
 
+        if not preferences.get('common__api_authentication_required'):
+            return True
+
         user = getattr(request, 'user', None)
         if user and user.is_authenticated:
             return True
diff --git a/api/tests/activity/test_views.py b/api/tests/activity/test_views.py
index bdc3c6339ffe91981621c8f8272788347a01cc8e..9b24f3ad3a9359f53264d6b6d0be52f3490a19f6 100644
--- a/api/tests/activity/test_views.py
+++ b/api/tests/activity/test_views.py
@@ -4,8 +4,8 @@ from funkwhale_api.activity import serializers
 from funkwhale_api.activity import utils
 
 
-def test_activity_view(factories, api_client, settings, anonymous_user):
-    settings.API_AUTHENTICATION_REQUIRED = False
+def test_activity_view(factories, api_client, preferences, anonymous_user):
+    preferences['common__api_authentication_required'] = False
     favorite = factories['favorites.TrackFavorite'](
         user__privacy_level='everyone')
     listening = factories['history.Listening']()
diff --git a/api/tests/favorites/test_favorites.py b/api/tests/favorites/test_favorites.py
index f4a045af825e9431f308903569ac16b07d609692..591fe7c9c8f36fbe8c3b625ee9bd26c4be67ad0d 100644
--- a/api/tests/favorites/test_favorites.py
+++ b/api/tests/favorites/test_favorites.py
@@ -99,8 +99,8 @@ def test_user_can_remove_favorite_via_api_using_track_id(
 @pytest.mark.parametrize('url,method', [
     ('api:v1:favorites:tracks-list', 'get'),
 ])
-def test_url_require_auth(url, method, db, settings, client):
-    settings.API_AUTHENTICATION_REQUIRED = True
+def test_url_require_auth(url, method, db, preferences, client):
+    preferences['common__api_authentication_required'] = True
     url = reverse(url)
     response = getattr(client, method)(url)
     assert response.status_code == 401
diff --git a/api/tests/history/test_history.py b/api/tests/history/test_history.py
index ec8689e9637ca2686452e72d9628a581251e014f..563cf2f08569327455fc64a5855c8b06bc5d6aa9 100644
--- a/api/tests/history/test_history.py
+++ b/api/tests/history/test_history.py
@@ -14,8 +14,9 @@ def test_can_create_listening(factories):
     l = models.Listening.objects.create(user=user, track=track)
 
 
-def test_anonymous_user_can_create_listening_via_api(client, factories, settings):
-    settings.API_AUTHENTICATION_REQUIRED = False
+def test_anonymous_user_can_create_listening_via_api(
+        client, factories, preferences):
+    preferences['common__api_authentication_required'] = False
     track = factories['music.Track']()
     url = reverse('api:v1:history:listenings-list')
     response = client.post(url, {
diff --git a/api/tests/music/test_api.py b/api/tests/music/test_api.py
index cc6fe644b6a4acb7a654453332021dfdc3803013..53ee29f3e909a9c4a78022d0669fdc3710412513 100644
--- a/api/tests/music/test_api.py
+++ b/api/tests/music/test_api.py
@@ -265,16 +265,16 @@ def test_can_search_tracks(factories, logged_in_client):
     ('api:v1:albums-list', 'get'),
 ])
 def test_can_restrict_api_views_to_authenticated_users(
-        db, route, method, settings, client):
+        db, route, method, preferences, client):
     url = reverse(route)
-    settings.API_AUTHENTICATION_REQUIRED = True
+    preferences['common__api_authentication_required'] = True
     response = getattr(client, method)(url)
     assert response.status_code == 401
 
 
 def test_track_file_url_is_restricted_to_authenticated_users(
-        api_client, factories, settings):
-    settings.API_AUTHENTICATION_REQUIRED = True
+        api_client, factories, preferences):
+    preferences['common__api_authentication_required'] = True
     f = factories['music.TrackFile']()
     assert f.audio_file is not None
     url = f.path
@@ -283,8 +283,8 @@ def test_track_file_url_is_restricted_to_authenticated_users(
 
 
 def test_track_file_url_is_accessible_to_authenticated_users(
-        logged_in_api_client, factories, settings):
-    settings.API_AUTHENTICATION_REQUIRED = True
+        logged_in_api_client, factories, preferences):
+    preferences['common__api_authentication_required'] = True
     f = factories['music.TrackFile']()
     assert f.audio_file is not None
     url = f.path
diff --git a/api/tests/playlists/test_views.py b/api/tests/playlists/test_views.py
index f0fb6d0fdc19286b2ebbbc0b2ef5336d3bd642f5..44d0608210d170b9df6b1e830cc1400ef148b7b5 100644
--- a/api/tests/playlists/test_views.py
+++ b/api/tests/playlists/test_views.py
@@ -107,8 +107,8 @@ def test_deleting_plt_updates_indexes(
 
 @pytest.mark.parametrize('level', ['instance', 'me', 'followers'])
 def test_playlist_privacy_respected_in_list_anon(
-        settings, level, factories, api_client):
-    settings.API_AUTHENTICATION_REQUIRED = False
+        preferences, level, factories, api_client):
+    preferences['common__api_authentication_required'] = False
     factories['playlists.Playlist'](privacy_level=level)
     url = reverse('api:v1:playlists-list')
     response = api_client.get(url)
@@ -137,8 +137,8 @@ def test_only_owner_can_edit_playlist_track(
 
 @pytest.mark.parametrize('level', ['instance', 'me', 'followers'])
 def test_playlist_track_privacy_respected_in_list_anon(
-        level, factories, api_client, settings):
-    settings.API_AUTHENTICATION_REQUIRED = False
+        level, factories, api_client, preferences):
+    preferences['common__api_authentication_required'] = False
     factories['playlists.PlaylistTrack'](playlist__privacy_level=level)
     url = reverse('api:v1:playlist-tracks-list')
     response = api_client.get(url)
diff --git a/api/tests/radios/test_radios.py b/api/tests/radios/test_radios.py
index c8038a4dbadcb4283d073492beb715e7092bab20..e78bd0e2fda548ebc204545986536bf2f63f7ff5 100644
--- a/api/tests/radios/test_radios.py
+++ b/api/tests/radios/test_radios.py
@@ -151,8 +151,8 @@ def test_can_start_radio_for_logged_in_user(logged_in_client):
     assert session.user == logged_in_client.user
 
 
-def test_can_start_radio_for_anonymous_user(api_client, db, settings):
-    settings.API_AUTHENTICATION_REQUIRED = False
+def test_can_start_radio_for_anonymous_user(api_client, db, preferences):
+    preferences['common__api_authentication_required'] = False
     url = reverse('api:v1:radios:sessions-list')
     response = api_client.post(url, {'radio_type': 'random'})
 
@@ -232,8 +232,8 @@ def test_can_start_tag_radio(factories):
         assert radio.pick() in good_tracks
 
 
-def test_can_start_artist_radio_from_api(api_client, settings, factories):
-    settings.API_AUTHENTICATION_REQUIRED = False
+def test_can_start_artist_radio_from_api(api_client, preferences, factories):
+    preferences['common__api_authentication_required'] = False
     artist = factories['music.Artist']()
     url = reverse('api:v1:radios:sessions-list')
 
diff --git a/api/tests/test_jwt_querystring.py b/api/tests/test_jwt_querystring.py
index bd07e1dc3212476f01dd7c60f341e8718c60351c..f18e6b7292839617cd61d6357725f6bd493462c4 100644
--- a/api/tests/test_jwt_querystring.py
+++ b/api/tests/test_jwt_querystring.py
@@ -5,9 +5,10 @@ jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
 jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
 
 
-def test_can_authenticate_using_token_param_in_url(factories, settings, client):
+def test_can_authenticate_using_token_param_in_url(
+        factories, preferences, client):
     user = factories['users.User']()
-    settings.API_AUTHENTICATION_REQUIRED = True
+    preferences['common__api_authentication_required'] = True
     url = reverse('api:v1:tracks-list')
     response = client.get(url)
 
diff --git a/api/tests/test_youtube.py b/api/tests/test_youtube.py
index 441179095d98398697c60fb3fabc922dafbeab6e..7ab6256daf73352542acc2a8e62ca7860cd58df4 100644
--- a/api/tests/test_youtube.py
+++ b/api/tests/test_youtube.py
@@ -18,8 +18,8 @@ def test_can_get_search_results_from_youtube(mocker):
 
 
 def test_can_get_search_results_from_funkwhale(
-        settings, mocker, api_client, db):
-    settings.API_AUTHENTICATION_REQUIRED = False
+        preferences, mocker, api_client, db):
+    preferences['common__api_authentication_required'] = False
     mocker.patch(
         'funkwhale_api.providers.youtube.client._do_search',
         return_value=api_data.search['8 bit adventure'])
@@ -70,8 +70,8 @@ def test_can_send_multiple_queries_at_once(mocker):
 
 
 def test_can_send_multiple_queries_at_once_from_funwkhale(
-        settings, mocker, db, api_client):
-    settings.API_AUTHENTICATION_REQUIRED = False
+        preferences, mocker, db, api_client):
+    preferences['common__api_authentication_required'] = False
     mocker.patch(
         'funkwhale_api.providers.youtube.client._do_search',
         return_value=api_data.search['8 bit adventure'])
diff --git a/deploy/env.prod.sample b/deploy/env.prod.sample
index e357d08d3389202ac28920b7332448099705223e..f1718ff7e28f757f8bc993e5a32e22b8f41f9aa2 100644
--- a/deploy/env.prod.sample
+++ b/deploy/env.prod.sample
@@ -88,9 +88,6 @@ DJANGO_SECRET_KEY=
 # want to
 # DJANGO_ADMIN_URL=^api/admin/
 
-# If True, unauthenticated users won't be able to query the API
-API_AUTHENTICATION_REQUIRED=True
-
 # Sentry/Raven error reporting (server side)
 # Enable Raven if you want to help improve funkwhale by
 # automatically sending error reports our Sentry instance.