diff --git a/src/Emoticons/EmotesApiRoutes.php b/src/Emoticons/EmotesApiRoutes.php
new file mode 100644
index 00000000..58d52550
--- /dev/null
+++ b/src/Emoticons/EmotesApiRoutes.php
@@ -0,0 +1,54 @@
+<?php
+namespace Misuzu\Emoticons;
+
+use Index\XArray;
+use Index\Http\{HttpRequest,HttpResponseBuilder};
+use Index\Http\Routing\{HttpGet,HttpOptions,RouteHandler,RouteHandlerCommon};
+
+final class EmotesApiRoutes implements RouteHandler {
+    use RouteHandlerCommon;
+
+    public function __construct(
+        private EmotesData $emotes
+    ) {}
+
+    /** @return int|mixed[] */
+    #[HttpOptions('/api/v1/emotes')]
+    #[HttpGet('/api/v1/emotes')]
+    public function getEmotes(HttpResponseBuilder $response, HttpRequest $request): int|array {
+        $response->setHeader('Access-Control-Allow-Origin', '*');
+        $response->setHeader('Access-Control-Allow-Methods', 'GET');
+        $response->addHeader('Access-Control-Allow-Headers', 'Cache-Control');
+        $response->setHeader('Cache-Control', 'public, max-age=3600');
+
+        if($request->method === 'OPTIONS')
+            return 204;
+
+        $includeId = !empty($request->getParam('include_id'));
+        $includeOrder = !empty($request->getParam('include_order'));
+
+        return XArray::select(
+            $this->emotes->getEmotes(orderBy: 'order'),
+            function($emote) use ($includeId, $includeOrder) {
+                $info = [
+                    'url' => $emote->url,
+                    'strings' => XArray::select(
+                        $this->emotes->getEmoteStrings($emote),
+                        fn($string) => $string->string
+                    ),
+                ];
+
+                if($includeId)
+                    $info['id'] = $emote->id;
+                if($includeOrder)
+                    $info['order'] = $emote->order;
+
+                $rank = $emote->minRank;
+                if($rank !== 0)
+                    $info['min_rank'] = $rank;
+
+                return $info;
+            }
+        );
+    }
+}
diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php
index 46475fac..cb6a3d5f 100644
--- a/src/MisuzuContext.php
+++ b/src/MisuzuContext.php
@@ -176,6 +176,8 @@ class MisuzuContext {
         ));
         $routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class));
 
+        $routingCtx->register($this->deps->constructLazy(Emoticons\EmotesApiRoutes::class));
+
         $routingCtx->register($this->deps->constructLazy(Home\HomeRoutes::class));
         $routingCtx->register($this->deps->constructLazy(Users\Assets\AssetsRoutes::class));
         $routingCtx->register($this->deps->constructLazy(Info\InfoRoutes::class));