diff --git a/api/config/api_urls.py b/api/config/api_urls.py
index e90601470105f7386255398c312991a23cc4fafc..04fbda87c7c958ee7dab011d52078e73f03db573 100644
--- a/api/config/api_urls.py
+++ b/api/config/api_urls.py
@@ -20,6 +20,7 @@ router.register(r"tracks", views.TrackViewSet, "tracks")
 router.register(r"uploads", views.UploadViewSet, "uploads")
 router.register(r"libraries", views.LibraryViewSet, "libraries")
 router.register(r"listen", views.ListenViewSet, "listen")
+router.register(r"stream", views.StreamViewSet, "stream")
 router.register(r"artists", views.ArtistViewSet, "artists")
 router.register(r"channels", audio_views.ChannelViewSet, "channels")
 router.register(r"subscriptions", audio_views.SubscriptionsViewSet, "subscriptions")
diff --git a/api/funkwhale_api/audio/serializers.py b/api/funkwhale_api/audio/serializers.py
index be67539e0a5915718cd369d91dfff7b444f8f8e1..fd57ed374e1b547a2f70ee0f9a5ad4877580f7ee 100644
--- a/api/funkwhale_api/audio/serializers.py
+++ b/api/funkwhale_api/audio/serializers.py
@@ -830,7 +830,10 @@ def rss_serialize_item(upload):
             {
                 # we enforce MP3, since it's the only format supported everywhere
                 "url": federation_utils.full_url(
-                    upload.get_listen_url(to="mp3", download=False)
+                    reverse(
+                        "api:v1:stream-detail", kwargs={"uuid": str(upload.track.uuid)}
+                    )
+                    + ".mp3"
                 ),
                 "length": upload.size or 0,
                 "type": "audio/mpeg",
diff --git a/api/funkwhale_api/common/routers.py b/api/funkwhale_api/common/routers.py
index 11e9dec615394f44d52d2b92d9dd84bb1eb2e647..af519b74236d603c6c162b7124ae69078aea2b0a 100644
--- a/api/funkwhale_api/common/routers.py
+++ b/api/funkwhale_api/common/routers.py
@@ -1,7 +1,7 @@
-from rest_framework.routers import SimpleRouter
+from rest_framework.routers import DefaultRouter
 
 
-class OptionalSlashRouter(SimpleRouter):
+class OptionalSlashRouter(DefaultRouter):
     def __init__(self):
         super().__init__()
         self.trailing_slash = "/?"
diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index 0df0d46408ac53e6e56a8c4c444733cf70fa0519..314162420f9481990ab07bbabee1a65037d918b1 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -10,6 +10,7 @@ import django.db.utils
 from django.utils import timezone
 
 from rest_framework import mixins
+from rest_framework import renderers
 from rest_framework import settings as rest_settings
 from rest_framework import views, viewsets
 from rest_framework.decorators import action
@@ -614,7 +615,7 @@ def handle_serve(
     return response
 
 
-class ListenViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
+class ListenMixin(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
     queryset = models.Track.objects.all()
     serializer_class = serializers.TrackSerializer
     authentication_classes = (
@@ -627,39 +628,66 @@ class ListenViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
     lookup_field = "uuid"
 
     def retrieve(self, request, *args, **kwargs):
+        config = {
+            "explicit_file": request.GET.get("upload"),
+            "download": request.GET.get("download", "true").lower() == "true",
+            "format": request.GET.get("to"),
+            "max_bitrate": request.GET.get("max_bitrate"),
+        }
         track = self.get_object()
-        actor = utils.get_actor_from_request(request)
-        queryset = track.uploads.prefetch_related(
-            "track__album__artist", "track__artist"
-        )
-        explicit_file = request.GET.get("upload")
-        download = request.GET.get("download", "true").lower() == "true"
-        if explicit_file:
-            queryset = queryset.filter(uuid=explicit_file)
-        queryset = queryset.playable_by(actor)
-        queryset = queryset.order_by(F("audio_file").desc(nulls_last=True))
-        upload = queryset.first()
-        if not upload:
-            return Response(status=404)
-
-        format = request.GET.get("to")
-        max_bitrate = request.GET.get("max_bitrate")
-        try:
-            max_bitrate = min(max(int(max_bitrate), 0), 320) or None
-        except (TypeError, ValueError):
-            max_bitrate = None
-
-        if max_bitrate:
-            max_bitrate = max_bitrate * 1000
-        return handle_serve(
-            upload=upload,
-            user=request.user,
-            format=format,
-            max_bitrate=max_bitrate,
-            proxy_media=settings.PROXY_MEDIA,
-            download=download,
-            wsgi_request=request._request,
-        )
+        return handle_stream(track, request, **config)
+
+
+def handle_stream(track, request, download, explicit_file, format, max_bitrate):
+    actor = utils.get_actor_from_request(request)
+    queryset = track.uploads.prefetch_related("track__album__artist", "track__artist")
+    if explicit_file:
+        queryset = queryset.filter(uuid=explicit_file)
+    queryset = queryset.playable_by(actor)
+    queryset = queryset.order_by(F("audio_file").desc(nulls_last=True))
+    upload = queryset.first()
+    if not upload:
+        return Response(status=404)
+
+    try:
+        max_bitrate = min(max(int(max_bitrate), 0), 320) or None
+    except (TypeError, ValueError):
+        max_bitrate = None
+
+    if max_bitrate:
+        max_bitrate = max_bitrate * 1000
+    return handle_serve(
+        upload=upload,
+        user=request.user,
+        format=format,
+        max_bitrate=max_bitrate,
+        proxy_media=settings.PROXY_MEDIA,
+        download=download,
+        wsgi_request=request._request,
+    )
+
+
+class ListenViewSet(ListenMixin):
+    pass
+
+
+class MP3Renderer(renderers.JSONRenderer):
+    format = "mp3"
+    media_type = "audio/mpeg"
+
+
+class StreamViewSet(ListenMixin):
+    renderer_classes = [MP3Renderer]
+
+    def retrieve(self, request, *args, **kwargs):
+        config = {
+            "explicit_file": None,
+            "download": False,
+            "format": "mp3",
+            "max_bitrate": None,
+        }
+        track = self.get_object()
+        return handle_stream(track, request, **config)
 
 
 class UploadViewSet(
diff --git a/api/tests/audio/test_serializers.py b/api/tests/audio/test_serializers.py
index 1667dfe2c38f52782d453874dbe82ebff04f5c6c..fbcd28a1a02625ca7366a37a0f7f300a17fe02ed 100644
--- a/api/tests/audio/test_serializers.py
+++ b/api/tests/audio/test_serializers.py
@@ -6,6 +6,7 @@ import pytest
 import pytz
 
 from django.templatetags.static import static
+from django.urls import reverse
 
 from funkwhale_api.audio import serializers
 from funkwhale_api.common import serializers as common_serializers
@@ -315,7 +316,10 @@ def test_rss_item_serializer(factories):
         "enclosure": [
             {
                 "url": federation_utils.full_url(
-                    upload.get_listen_url("mp3", download=False)
+                    reverse(
+                        "api:v1:stream-detail", kwargs={"uuid": str(upload.track.uuid)}
+                    )
+                    + ".mp3"
                 ),
                 "length": upload.size,
                 "type": "audio/mpeg",
diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py
index 81988a6b38d9b82d1f06172be32620e980bbba89..3ab5812f2f49072298c8ae278066772799f3efa8 100644
--- a/api/tests/music/test_views.py
+++ b/api/tests/music/test_views.py
@@ -445,6 +445,30 @@ def test_listen_explicit_file(factories, logged_in_api_client, mocker, settings)
     )
 
 
+def test_stream(factories, logged_in_api_client, mocker, settings):
+    mocked_serve = mocker.spy(views, "handle_serve")
+    upload = factories["music.Upload"](
+        library__privacy_level="everyone", import_status="finished"
+    )
+    url = (
+        reverse("api:v1:stream-detail", kwargs={"uuid": str(upload.track.uuid)})
+        + ".mp3"
+    )
+    assert url.endswith("/{}.mp3".format(upload.track.uuid))
+    response = logged_in_api_client.get(url)
+
+    assert response.status_code == 200
+    mocked_serve.assert_called_once_with(
+        upload=upload,
+        user=logged_in_api_client.user,
+        format="mp3",
+        download=False,
+        max_bitrate=None,
+        proxy_media=True,
+        wsgi_request=response.wsgi_request,
+    )
+
+
 def test_listen_no_proxy(factories, logged_in_api_client, settings):
     settings.PROXY_MEDIA = False
     upload = factories["music.Upload"](