On this page

← All documentation

Map index API

The public, read-only JSON API of the map index: chart identity, sources, chartsets, the .rox file, limits.

The map index behind the Browse page has a small public API. It is read-only, needs no account and returns JSON (the chart file excepted). Please be gentle: answers are cached, and requests are limited per address.

Chart identity: the note hash

A chart is identified by its note hash: 64 lowercase hexadecimal characters, a BLAKE3 digest of the key count and of every note (start time, column, kind, end time of a long note) in whole milliseconds, relative to the first note. The same notes give the same hash whether they come from an .osu, .sm, .ssc or .qua file, and whatever the file is called; mines and bursts are ignored; the audio and the metadata do not count. The hash version is returned as noteHashVersion. The index only holds osu!mania and Etterna charts of 4 to 7 keys.

GET /api/charts/{hash}

Where can this chart be downloaded? A malformed hash answers 400; a well-formed hash the index does not know answers 200 with status: "unknown" (so that a client can ask without error handling).

{
  "chartHash": "9f2c…64 hex…",
  "status": "known",
  "sources": [
    { "kind": "osuSet", "ref": "4001", "confidence": "verified", "checkedAtMs": 1791600000000 }
  ],
  "ttlMs": 300000,
  "chart": {
    "hash": "9f2c…", "noteHashVersion": 1, "keys": 7,
    "bpmMin": 120.0, "bpmMax": 180.0, "bpmMain": 180.0,
    "lengthMs": 144000, "notes": 1200, "lns": 40,
    "densityAvg": 8.3, "densityPeak": 14.0, "roxBytes": 5120,
    "title": "Song", "artist": "Artist", "creator": "Mapper", "difficulty": "7K Hard"
  }
}

kind is an open set (osuSet, etternaPack, ...): ignore kinds you do not know. ref is an identifier, never a URL: an osu! set id, an EtternaOnline pack id. The page of the source is https://osu.ppy.sh/beatmapsets/<ref> or https://etternaonline.com/packs/<ref>. The answer carries an ETag and Cache-Control: public, max-age=300.

GET /api/charts/{hash}/rox

The chart in the compact ROX format (the open exchange format of the game), served on request as an attachment (application/octet-stream, ETag: "<hash>", immutable). 404 if the chart is unknown or was removed. Never audio, never images.

GET /api/chartsets

The chartsets (one per song folder of an osu! set or an Etterna pack), newest first by default.

Query parameter Meaning
q text found in the title, artist, creator or tags
keys comma-separated key counts, e.g. 4,7
source osuSet or etternaPack
creator part of the creator's name
bpmMin, bpmMax BPM range overlapping the set's charts
lengthMin, lengthMax length in seconds
densityMin peak notes per second
sort updated (default), title, artist, bpm, length, density
page, perPage perPage is at most 60

The answer is { "items": [...], "total": n, "page": p, "perPage": n }; each item carries id, title, artist, creator, sourceKind, keys, bpmMin, bpmMax, lengthMinMs, lengthMaxMs, densityMax, chartCount and downloadUrl (/go/chartset/<id>, absent for a set with no public source).

GET /api/chartsets/{id}

One chartset: the fields above plus tags, sources and charts (each with its chart object, its difficulty name in this set and, for osu!, its osuBeatmapId).

GET /go/chartset/{id}

A 302 redirect to the page of the original source, where the map is downloaded. 404 for a set without a public source.

Limits and manners

  • Requests are limited per address (240 per minute by default for each of the three groups: chart lookups, .rox files, the chartset list); beyond it the answer is 429 with Retry-After.
  • Use ETag / If-None-Match; do not poll.
  • Put a way to reach you in your User-Agent.
  • Creators are credited wherever a chart is shown. To have an entry removed, use the Contact page.