This human-readable reference is generated with Maud and is not an OpenAPI document.
All routes are read-only HTTP GET endpoints. JSON endpoints return UTF-8 JSON; downloads return a binary osu! beatmap archive.
The four lookup routes and search reuse the following serialized Rust types. Fields marked optional may be JSON null; fields marked omitted are absent when no value exists.
| Fields | Type and meaning |
|---|---|
beatmapset_id, id, mode_int, total_length, hit_length, user_id, ranked, passcount, playcount | Integers: parent set, beatmap and creator IDs; ruleset number; durations in seconds; rank state and play statistics. |
difficulty_rating, accuracy, ar, bpm, cs, drain | Numbers: star rating, OD, approach rate, tempo, circle size and HP drain. |
mode, status, version, url | Strings describing the ruleset, rank status, difficulty name and beatmap URL. |
convert | Boolean indicating a converted difficulty. |
countCircles, countSliders, countSpinners, isScoreable | Nullable object counts and scoreable flag. |
lastUpdated | Timestamp string as stored by the index. |
deletedAt, checksum, max_combo | Optional deletion timestamp, MD5 checksum and maximum combo; omitted when unavailable. |
| Fields | Type and meaning |
|---|---|
id, user_id, play_count, favourite_count, offset, ranked, track_id | Integer identifiers, counts, offset and rank state; track_id is nullable. |
artist, artist_unicode, title, title_unicode, creator, status, source, tags, preview_url | Metadata strings; Unicode artist/title variants are nullable. |
bpm | Numeric tempo. |
nsfw, video, storyboard, spotlight, can_be_hyped, discussion_enabled, discussion_locked, has_favourited | Boolean feature and state flags. |
is_scoreable | Nullable scoreable flag. |
last_updated, submitted_date, ranked_date, deleted_at, legacy_thread_url | Timestamp or URL strings; all except the first two are nullable. |
hype, nominations_summary, availability, covers | JSON values retained from the osu! API. |
beatmaps | Array of Beatmap objects. |
pack_tags | Array of strings. |
description, ratings | Optional description string and integer array; omitted when unavailable. |
GET /api/v1/beatmaps/md5/:checksumReturns the first indexed beatmapset containing a beatmap with the supplied checksum.
| Location | Name | Required | Description |
|---|---|---|---|
| Path | checksum | Yes | Beatmap checksum string, normally an MD5 digest. |
200 OK — application/json; one Beatmapset object.
404 Not Found — no indexed beatmapset contains the checksum.500 Internal Server Error — the search index lookup failed.Lookup errors do not have a documented JSON body.
curl 'https://mirror.example/api/v1/beatmaps/md5/0123456789abcdef0123456789abcdef'GET /api/v1/beatmaps/:idReturns one indexed beatmap difficulty by beatmap ID.
| Location | Name | Required | Description |
|---|---|---|---|
| Path | id | Yes | Signed 64-bit beatmap ID. A value that cannot be parsed is looked up as ID 0 and normally returns 404. |
200 OK — application/json; one Beatmap object.
404 Not Found — the indexed beatmap was not found.500 Internal Server Error — the search index lookup failed.Lookup errors do not have a documented JSON body.
curl 'https://mirror.example/api/v1/beatmaps/4662168'GET /api/v1/beatmapsets/:idReturns one indexed beatmapset by beatmapset ID.
| Location | Name | Required | Description |
|---|---|---|---|
| Path | id | Yes | Signed 64-bit beatmapset ID. A value that cannot be parsed is looked up as ID 0 and normally returns 404. |
200 OK — application/json; one Beatmapset object.
404 Not Found — the indexed beatmapset was not found.500 Internal Server Error — the search index lookup failed.Lookup errors do not have a documented JSON body.
curl 'https://mirror.example/api/v1/beatmapsets/2556827'GET /api/v1/beatmapsets/beatmap/:idReturns the first indexed beatmapset containing the supplied beatmap ID.
| Location | Name | Required | Description |
|---|---|---|---|
| Path | id | Yes | Signed 64-bit beatmap ID. A value that cannot be parsed is looked up as ID 0 and normally returns 404. |
200 OK — application/json; one Beatmapset object.
404 Not Found — no indexed beatmapset contains the beatmap ID.500 Internal Server Error — the search index lookup failed.Lookup errors do not have a documented JSON body.
curl 'https://mirror.example/api/v1/beatmapsets/beatmap/4662168'GET /api/v1/download/:id and its exact short alias GET /d/:id download an osu! beatmap archive. :id is the numeric beatmapset ID.
| Location | Name | Required/default | Description |
|---|---|---|---|
| Path | id | Required | Signed 64-bit beatmapset ID. |
| Query | video | true by default | Boolean: true returns the normal archive; false requests the upstream no-video variant. Only true and false are valid. |
The no-video variant uses the official upstream noVideo=1 option and a separate {id}_novid.osz disk file and RAM cache key, so it never collides with the default {id}.osz archive.
RAM and valid disk responses are cache hits; an origin download is a miss. Cached responses and upstream misses stream with backpressure. A miss is published to disk atomically only after the complete upstream body is validated and durably written. Byte-range requests are not supported; downloads return the complete archive.
The bounded smart RAM cache is populated lazily from eligible valid disk files or bytes already flowing through a request. Policy refresh never prefetches from the origin. Every ten minutes it retains candidates from the 50 latest ranked maps and the top 30 maps by successful download count, using the most recent successful download time and then map ID to break count ties. Entries outside that union are evicted, and byte capacity always wins, so a small cache may hold fewer maps. Video variants have independent byte entries.
RAM hits clone the retained byte buffer without copying its contents. An archive larger than the configured capacity is still served and kept on disk but is not admitted to RAM. Capacity bounds bytes owned or reserved by the cache; response clones already in flight may outlive eviction.
The cache_size setting is read from Mirria's confy YAML configuration at API startup. It defaults to "10%". Values are trimmed and case-insensitive:
cache_size: "2048MB"
# or
cache_size: "4GB"
# or
cache_size: "10%"MB and GB are positive integer decimal byte units.1% through 100% and is resolved once against total host physical memory at startup.application/x-osu-beatmap-archive binary body.
| Header | Meaning |
|---|---|
Content-Type | application/x-osu-beatmap-archive |
Content-Disposition | Attachment filename; UTF-8 names use RFC 5987 encoding. |
Content-Length | Archive length when known. |
X-Cache-Hit | hit for RAM or disk, or miss for an upstream stream. |
X-RateLimit-Remaining | Reservations still available in the applicable client window after this request was reserved. |
X-RateLimit-Reset | Whole seconds remaining until the applicable fixed window resets (0 through 5). |
Clients are identified by their direct peer IP address; forwarding headers are intentionally not trusted. Cached and non-cached downloads have independent five-second fixed windows.
Capacity is reserved before file or upstream work begins, preventing concurrent requests from bypassing a limit. A reservation is committed only when its response body completes successfully; upstream, cache-file, streaming or abandoned-client failures refund it.
| Status | Meaning | Body/headers |
|---|---|---|
400 Bad Request | The path ID or video boolean could not be parsed. | Axum rejection body; no stable JSON schema. |
429 Too Many Requests | The applicable client window has no capacity. No download or cache-file stream is started. | JSON error body; X-RateLimit-Remaining: 0, plus matching delta-seconds values in X-RateLimit-Reset and Retry-After. |
500 Internal Server Error | The local cache could not be prepared, inspected or opened. | {"ok":false,"message":"..."} |
502 Bad Gateway | The upstream download could not be started, declared an empty body or returned a non-success status. | {"ok":false,"message":"..."} |
# Default archive, including video when the set has one
curl -OJ 'https://mirror.example/api/v1/download/2556827'
# No-video variant through the short alias
curl -OJ 'https://mirror.example/d/2556827?video=false'GET /api/v1/searchRuns a full-text and filtered search over indexed beatmapsets. The response is the hit list itself, not a pagination envelope.
| Name | Type | Default | Behavior |
|---|---|---|---|
query | String | Empty string | Full-text search text. |
limit | Signed integer | 50 | Maximum hits passed to the search backend. The handler does not impose its own range validation. |
offset | Signed integer | 0 | Hit offset passed to the search backend. The handler does not impose its own range validation. |
statuses | Array of strings | [ranked, loved, aproved, qualified] | Status filter. The current default intentionally reflects the implemented spelling aproved. |
sort | String | updated_desc | updated_asc sorts oldest update first; playcount sorts play count ascending; every other value uses last-updated descending. |
modes | Array of enum strings | [osu, taiko, fruits, mania] | Accepted values are osu, taiko, fruits and mania. |
Arrays use indexed serde_qs syntax, for example statuses[0]=ranked&statuses[1]=loved and modes[0]=osu. Percent-encode square brackets when required by the client.
200 OK — application/json; an array of Beatmapset objects. An empty search result is [].
400 Bad Request — malformed query syntax, type or ruleset value.500 Internal Server Error — the search backend failed.Search errors do not have a documented JSON body.
curl 'https://mirror.example/api/v1/search?query=camellia&limit=10&statuses%5B0%5D=ranked&modes%5B0%5D=osu&sort=updated_desc'[{"id":2556827,"artist":"Unlucky Morpheus","title":"Kamigami ga Koishita Gensoukyou","beatmaps":[],"pack_tags":[],"covers":{},"availability":{},"nominations_summary":{},"hype":null,"artist_unicode":null,"title_unicode":null,"creator":"tmk","user_id":1,"status":"ranked","bpm":180.0,"play_count":0,"favourite_count":0,"nsfw":false,"video":false,"storyboard":false,"is_scoreable":true,"source":"","tags":"","preview_url":"","offset":0,"spotlight":false,"ranked":1,"last_updated":"2026-08-17T15:27:44Z","submitted_date":"2026-08-17T15:27:44Z","ranked_date":null,"deleted_at":null,"can_be_hyped":false,"discussion_enabled":false,"discussion_locked":false,"legacy_thread_url":null,"track_id":null,"has_favourited":false}]The example shows the JSON shape with representative values; real metadata and nested arrays vary by result.
GET /metricsReturns the current Prometheus metrics exposition generated by the HTTP metrics recorder. Metric series and labels depend on observed runtime traffic.
No path or query parameters.
200 OK — Prometheus text exposition returned as text/plain; charset=utf-8. This handler defines no route-specific error status.
curl 'https://mirror.example/metrics'# HELP <metric_name> <description>
# TYPE <metric_name> counter
<metric_name>{...} <value>GET /docsServes this generated, human-readable API reference.
No path or query parameters.
200 OK — text/html; charset=utf-8. This handler defines no route-specific error status.
curl 'https://mirror.example/docs'