Mirria HTTP API

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.

JSON response schemas

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.

Beatmap

FieldsType and meaning
beatmapset_id, id, mode_int, total_length, hit_length, user_id, ranked, passcount, playcountIntegers: parent set, beatmap and creator IDs; ruleset number; durations in seconds; rank state and play statistics.
difficulty_rating, accuracy, ar, bpm, cs, drainNumbers: star rating, OD, approach rate, tempo, circle size and HP drain.
mode, status, version, urlStrings describing the ruleset, rank status, difficulty name and beatmap URL.
convertBoolean indicating a converted difficulty.
countCircles, countSliders, countSpinners, isScoreableNullable object counts and scoreable flag.
lastUpdatedTimestamp string as stored by the index.
deletedAt, checksum, max_comboOptional deletion timestamp, MD5 checksum and maximum combo; omitted when unavailable.

Beatmapset

FieldsType and meaning
id, user_id, play_count, favourite_count, offset, ranked, track_idInteger identifiers, counts, offset and rank state; track_id is nullable.
artist, artist_unicode, title, title_unicode, creator, status, source, tags, preview_urlMetadata strings; Unicode artist/title variants are nullable.
bpmNumeric tempo.
nsfw, video, storyboard, spotlight, can_be_hyped, discussion_enabled, discussion_locked, has_favouritedBoolean feature and state flags.
is_scoreableNullable scoreable flag.
last_updated, submitted_date, ranked_date, deleted_at, legacy_thread_urlTimestamp or URL strings; all except the first two are nullable.
hype, nominations_summary, availability, coversJSON values retained from the osu! API.
beatmapsArray of Beatmap objects.
pack_tagsArray of strings.
description, ratingsOptional description string and integer array; omitted when unavailable.

GET /api/v1/beatmaps/md5/:checksum

Returns the first indexed beatmapset containing a beatmap with the supplied checksum.

Parameters

LocationNameRequiredDescription
PathchecksumYesBeatmap checksum string, normally an MD5 digest.

Response and statuses

200 OK — application/json; one Beatmapset object.

Lookup errors do not have a documented JSON body.

Example

curl 'https://mirror.example/api/v1/beatmaps/md5/0123456789abcdef0123456789abcdef'

GET /api/v1/beatmaps/:id

Returns one indexed beatmap difficulty by beatmap ID.

Parameters

LocationNameRequiredDescription
PathidYesSigned 64-bit beatmap ID. A value that cannot be parsed is looked up as ID 0 and normally returns 404.

Response and statuses

200 OK — application/json; one Beatmap object.

Lookup errors do not have a documented JSON body.

Example

curl 'https://mirror.example/api/v1/beatmaps/4662168'

GET /api/v1/beatmapsets/:id

Returns one indexed beatmapset by beatmapset ID.

Parameters

LocationNameRequiredDescription
PathidYesSigned 64-bit beatmapset ID. A value that cannot be parsed is looked up as ID 0 and normally returns 404.

Response and statuses

200 OK — application/json; one Beatmapset object.

Lookup errors do not have a documented JSON body.

Example

curl 'https://mirror.example/api/v1/beatmapsets/2556827'

GET /api/v1/beatmapsets/beatmap/:id

Returns the first indexed beatmapset containing the supplied beatmap ID.

Parameters

LocationNameRequiredDescription
PathidYesSigned 64-bit beatmap ID. A value that cannot be parsed is looked up as ID 0 and normally returns 404.

Response and statuses

200 OK — application/json; one Beatmapset object.

Lookup errors do not have a documented JSON body.

Example

curl 'https://mirror.example/api/v1/beatmapsets/beatmap/4662168'

Map download

GET /api/v1/download/:id and its exact short alias GET /d/:id download an osu! beatmap archive. :id is the numeric beatmapset ID.

Parameters

LocationNameRequired/defaultDescription
PathidRequiredSigned 64-bit beatmapset ID.
Queryvideotrue by defaultBoolean: 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.

Streaming and cache behavior

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. Every ten minutes it refreshes up to 1,000 persisted download statistics; completed downloads update the live counts immediately. Refresh does not prefetch archives. Admission under capacity pressure evicts lower-priority entries by download count, successful-download time and access order; a lower-priority incoming map cannot displace a higher-priority entry. 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.

RAM capacity configuration

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%"

Successful response (200)

application/x-osu-beatmap-archive binary body.

HeaderMeaning
Content-Typeapplication/x-osu-beatmap-archive
Content-DispositionAttachment filename; UTF-8 names use RFC 5987 encoding.
Content-LengthArchive length when known.
X-Cache-Hithit for RAM or disk, or miss for an upstream stream.
X-RateLimit-RemainingReservations still available in the applicable client window after this request was reserved.
X-RateLimit-ResetWhole seconds remaining until the applicable fixed window resets (0 through 5).

Rate limits

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.

Error responses

StatusMeaningBody/headers
400 Bad RequestThe path ID or video boolean could not be parsed.Axum rejection body; no stable JSON schema.
429 Too Many RequestsThe 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 ErrorThe local cache could not be prepared, inspected or opened.{"ok":false,"message":"..."}
502 Bad GatewayThe upstream download could not be started, declared an empty body or returned a non-success status.{"ok":false,"message":"..."}

Examples

# 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/cache

Returns a read-only snapshot of this API process's live RAM cache, not its disk archive directory or a mounted ramdisk. Observing the cache does not change eviction order or retain archive buffers.

Optional query parameter limit defaults to 50 and accepts integers from 1 through 100. Beatmapsets are ordered by the cache's known completed-download count descending, then ID ascending. Video and no-video payload lengths are combined into one row; their shared download count is not doubled.

Response fields

FieldMeaning
ranking"download_count".
storage.used_bytesArchive payload bytes currently retained by the RAM cache.
storage.reserved_bytesCapacity reserved for in-progress cache fills, not completed entries.
storage.total_bytesConfigured RAM cache capacity resolved from cache_size at startup.
storage.available_bytesCapacity remaining after both retained payloads and reservations.
cache.map_count, cache.entry_count, cache.size_bytesTotal unique cached beatmapsets, separate video-variant entries, and retained payload bytes, before applying the result limit.
mapsAt most limit rows with id, title, artist, download_count, size_bytes. Byte size is the sum of that beatmapset's retained variants. Titles and artists may be null.
metadata_availableFalse if the optional title/artist lookup failed. Live counts, sizes, ranking and totals are still returned; missing individual documents leave null metadata without removing rows.

These are cache payload and budget metrics, not process RSS. Allocator overhead and buffers held only by in-flight responses after eviction are excluded. Counts include live completions and the persisted baseline known to the cache; separate API processes have separate live snapshots.

Statuses and example

200 OK — application/json, including an empty RAM cache or unavailable optional metadata. 400 Bad Request — invalid limit, with a JSON ok: false, message error.

curl 'https://mirror.example/api/v1/cache?limit=50'
{"ranking":"download_count","storage":{"used_bytes":300,"reserved_bytes":100,"total_bytes":1000,"available_bytes":600},"cache":{"map_count":2,"entry_count":3,"size_bytes":300},"metadata_available":false,"maps":[{"id":200,"title":null,"artist":null,"download_count":7,"size_bytes":200},{"id":100,"title":null,"artist":null,"download_count":3,"size_bytes":100}]}

GET /cache

Serves the minimal RAM cache dashboard as text/html; charset=utf-8. It fetches this endpoint with a limit of 50 and has a manual refresh button.

Open RAM cache status

GET /health

Returns 200 OK with the plain-text body ok once the API is listening. No parameters, authentication, shared-state locks or external dependency requests are required.

This is an HTTP liveness probe, not a Meilisearch or osu! readiness check. Dependency outages do not change its response while the HTTP server remains responsive.

Docker healthcheck

The image checks http://127.0.0.1:3000/health every 30 seconds using Alpine's bundled wget, with a 3-second request timeout, a 5-second Docker timeout, a 60-second startup grace period and 3 consecutive failures before marking the container unhealthy.

Docker Compose disables this API-only probe for the crawler, which has no HTTP listener. When running a crawler directly from the shared image, use docker run --no-healthcheck.

curl -f 'https://mirror.example/health'

GET /metrics

Returns the current Prometheus metrics exposition generated by the HTTP metrics recorder. Metric series and labels depend on observed runtime traffic.

Parameters

No path or query parameters.

Response and statuses

200 OK — Prometheus text exposition returned as text/plain; charset=utf-8. This handler defines no route-specific error status.

Example

curl 'https://mirror.example/metrics'
# HELP <metric_name> <description>
# TYPE <metric_name> counter
<metric_name>{...} <value>

GET /docs

Serves this generated, human-readable API reference.

Parameters

No path or query parameters.

Response and statuses

200 OK — text/html; charset=utf-8. This handler defines no route-specific error status.

Example

curl 'https://mirror.example/docs'