MENU

Introduction

Welcome to the documentation for osu!api v2. You can use this API to get information on various circles and those who click them.

Note that while we endeavour to keep this documentation up to date, consider it a work-in-progress and note that it will likely contain errors.

If you notice any errors in the documentation or encounter problems using the API, please check for (or create if necessary) issues on GitHub. Alternatively, you can ask in #osu-web on the development discord.

Code examples are provided in the dark area to the right, you can use the tabs at the top of the page to switch between bash and javascript samples.

Terms of Use

Use the API for good. Don't overdo it. If in doubt, ask before (ab)using :). this section may expand as necessary.

Current rate limit is set at an insanely high 1200 requests per minute, with burst capability of up to 200 beyond that. If you require more, you probably fall into the above category of abuse. If you are doing more than 60 requests a minute, you should probably give peppy a yell.

Wrappers

Below is a list of some language-specific wrappers maintained by the community. Your mileage may vary when using them – please report any issues to the wrapper first before reporting back to us.

Changelog

For a full list of changes, see the Changelog on the site.

Breaking Changes

2025-04-10

2024-07-30

2024-01-23

2023-10-17

2023-09-11

2023-02-17

2023-01-05

2022-11-21

2022-11-11

2022-09-27

2022-07-06

2022-06-08

2021-10-28

2021-09-01

2021-08-11

2021-06-14

2021-06-09

2021-04-20

2021-02-25

2020-09-08

2020-08-28

2020-05-01

2020-02-18

2019-10-09

2019-07-18

Endpoint

Base URL

The base URL is: https://osu.ppy.sh/api/[version]/

API Versions

This is combined with the base endpoint to determine where requests should be sent.

Version Status
v2 current
v1 legacy api provided by the old site, will be deprecated soon

API Response Version

Sometimes, an API response need to be updated in non-backward compatible ways. In such cases, the x-api-version header is used to determine which version of the response will be returned.

Version 0 is assumed when the header is omitted.

Version Change
20220705 Score object with different set of fields.
20240529 GET /rooms will not return rooms with category daily_challenge prior to this version. Temporary, will not be supported after 2024-11-29.

Language

Language for the response is determined by Accept-Language header when specified. Specifying * or not setting the header will set the language to user configured language when accessing API as a user.

Authentication

Routes marked with the OAuth label require a valid OAuth2 token for access.

More information about applications you have registered and granted permissions to can be found here.

The API supports the following grant types:

Before you can use the osu!api, you will need to

  1. have registered an OAuth Application.
  2. acquire an access token by either:
    • authorizing users for your application;
    • requesting Client Credentials token.

Registering an OAuth application

Before you can get an OAuth token, you will need to register an OAuth application on your account settings page.

To register an OAuth application you will need to provide the:

Name Description
Application Name This is the name that will be visible to users of your application. The name of your application cannot be changed.
Application Callback URL The URL in your application where users will be sent after authorization.

The Application Callback URL is required when for using Authorization Codes. This may be left blank if you are only using Client Credentials Grants.

Your new OAuth application will have a Client ID and Client Secret; the Client Secret is like a password for your OAuth application, it should be kept private and do not share it with anyone else.

Authorization Code Grant

The flow to authorize users for your application is:

  1. Requesting authorization from users
  2. Users are redirected back to your site
  3. Your application accesses the API with the user's access token

Request authorization from a user

To obtain an access token, you must first get an authorization code that is created when a user grants permissions to your application. To request permission from the user, they should be redirected to:

Example request:
curl --request GET \ --get "https://osu.ppy.sh/oauth/authorize?client_id=1&redirect_uri=http%3A%2F%2Flocalhost%3A4000&response_type=code&scope=public+identify&state=randomval"
const url = new URL( "https://osu.ppy.sh/oauth/authorize" ); const params = { "client_id": "1", "redirect_uri": "http://localhost:4000", "response_type": "code", "scope": "public identify", "state": "randomval", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); fetch(url, { method: "GET", }).then(response => response.json());

GET https://osu.ppy.sh/oauth/authorize

User is redirected back to your site

If the user accepts your request, they will be redirected back to your site with a temporary single-use code contained in the URL parameter. If a state value was provided in the previous request, it will be returned here.

Exchange this code for an access token:

Example request:
curl --request POST \ "https://osu.ppy.sh/oauth/token" \ --header "Accept: application/json" \ --header "Content-Type: application/x-www-form-urlencoded" \ --data "client_id=1&client_secret=clientsecret&code=receivedcode&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A4000" 
const url = new URL( "https://osu.ppy.sh/oauth/token" ); const headers = { "Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded", }; let body = "client_id=1&client_secret=clientsecret&code=receivedcode&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A4000"; fetch(url, { method: "POST", headers, body, }).then(response => response.json());

Example response (200):

 { "access_token": "verylongstring", "expires_in": 86400, "refresh_token": "anotherlongstring", "token_type": "Bearer" } 

POST https://osu.ppy.sh/oauth/token

Response Format

Successful requests will be issued an access token:

Name Type Description
token_type string The type of token, this should always be Bearer.
expires_in integer The number of seconds the token will be valid for.
access_token string The access token.
refresh_token string The refresh token.

Refresh access token

Access token expires after some time as per expires_in field. Refresh the token to get new access token without going through authorization process again.

Use refresh_token received during previous access token request:

Example request:
curl --request POST \ "https://osu.ppy.sh/oauth/token" \ --header "Accept: application/json" \ --header "Content-Type: application/x-www-form-urlencoded" \ --data "client_id=1&client_secret=clientsecret&grant_type=refresh_token&refresh_token=longstring&scope=public+identify" 
const url = new URL( "https://osu.ppy.sh/oauth/token" ); const headers = { "Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded", }; let body = "client_id=1&client_secret=clientsecret&grant_type=refresh_token&refresh_token=longstring&scope=public+identify"; fetch(url, { method: "POST", headers, body, }).then(response => response.json());

Example response (200):

 { "access_token": "verylongstring", "expires_in": 86400, "refresh_token": "anotherlongstring", "token_type": "Bearer" } 

POST https://osu.ppy.sh/oauth/token

Response Format

Successful requests will be issued an access token and a new refresh token:

Name Type Description
token_type string The type of token, this should always be Bearer.
expires_in integer The number of seconds the token will be valid for.
access_token string The access token.
refresh_token string The refresh token.

Client Credentials Grant

The client credential flow provides a way for developers to get access tokens that do not have associated user permissions; as such, these tokens are considered as guest users.

Example for requesting Client Credentials token:

Example request:
curl --request POST \ "https://osu.ppy.sh/oauth/token" \ --header "Accept: application/json" \ --header "Content-Type: application/x-www-form-urlencoded" \ --data "client_id=1&client_secret=clientsecret&grant_type=client_credentials&scope=public" 
const url = new URL( "https://osu.ppy.sh/oauth/token" ); const headers = { "Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded", }; let body = "client_id=1&client_secret=clientsecret&grant_type=client_credentials&scope=public"; fetch(url, { method: "POST", headers, body, }).then(response => response.json());

Example response (200):

 { "access_token": "verylongstring", "expires_in": 86400, "token_type": "Bearer" } 

POST https://osu.ppy.sh/oauth/token

Response Format

Successful requests will be issued an access token:

Name Type Description
token_type string The type of token, this should always be Bearer.
expires_in integer The number of seconds the token will be valid for.
access_token string The access token.

Using the access token to access the API

With the access token, you can make requests to osu!api on behalf of a user.

The token should be included in the header of requests to the API.

Authorization: Bearer {{token}}

# With shell, you can just pass the correct header with each request curl "https://osu.ppy.sh/api/[version]/[endpoint]" -H "Authorization: Bearer {{token}}"
 
// This javascript example uses fetch() fetch("https://osu.ppy.sh/api/[version]/[endpoint]", { headers: { Authorization: 'Bearer {{token}}' } });

Make sure to replace {{token}} with your OAuth2 token.

Resource Owner

The Resource Owner is the user that a token acts on behalf of.

For Authorization Code Grant tokens, the Resource Owner is the user authorizing the token.

Client Credentials Grant tokens do not have a Resource Owner (i.e. is a guest user), unless they have been granted the delegate scope. The Resource Owner of tokens with the delegate scope is the owner of the OAuth Application that was granted the token.

Routes marked with requires user require the use of tokens that have a Resource Owner.

Client Credentials Delegation

Client Credentials Grant tokens may be allowed to act on behalf of the owner of the OAuth client (delegation) by requesting the delegate scope, in addition to other scopes supporting delegation. When using delegation, scopes that support delegation cannot be used together with scopes that do not support delegation. Delegation is only available to Chat Bots.

Refer to the list of Scopes for which scopes support delegation.

Scopes

The following scopes are currently supported:

Name Description Can Delegate?
chat.read

Allows read chat messages on a user's behalf.

No
chat.write

Allows sending chat messages on a user's behalf.

Yes
chat.write_manage

Allows joining and leaving chat channels on a user's behalf.

Yes
delegate

Allows acting as the owner of a client; only available for Client Credentials Grant.

Yes
forum.write

Allows creating and editing forum posts on a user's behalf.

Yes
forum.write_manage

Allows managing forum topics on a user's behalf.

Yes
friends.read

Allows reading of the user's friend list.

No
group_permissions

Allows delegate tokens to inherit the Resource Owner's group permissions in some cases.

Yes
identify

Allows reading of the public profile of the user (/me).

No
public

Allows reading of publicly available data on behalf of the user.

No

identify is the default scope for the Authorization Code Grant and always implicitly provided. The Client Credentials Grant does not currently have any default scopes.

Routes marked with lazer are intended for use by the osu!lazer client and not currently available for use with Authorization Code or Client Credentials grants.

Using any of the chat.read chat.write chat.write_manage scopes requires either

Managing OAuth applications

Your account settings page will show your registered OAuth applications, and all the OAuth applications you have granted permissions to.

Reset Client Secret

You can generate a new Client Secret by choosing to "Reset client secret", however, this will disable all access tokens issued for the application.

Account

GET api/v2/me/beatmapset-favourites

requires user OAuth identify

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/me/beatmapset-favourites" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/me/beatmapset-favourites" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /me/beatmapset-favourites

Beatmap Packs

Get Beatmap Packs

OAuth public

Returns a list of beatmap packs.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/packs?type=neque" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/packs" ); const params = { "type": "neque", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmaps/packs

Response format

Field Type
beatmap_packs BeatmapPack[]

Get Beatmap Pack

OAuth public

Gets the beatmap pack for the specified beatmap pack tag.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/packs/id?legacy_only=0" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/packs/id" ); const params = { "legacy_only": "0", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmaps/packs/{pack}

Response format

Returns BeatmapPack object. The following attributes are always included as well:

Attribute
beatmapsets
user_completion_data

Beatmaps

Lookup Beatmap

OAuth public

Returns beatmap.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/lookup?checksum=quibusdam&filename=eum&id=id" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/lookup" ); const params = { "checksum": "quibusdam", "filename": "eum", "id": "id", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 "See Beatmap object section" 

Request

GET /beatmaps/lookup

Response format

See Get Beatmap

Get a User Beatmap score

OAuth public

Return a User's score on a Beatmap

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/5/scores/users/18?legacy_only=0&mode=fugiat&mods=quis" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/5/scores/users/18" ); const params = { "legacy_only": "0", "mode": "fugiat", "mods": "quis", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmaps/{beatmap}/scores/users/{user}

Response Format

Returns BeatmapUserScore

The position returned depends on the requested mode and mods.

Get a User Beatmap scores

OAuth public

Return a User's scores on a Beatmap

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/7/scores/users/4/all?legacy_only=0&ruleset=osu" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/7/scores/users/4/all" ); const params = { "legacy_only": "0", "ruleset": "osu", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmaps/{beatmap}/scores/users/{user}/all

Response Format

Field Type
scores Score[]

Get Beatmap scores

OAuth public

Returns the top scores for a beatmap. Depending on user preferences, this may only show legacy scores.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/16/scores?legacy_only=0&mode=dolorum&mods=aut&type=qui" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/16/scores" ); const params = { "legacy_only": "0", "mode": "dolorum", "mods": "aut", "type": "qui", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmaps/{beatmap}/scores

Response Format

Returns BeatmapScores. Score object inside includes user and the included user includes country and cover.

Get Beatmap scores (non-legacy)

OAuth public

Returns the top scores for a beatmap.

This endpoint is deprecated. Use Get Beatmap scores with appropriate api version header instead.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/12/solo-scores?mode=error&mods=at&type=magni" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/12/solo-scores" ); const params = { "mode": "error", "mods": "at", "type": "magni", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmaps/{beatmap}/solo-scores

Response Format

Returns BeatmapScores. Score object inside includes user and the included user includes country and cover.

Get Beatmaps

OAuth public

Returns a list of beatmaps.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps?ids%5B%5D=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps" ); const params = { "ids[]": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "beatmaps": [ { "id": 1, // Other Beatmap attributes... } ] } 

Request

GET /beatmaps

Response format

Field Type Description
beatmaps BeatmapExtended[] Includes beatmapset (with ratings), failtimes, max_combo, and owners.

Get Beatmap

OAuth public

Gets beatmap data for the specified beatmap ID.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmaps/19" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/19" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 "See Beatmap object section." 

Request

GET /beatmaps/{beatmap}

Response format

Returns BeatmapExtended object. Following attributes are included in the response object when applicable,

Attribute Notes
beatmapset Includes ratings property.
failtimes
max_combo

Get Beatmap Attributes

OAuth public

Returns difficulty attributes of beatmap with specific mode and mods combination.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/beatmaps/2/attributes" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"mods\": 1, \"ruleset\": \"osu\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/2/attributes" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "mods": 1, "ruleset": "osu" }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Example response (200):

 { "attributes": { "max_combo": 100, ... } } 

Request

POST /beatmaps/{beatmap}/attributes

Response format

Field Type
Attributes DifficultyAttributes

Beatmapset Discussions

Get Beatmapset Discussion Posts

OAuth public

Returns the posts of beatmapset discussions.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/discussions/posts?beatmapset_discussion_id=ea&limit=14&page=18&sort=magni&types%5B%5D=perferendis&user=quidem&with_deleted=sed" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/discussions/posts" ); const params = { "beatmapset_discussion_id": "ea", "limit": "14", "page": "18", "sort": "magni", "types[]": "perferendis", "user": "quidem", "with_deleted": "sed", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/discussions/posts

Response Format

Field Type Description
beatmapsets Beatmapset
cursor_string CursorString
posts BeatmapsetDiscussionPost[]
users User

Get Beatmapset Discussion Votes

OAuth public

Returns the votes given to beatmapset discussions.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/discussions/votes?beatmapset_discussion_id=aut&limit=8&page=3&receiver=rem&score=doloribus&sort=quis&user=molestiae&with_deleted=nesciunt" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/discussions/votes" ); const params = { "beatmapset_discussion_id": "aut", "limit": "8", "page": "3", "receiver": "rem", "score": "doloribus", "sort": "quis", "user": "molestiae", "with_deleted": "nesciunt", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/discussions/votes

Response Format

Field Type Description
cursor_string CursorString
discussions BeatmapsetDiscussion
users User
votes BeatmapsetDiscussionVote[]

Get Beatmapset Discussions

OAuth public

Returns a list of beatmapset discussions.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/discussions?beatmap_id=facere&beatmapset_id=dolore&beatmapset_status=eum&limit=8&message_types%5B%5D=placeat&only_unresolved=libero&page=1&sort=officia&user=fuga&with_deleted=sint" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/discussions" ); const params = { "beatmap_id": "facere", "beatmapset_id": "dolore", "beatmapset_status": "eum", "limit": "8", "message_types[]": "placeat", "only_unresolved": "libero", "page": "1", "sort": "officia", "user": "fuga", "with_deleted": "sint", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/discussions

Response Format

Field Type Description
beatmaps BeatmapExtended[] List of beatmaps associated with the discussions returned.
cursor_string CursorString
discussions BeatmapsetDiscussion[] List of discussions according to sort order.
included_discussions BeatmapsetDiscussion[] Additional discussions related to discussions.
reviews_config.max_blocks integer Maximum number of blocks allowed in a review.
users User[] List of users associated with the discussions returned.

Beatmapsets

OAuth public

TODO: documentation

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/search" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/search" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/search

GET api/v2/beatmapsets/lookup

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/lookup" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/lookup" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/lookup

GET api/v2/beatmapsets/{beatmapset}

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/numquam" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/numquam" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/{beatmapset}

GET api/v2/beatmapsets/{beatmapset}/download

OAuth lazer

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/iusto/download" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/iusto/download" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/{beatmapset}/download

Changelog

Get Changelog Build

Returns details of the specified build.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/changelog/stable40/20210520.2" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/changelog/stable40/20210520.2" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "id": 5778, "version": "20210520.2", "display_version": "20210520.2", "users": 22093, "created_at": "2021-05-20T14:28:04+00:00", "update_stream": { "id": 5, "name": "stable40", "display_name": "Stable", "is_featured": true }, "changelog_entries": [ { "id": null, "repository": null, "github_pull_request_id": null, "github_url": null, "url": "https://osu.ppy.sh/home/news/2021-05-20-spring-fanart-contest-results", "type": "fix", "category": "Misc", "title": "Spring is here!", "message_html": "<div class='changelog-md'><p class=\"changelog-md__paragraph\">New seasonal backgrounds ahoy! Amazing work by the artists.</p>\n</div>", "major": true, "created_at": "2021-05-20T10:56:49+00:00", "github_user": { "display_name": "peppy", "github_url": null, "github_username": null, "id": null, "osu_username": "peppy", "user_id": 2, "user_url": "https://osu.ppy.sh/users/2" } } ], "versions": { "previous": { "id": 5774, "version": "20210519.3", "display_version": "20210519.3", "users": 10, "created_at": "2021-05-19T11:51:48+00:00", "update_stream": { "id": 5, "name": "stable40", "display_name": "Stable", "is_featured": true } } } } 

Request

GET /changelog/{stream}/{build}

Response Format

A Build with changelog_entries, changelog_entries.github_user, and versions included.

Get Changelog Listing

Returns a listing of update streams, builds, and changelog entries.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/changelog?message_formats%5B%5D=porro" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/changelog" ); const params = { "message_formats[]": "porro", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "streams": [ { "id": 5, "name": "stable40", "display_name": "Stable", "is_featured": true, "latest_build": { "id": 5778, "version": "20210520.2", "display_version": "20210520.2", "users": 23683, "created_at": "2021-05-20T14:28:04+00:00", "update_stream": { "id": 5, "name": "stable40", "display_name": "Stable", "is_featured": true } }, "user_count": 23965 }, // ... ], "builds": [ { "id": 5823, "version": "2021.619.1", "display_version": "2021.619.1", "users": 0, "created_at": "2021-06-19T08:30:45+00:00", "update_stream": { "id": 7, "name": "lazer", "display_name": "Lazer", "is_featured": false }, "changelog_entries": [ { "id": 12925, "repository": "ppy/osu", "github_pull_request_id": 13572, "github_url": "https://github.com/ppy/osu/pull/13572", "url": null, "type": "fix", "category": "Reliability", "title": "Fix game crashes due to attempting localisation load for unsupported locales", "message_html": null, "major": true, "created_at": "2021-06-19T08:09:39+00:00", "github_user": { "display_name": "bdach", "github_url": "https://github.com/bdach", "github_username": "bdach", "id": 218, "osu_username": null, "user_id": null, "user_url": null } } ] }, // ... ], "search": { "stream": null, "from": null, "to": null, "max_id": null, "limit": 21 } } 

Request

GET /changelog

Response Format

Field Type Notes
builds Build[] Includes changelog_entries, changelog_entries.github_user, and changelog entry message in requested formats.
search.from string? from input.
search.limit integer Always 21.
search.max_id integer? max_id input.
search.stream string? stream input.
search.to string? to input.
streams UpdateStream[] Always contains all available streams. Includes latest_build and user_count.

Lookup Changelog Build

Returns details of the specified build.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/changelog/20210520.2?message_formats%5B%5D=tenetur" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/changelog/20210520.2" ); const params = { "message_formats[]": "tenetur", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 See "Get Changelog Build" response. 

Request

GET /changelog/{changelog}

Response Format

See Get Changelog Build.

Chat

Chat Keepalive

requires user OAuth chat.read

Request periodically to reset chat activity timeout. Also returns an updated list of recent silences.

See Public channels and activity timeout

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/chat/ack?history_since=15" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/ack" ); const params = { "history_since": "15", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /chat/ack

Response Format

Field Type
silences UserSilence[]

Create New PM

requires user OAuth chat.write

This endpoint allows you to create a new PM channel.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/chat/new" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"target_id\": 11, \"message\": \"ut\", \"is_action\": true, \"uuid\": \"some-uuid-string\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/chat/new" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "target_id": 11, "message": "ut", "is_action": true, "uuid": "some-uuid-string" }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Example response (200):

 { "channel": { "channel_id": 1234, "description": "", "icon": "https://a.ppy.sh/102?1500537068" "message_length_limit": 450, "moderated": false, "name": "peppy", "type": "PM", "uuid": null, "last_message_id": 9150005005, "users": [ 101, 102 ] }, "message": { "channel_id": 1234, "content": "i can haz featured artist plz?", "is_action": false, "message_id": 9150005005, "sender_id": 102, "timestamp": "2024-12-23T01:23:45+00:00", "type": "plain", "uuid": "some-uuid-string", "sender": { "avatar_url": "https://a.ppy.sh/102?1500537068", "country_code": "AU", "default_group": "default", "id": 102, "is_active": true, "is_bot": false, "is_deleted": false, "is_online": true, "is_supporter": true "last_visit": "2024-12-23T01:23:45+00:00", "pm_friends_only": false, "profile_colour": "#333333", "username": "nekodex", } }, "new_channel_id": 1234, } 

Request

POST /chat/new

Response Format

Field Type
channel The new ChatChannel
message the sent ChatMessage
new_channel_id Deprecated; channel_id of newly created ChatChannel

Get Updates

requires user OAuth lazer

Returns the list of channels the current User is in along with an updated list of UserSilences.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/chat/updates?history_since=19" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/updates" ); const params = { "history_since": "19", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "presence": [ { "channel_id": 5, "current_user_attributes": { "can_message": true, "can_message_error": null, "last_read_id": 9150005005 }, "name": "#osu", "description": "The official osu! channel (english only).", "type": "public", "last_read_id": 9150005005, "last_message_id": 9150005005 }, { "channel_id": 12345, "current_user_attributes": { "can_message": true, "can_message_error": null, "last_read_id": 9150001235 }, "type": "PM", "name": "peppy", "icon": "https://a.ppy.sh/2?1519081077.png", "users": [ 2, 102 ], "last_read_id": 9150001235, "last_message_id": 9150001234 } ], "silences": [ { "id": 1, "user_id": 2 } ] } 

Request

GET /chat/updates

Response Format

Field Type
messages This field is not used and will be removed.
presence ChatChannel[]?
silences UserSilence[]?

Get Channel Messages

requires user OAuth chat.read

This endpoint returns the chat messages for a specific channel.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/chat/channels/20/messages?limit=13&since=11&until=4" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels/20/messages" ); const params = { "limit": "13", "since": "11", "until": "4", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 [ { "message_id": 9150005004, "sender_id": 2, "channel_id": 5, "timestamp": "2018-07-06T06:33:34+00:00", "content": "i am a lazerface", "is_action": 0, "sender": { "id": 2, "username": "peppy", "profile_colour": "#3366FF", "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "is_active": true, "is_bot": false, "is_online": true, "is_supporter": true } }, { "message_id": 9150005005, "sender_id": 102, "channel_id": 5, "timestamp": "2018-07-06T06:33:42+00:00", "content": "uh ok then", "is_action": 0, "sender": { "id": 102, "username": "nekodex", "profile_colour": "#333333", "avatar_url": "https://a.ppy.sh/102?1500537068", "country_code": "AU", "is_active": true, "is_bot": false, "is_online": true, "is_supporter": true } } ] 

Request

GET /chat/channels/{channel}/messages

Response Format

Returns an array of ChatMessage

Send Message to Channel

requires user OAuth chat.write

This endpoint returns the chat messages for a specific channel.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/chat/channels/8/messages" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"message\": \"fugit\", \"is_action\": false }" 
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels/8/messages" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "message": "fugit", "is_action": false }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Example response (200):

 { "message_id": 9150005004, "sender_id": 2, "channel_id": 5, "timestamp": "2018-07-06T06:33:34+00:00", "content": "i am a lazerface", "is_action": 0, "sender": { "id": 2, "username": "peppy", "profile_colour": "#3366FF", "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "is_active": true, "is_bot": false, "is_online": true, "is_supporter": true } } 

Request

POST /chat/channels/{channel}/messages

Response Format

The sent ChatMessage

Join Channel

requires user OAuth chat.write_manage

This endpoint allows you to join a public or multiplayer channel.

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/chat/channels/at/users/similique" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels/at/users/similique" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Example response (200):

 { "channel_id": 5, "current_user_attributes": { "can_message": true, "can_message_error": null }, "description": "The official osu! channel (english only).", "icon": "https://a.ppy.sh/2?1519081077.png", "last_message_id": 1029, "moderated": false, "name": "#osu", "type": "public" "users": [] } 

Request

PUT /chat/channels/{channel}/users/{user}

Response Format

Returns the joined ChatChannel.

Leave Channel

requires user OAuth chat.write_manage

This endpoint allows you to leave a public channel.

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/chat/channels/fugit/users/hic" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels/fugit/users/hic" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

DELETE /chat/channels/{channel}/users/{user}

Response Format

empty response

Mark Channel as Read

requires user OAuth chat.read

This endpoint marks the channel as having being read up to the given message_id.

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/chat/channels/sunt/mark-as-read/saepe?channel_id=molestiae&message_id=quo" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels/sunt/mark-as-read/saepe" ); const params = { "channel_id": "molestiae", "message_id": "quo", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

PUT /chat/channels/{channel}/mark-as-read/{message}

Response Format

empty response

Get Channel List

requires user OAuth chat.read

This endpoint returns a list of all joinable public channels.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/chat/channels" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 [ { "channel_id": 5, "description": "The official osu! channel (english only).", "icon": "https://a.ppy.sh/2?1519081077.png", "moderated": false, "name": "#osu", "type": "public" } ] 

Request

GET /chat/channels

Response Format

Returns an array of ChatChannel

Create Channel

requires user OAuth chat.write_manage

Creates a new PM or announcement channel. Rejoins the PM channel if it already exists.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/chat/channels" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"target_id\": 2, \"type\": \"PM\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "target_id": 2, "type": "PM" }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Example response (200):

 { "channel_id": 1, "description": "best channel", "icon": "https://a.ppy.sh/2?1519081077.png", "moderated": false, "name": "#pm_1-2", "type": "PM", "recent_messages": [ { "message_id": 1, "sender_id": 1, "channel_id": 1, "timestamp": "2020-01-01T00:00:00+00:00", "content": "Happy new year", "is_action": false, "sender": { "id": 2, "username": "peppy", "profile_colour": "#3366FF", "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "is_active": true, "is_bot": false, "is_online": true, "is_supporter": true } } ] } 

Request

POST /chat/channels

Response Format

Returns ChatChannel with recent_messages attribute; recent_messages is deprecated and should not be used.

Get Channel

requires user OAuth chat.read

Gets details of a chat channel.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/chat/channels/quia" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/channels/quia" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "channel": { "channel_id": 1337, "current_user_attributes": { "can_message": true, "can_message_error": null }, "name": "test channel", "description": "wheeeee", "icon": "/images/layout/avatar-guest@2x.png", "type": "PM", "last_message_id": 9150005005, "moderated": false, "users": [ 2, 102 ] }, "users": [ { "id": 2, "username": "peppy", "profile_colour": "#3366FF", "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "is_active": true, "is_bot": false, "is_deleted": false, "is_online": true, "is_supporter": true }, { "id": 102, "username": "lambchop", "profile_colour": "#3366FF", "icon": "/images/layout/avatar-guest@2x.png", "country_code": "NZ", "is_active": true, "is_bot": false, "is_deleted": false, "is_online": false, "is_supporter": false } ] } 

Request

GET /chat/channels/{channel}

Response Format

Field Type Description
channel ChatChannel
users User Users are only visible for PM channels.

Comments

Get Comments

Returns a list comments and their replies up to 2 levels deep.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/comments?commentable_type=beatmapset&commentable_id=1&parent_id=1&sort=new" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments" ); const params = { "commentable_type": "beatmapset", "commentable_id": "1", "parent_id": "1", "sort": "new", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /comments

Response Format

Returns CommentBundle.

pinned_comments is only included when commentable_type and commentable_id are specified.

Post a new comment

requires user OAuth lazer

Posts a new comment to a comment thread.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/comments" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /comments

Response Format

Returns CommentBundle

Get a Comment

Gets a comment and its replies up to 2 levels deep.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/comments/architecto" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments/architecto" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /comments/{comment}

Response Format

Returns CommentBundle

Edit Comment

requires user OAuth lazer

Edit an existing comment.

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/comments/pariatur" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments/pariatur" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Request

PUT /comments/{comment}

PATCH /comments/{comment}

Response Format

Returns CommentBundle

Delete Comment

requires user OAuth lazer

Deletes the specified comment.

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/comments/et" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments/et" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /comments/{comment}

Response Format

Returns CommentBundle

Add Comment vote

requires user OAuth lazer

Upvotes a comment.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/comments/atque/vote" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments/atque/vote" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /comments/{comment}/vote

Response Format

Returns CommentBundle

Remove Comment vote

requires user OAuth lazer

Un-upvotes a comment.

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/comments/earum/vote" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/comments/earum/vote" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /comments/{comment}/vote

Response Format

Returns CommentBundle

Events

Get Events

OAuth public

Returns a collection of Events in order of creation time.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/events" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/events" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { events: [ { created_at: "2022-12-08T02:02:51+00:00", id: 57, type: "achievement", achievement: { ... }, user: { ... } }, ... ], cursor_string: "eyJldmVudF9pZCI6OH0" } 

Request

GET /events

Response Format

Field Type
cursor_string CursorString
events Event[]

Forum

Lock Topic

requires user OAuth forum.write_manage

Locks a Topic

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/forums/topics/12/lock?lock=" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics/12/lock" ); const params = { "lock": "0", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

POST /forums/topics/{topic}/lock

Response Format

empty response

POST api/v2/forums/topics/{topic}/move

OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/forums/topics/voluptates/move" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics/voluptates/move" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /forums/topics/{topic}/move

Pin Topic

requires user OAuth forum.write_manage

Pins a Topic

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/forums/topics/11/pin?pin=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics/11/pin" ); const params = { "pin": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

POST /forums/topics/{topic}/pin

Response Format

empty response

Reply Topic

requires user OAuth forum.write

Create a post replying to the specified topic.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/forums/topics/1/reply" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"body\": \"hello\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics/1/reply" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "body": "hello" }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Request

POST /forums/topics/{topic}/reply

Response Format

ForumPost with body included.

Get Topic Listing

OAuth public

Get a sorted list of topics, optionally from a specific forum

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/forums/topics" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "topics": [ { "id": 1, "...": "..." }, { "id": 2, "...": "..." } ], "cursor_string": "eyJoZWxsbyI6IndvcmxkIn0" } 

Request

GET /forums/topics

Response Format

Field Type Notes
topics ForumTopic[]
cursor_string CursorString

Create Topic

requires user OAuth forum.write

Create a new topic.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/forums/topics" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"body\": \"hello\", \"forum_id\": 1, \"title\": \"untitled\", \"with_poll\": true, \"forum_topic_poll[options]\": \"item A...\", \"forum_topic_poll[title]\": \"my poll\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "body": "hello", "forum_id": 1, "title": "untitled", "with_poll": true, "forum_topic_poll[options]": "item A...", "forum_topic_poll[title]": "my poll" }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Request

POST /forums/topics

Response Format

Field Type Includes
topic ForumTopic
post ForumPost body

Get Topic and Posts

OAuth public

Get topic and its posts.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/forums/topics/1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics/1" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "topic": { "id": 1, "...": "..." }, "posts": [ { "id": 1, "...": "..." }, { "id": 2, "...": "..." } ], "cursor_string": "eyJoZWxsbyI6IndvcmxkIn0", "sort": "id_asc" } 

Request

GET /forums/topics/{topic}

Response Format

Field Type Notes
cursor_string CursorString
posts ForumPost[] Includes body.
search Parameters used for current request excluding cursor.
topic ForumTopic

Edit Topic

OAuth forum.write

Edit topic. Only title can be edited through this endpoint.

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/forums/topics/1" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"forum_topic[topic_title]\": \"titled\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/forums/topics/1" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "forum_topic[topic_title]": "titled" }; fetch(url, { method: "PUT", headers, body: JSON.stringify(body), }).then(response => response.json());

Request

PUT /forums/topics/{topic}

PATCH /forums/topics/{topic}

Response Format

The edited ForumTopic.

Edit Post

OAuth forum.write

Edit specified forum post.

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/forums/posts/1" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"body\": \"hello\" }" 
const url = new URL( "https://osu.ppy.sh/api/v2/forums/posts/1" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "body": "hello" }; fetch(url, { method: "PUT", headers, body: JSON.stringify(body), }).then(response => response.json());

Request

PUT /forums/posts/{post}

PATCH /forums/posts/{post}

Response Format

ForumPost with body included.

Get Forum Listing

OAuth public

Get top-level forums and their subforums (max 2 deep).

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/forums" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "forums": [ { "forum_id": 1, "...": "..." }, { "forum_id": 2, "...": "..." } ] } 

Request

GET /forums

Response Format

Field Type
forums Forum[]

Get Forum and Topics

OAuth public

Get a forum by id, its pinned topics, recent topics, and its subforums.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/forums/1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/forums/1" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "forum": { "id": 1, "...": "..." }, "topics": [ { "id": 1, "...": "..." }, { "id": 2, "...": "..." }, ], "pinned_topics": [ { "id": 1, "...": "..." }, { "id": 2, "...": "..." }, ] } 

Request

GET /forums/{forum}

Response Format

Field Type
forum Forum
topics ForumTopic[]
pinned_topics ForumTopic[]

Home

OAuth public

Searches users and wiki pages.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/search?mode=all&query=hello&page=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/search" ); const params = { "mode": "all", "query": "hello", "page": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /search

Response Format

Field Type Description
user SearchResult<User>? For all or user mode. Only first 100 results are accessible
wiki_page SearchResult<WikiPage>? For all or wiki_page mode

SearchResult<T>

Field Type Description
data T[]
total integer

Matches

Get Matches Listing

OAuth public

Returns a list of matches.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/matches" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/matches" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "matches": [ { "id": 114428685, "start_time": "2024-06-25T00:55:30+00:00", "end_time": null, "name": "peppy's game" }, // ... ], "params": { "limit": 50, "sort": "id_desc", "active": null }, "cursor": { "match_id": 114428685 }, "cursor_string": "eyJtYXRjaF9pZCI6MTE0NDI4Njg1fQ" } 

Request

GET /matches

Response Format

Field Type Notes
cursor Cursor
cursor_string CursorString
matches Match[]
params.limit integer
params.sort string
params.active boolean?

Get Match

OAuth public

Returns details of the specified match.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/matches/" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/matches/" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "match": { "id": 16155689, "start_time": "2015-05-16T09:44:51+00:00", "end_time": "2015-05-16T10:55:08+00:00", "name": "CWC 2015: (Australia) vs (Poland)" }, "events": [ { "id": 484385927, "detail": { "type": "match-created" }, "timestamp": "2015-05-16T09:44:51+00:00", "user_id": null }, // ... ], "users": [], "first_event_id": 484385927, "latest_event_id": 484410607, "current_game_id": null } 

Request

GET /matches/{match}

Response Format

Field Type Notes
match Match
events MatchEvent[]
users User[] Includes country.
first_event_id integer ID of the first MatchEvent in the match.
latest_event_id integer ID of the lastest MatchEvent in the match.

Multiplayer

Get User High Score

requires user OAuth lazer

Returns detail of highest score of specified user and the surrounding scores.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms/5/playlist/10/scores/users/20" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/5/playlist/10/scores/users/20" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms/{room}/playlist/{playlist}/scores/users/{user}

Response Format

Returns Score object.

Get Scores

OAuth public

Returns a list of scores for specified playlist item.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms/9/playlist/3/scores?limit=16&sort=eaque" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/9/playlist/3/scores" ); const params = { "limit": "16", "sort": "eaque", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms/{room}/playlist/{playlist}/scores

Response Format

Returns MultiplayerScores object.

Get a Score

requires user OAuth lazer

Returns detail of specified score and the surrounding scores.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms/1/playlist/12/scores/10" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/1/playlist/12/scores/10" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms/{room}/playlist/{playlist}/scores/{score}

Response Format

Returns Score object.

Get Multiplayer Rooms

OAuth public

Returns a list of multiplayer rooms.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms

News

Get News Listing

Returns a list of news posts and related metadata.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/news" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/news" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "news_posts": [ { "id": 964, "author": "RockRoller", "edit_url": "https://github.com/ppy/osu-wiki/tree/master/news/2021-05-27-skinning-contest-results.md", "first_image": "https://i.ppy.sh/d431ff921955d5c8792dc9bae40ac082d4e53131/68747470733a2f2f6f73752e7070792e73682f77696b692f696d616765732f7368617265642f6e6577732f323032312d30352d32372d736b696e6e696e672d636f6e746573742d726573756c74732f736b696e6e696e675f636f6e746573745f62616e6e65722e6a7067", "published_at": "2021-05-27T12:00:00+00:00", "updated_at": "2021-05-28T17:11:35+00:00", "slug": "2021-05-27-skinning-contest-results", "title": "Skinning Contest: Results Out", "preview": "The ship full of skins is now back with your votes. Check out the results for our first-ever official skinning contest right here!" }, // ... ], "news_sidebar": { "current_year": 2021, "news_posts": [ { "id": 964, "author": "RockRoller", "edit_url": "https://github.com/ppy/osu-wiki/tree/master/news/2021-05-27-skinning-contest-results.md", "first_image": "https://i.ppy.sh/d431ff921955d5c8792dc9bae40ac082d4e53131/68747470733a2f2f6f73752e7070792e73682f77696b692f696d616765732f7368617265642f6e6577732f323032312d30352d32372d736b696e6e696e672d636f6e746573742d726573756c74732f736b696e6e696e675f636f6e746573745f62616e6e65722e6a7067", "published_at": "2021-05-27T12:00:00+00:00", "updated_at": "2021-05-28T17:11:35+00:00", "slug": "2021-05-27-skinning-contest-results", "title": "Skinning Contest: Results Out" }, // ... ], "years": [2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013] }, "search": { "limit": 12, "sort": "published_desc" }, "cursor_string": "WyJodHRwczpcL1wvd3d3LnlvdXR1YmUuY29tXC93YXRjaD92PWRRdzR3OVdnWGNRIl0" } 

Request

GET /news

Response Format

Field Type Notes
cursor_string CursorString
news_posts NewsPost[] Includes preview.
news_sidebar.current_year integer Year of the first post's publish time, or current year if no posts returned.
news_sidebar.news_posts NewsPost[] All posts published during current_year.
news_sidebar.years integer[] All years during which posts have been published.
search.limit integer Clamped limit input.
search.sort string Always published_desc.

Get News Post

Returns details of the specified news post.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/news/2021-04-27-results-a-labour-of-love" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/news/2021-04-27-results-a-labour-of-love" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "id": 943, "author": "pishifat", "edit_url": "https://github.com/ppy/osu-wiki/tree/master/news/2021-04-27-results-a-labour-of-love.md", "first_image": "https://i.ppy.sh/65c9c2eb2f8d9bc6008b95aba7d0ef45e1414c1e/68747470733a2f2f6f73752e7070792e73682f77696b692f696d616765732f7368617265642f6e6577732f323032302d31312d33302d612d6c61626f75722d6f662d6c6f76652f616c6f6c5f636f7665722e6a7067", "published_at": "2021-04-27T20:00:00+00:00", "updated_at": "2021-04-27T20:25:57+00:00", "slug": "2021-04-27-results-a-labour-of-love", "title": "Results - A Labour of Love", "content": "<div class='osu-md osu-md--news'>...</div>", "navigation": { "newer": { "id": 944, "author": "pishifat", "edit_url": "https://github.com/ppy/osu-wiki/tree/master/news/2021-04-28-new-featured-artist-emilles-moonlight-serenade.md", "first_image": "https://i.ppy.sh/7e22cc5f4755c21574d999d8ce3a2f40a3268e84/68747470733a2f2f6173736574732e7070792e73682f617274697374732f3136302f6865616465722e6a7067", "published_at": "2021-04-28T08:00:00+00:00", "updated_at": "2021-04-28T09:51:28+00:00", "slug": "2021-04-28-new-featured-artist-emilles-moonlight-serenade", "title": "New Featured Artist: Emille's Moonlight Serenade" }, "older": { "id": 942, "author": "pishifat", "edit_url": "https://github.com/ppy/osu-wiki/tree/master/news/2021-04-24-new-featured-artist-grynpyret.md", "first_image": "https://i.ppy.sh/acdce813b71371b95e8240f9249c916285fdc5a0/68747470733a2f2f6173736574732e7070792e73682f617274697374732f3135392f6865616465722e6a7067", "published_at": "2021-04-24T08:00:00+00:00", "updated_at": "2021-04-24T10:23:59+00:00", "slug": "2021-04-24-new-featured-artist-grynpyret", "title": "New Featured Artist: Grynpyret" } } } 

Request

GET /news/{news}

Response Format

Returns a NewsPost with content and navigation included.

Notification

Get Notifications

requires user OAuth lazer

This endpoint returns a list of the user's unread notifications. Sorted descending by id with limit of 50.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/notifications?max_id=alias" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/notifications" ); const params = { "max_id": "alias", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "has_more": true, "notifications": [ { "id": 1, "name": "forum_topic_reply", "created_at": "2019-04-24T07:12:43+00:00", "object_type": "forum_topic", "object_id": 1, "source_user_id": 1, "is_read": false, "details": { "title": "A topic", "post_id": 2, "username": "User", "cover_url": "https://..." } } ], "unread_count": 100, "notification_endpoint": "wss://notify.ppy.sh" } 

Request

GET /notifications

Response Format

Returns an object containing Notification and other related attributes.

Field Type
has_more boolean whether or not there are more notifications
notifications array of Notification
unread_count total unread notifications
notification_endpoint url to connect to websocket server

Mark Notifications as Read

requires user OAuth lazer

This endpoint allows you to mark notifications read.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/notifications/mark-read" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ 
const url = new URL( "https://osu.ppy.sh/api/v2/notifications/mark-read" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

POST /notifications/mark-read

Response Format

empty response

OAuth Tokens

Revoke current token

OAuth

Revokes currently authenticated token.

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/oauth/tokens/current" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/oauth/tokens/current" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

DELETE /oauth/tokens/current

Ranking

Get Kudosu Ranking

OAuth public

Gets the kudosu ranking.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rankings/kudosu?page=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rankings/kudosu" ); const params = { "page": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rankings/kudosu

Response format

Field Type Description
ranking User[] Includes kudosu.

Get Ranking

OAuth public

Gets the current ranking for the specified type and game mode.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rankings/expedita/global?country=JP&filter=all&variant=4k" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rankings/expedita/global" ); const params = { "country": "JP", "filter": "all", "variant": "4k", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rankings/{mode}/{type}

Response Format

Returns Rankings

Get Spotlights

OAuth public

Gets the list of spotlights.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/spotlights" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/spotlights" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /spotlights

Response Format

Returns Spotlights

Scores

Get Scores

OAuth public

Returns all passed scores. Up to 1000 scores will be returned in order of oldest to latest. Most recent scores will be returned if cursor_string parameter is not specified.

Obtaining new scores that arrived after the last request can be done by passing cursor_string parameter from the previous request.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/scores?ruleset=eos&cursor_string=rerum" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/scores" ); const params = { "ruleset": "eos", "cursor_string": "rerum", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /scores

Response Format

Field Type Notes
scores Score[]
cursor_string CursorString Same value as the request will be returned if there's no new scores

Reorder Pinned Score

requires user OAuth lazer

Changes pinned position of a score on the user's profile.

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/score-pins/19/reorder" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data "{ \"after_score_id\": 14 }" 
const url = new URL( "https://osu.ppy.sh/api/v2/score-pins/19/reorder" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; let body = { "after_score_id": 14 }; fetch(url, { method: "POST", headers, body: JSON.stringify(body), }).then(response => response.json());

Example response (204):

[Empty response] 

Request

POST /score-pins/{score}/reorder

Response Format

empty response

Unpin Score

requires user OAuth lazer

Unpins a score from the user's profile.

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/score-pins/16" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/score-pins/16" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

DELETE /score-pins/{score}

Response Format

empty response

Pin Score

requires user OAuth lazer

Pins a score to the user's profile.

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/score-pins/18" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/score-pins/18" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Example response (204):

[Empty response] 

Request

PUT /score-pins/{score}

Response Format

empty response

Undocumented

POST api/v2/session/verify

requires user OAuth

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/session/verify" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/session/verify" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /session/verify

POST api/v2/session/verify/mail-fallback

requires user OAuth

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/session/verify/mail-fallback" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/session/verify/mail-fallback" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /session/verify/mail-fallback

POST api/v2/session/verify/reissue

requires user OAuth

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/session/verify/reissue" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/session/verify/reissue" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /session/verify/reissue

POST api/v2/beatmaps/{beatmap}/solo/scores

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/beatmaps/nulla/solo/scores" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/nulla/solo/scores" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /beatmaps/{beatmap}/solo/scores

PUT api/v2/beatmaps/{beatmap}/solo/scores/{token}

requires user OAuth lazer

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/beatmaps/saepe/solo/scores/quo" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/saepe/solo/scores/quo" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Request

PUT /beatmaps/{beatmap}/solo/scores/{token}

PUT api/v2/beatmaps/{beatmap}/tags/{tag}

requires user OAuth lazer

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/beatmaps/ex/tags/dolores" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/ex/tags/dolores" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Request

PUT /beatmaps/{beatmap}/tags/{tag}

PATCH /beatmaps/{beatmap}/tags/{tag}

DELETE api/v2/beatmaps/{beatmap}/tags/{tag}

requires user OAuth lazer

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/beatmaps/recusandae/tags/placeat" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmaps/recusandae/tags/placeat" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /beatmaps/{beatmap}/tags/{tag}

GET api/v2/beatmapsets/events

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/beatmapsets/events" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/events" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /beatmapsets/events

POST api/v2/beatmapsets/{beatmapset}/favourites

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/beatmapsets/aut/favourites" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/beatmapsets/aut/favourites" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /beatmapsets/{beatmapset}/favourites

GET api/v2/blocks

requires user OAuth lazer

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/blocks" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/blocks" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /blocks

POST api/v2/blocks

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/blocks" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/blocks" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /blocks

DELETE api/v2/blocks/{block}

requires user OAuth lazer

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/blocks/possimus" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/blocks/possimus" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /blocks/{block}

GET api/v2/chat/presence

requires user OAuth lazer

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/chat/presence" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/chat/presence" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /chat/presence

POST api/v2/reports

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/reports" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/reports" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /reports

PUT api/v2/rooms/{room}/users/{user}

requires user OAuth lazer

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/rooms/tenetur/users/eum" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/tenetur/users/eum" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Request

PUT /rooms/{room}/users/{user}

DELETE api/v2/rooms/{room}/users/{user}

requires user OAuth lazer

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/rooms/in/users/vitae" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/in/users/vitae" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /rooms/{room}/users/{user}

GET api/v2/rooms/{room}/leaderboard

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms/dolores/leaderboard" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/dolores/leaderboard" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms/{room}/leaderboard

GET api/v2/rooms/{room}/events

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms/officia/events" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/officia/events" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms/{room}/events

POST api/v2/rooms/{room}/playlist/{playlist}/scores

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/rooms/a/playlist/qui/scores" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/a/playlist/qui/scores" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /rooms/{room}/playlist/{playlist}/scores

PUT api/v2/rooms/{room}/playlist/{playlist}/scores/{score}

requires user OAuth lazer

Example request:
curl --request PUT \ "https://osu.ppy.sh/api/v2/rooms/sunt/playlist/beatae/scores/architecto" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/sunt/playlist/beatae/scores/architecto" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "PUT", headers, }).then(response => response.json());

Request

PUT /rooms/{room}/playlist/{playlist}/scores/{score}

PATCH /rooms/{room}/playlist/{playlist}/scores/{score}

POST api/v2/rooms

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/rooms" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /rooms

GET api/v2/rooms/{room}

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/rooms/quia" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/quia" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /rooms/{room}

DELETE api/v2/rooms/{room}

requires user OAuth lazer

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/rooms/dolor" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/rooms/dolor" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /rooms/{room}

GET api/v2/seasonal-backgrounds

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/seasonal-backgrounds" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/seasonal-backgrounds" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /seasonal-backgrounds

GET api/v2/scores/{score}/download

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/scores/nulla/download" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/scores/nulla/download" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /scores/{score}/download

GET api/v2/scores/{rulesetOrScore}/{score}/download

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/scores/porro/harum/download" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/scores/porro/harum/download" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /scores/{rulesetOrScore}/{score}/download

GET api/v2/scores/{rulesetOrScore}/{score?}

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/scores/nobis/quod" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/scores/nobis/quod" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /scores/{rulesetOrScore}/{score?}

GET api/v2/users/lookup

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/lookup" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/lookup" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /users/lookup

GET api/v2/friends

requires user OAuth friends.read

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/friends" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/friends" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /friends

POST api/v2/friends

requires user OAuth lazer

Example request:
curl --request POST \ "https://osu.ppy.sh/api/v2/friends" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/friends" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "POST", headers, }).then(response => response.json());

Request

POST /friends

DELETE api/v2/friends/{friend}

requires user OAuth lazer

Example request:
curl --request DELETE \ "https://osu.ppy.sh/api/v2/friends/cum" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/friends/cum" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "DELETE", headers, }).then(response => response.json());

Request

DELETE /friends/{friend}

GET api/v2/me/download-quota-check

requires user OAuth lazer

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/me/download-quota-check" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/me/download-quota-check" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /me/download-quota-check

GET api/v2/tags

OAuth public

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/tags" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/tags" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /tags

Users

Get User Kudosu

OAuth public

Returns kudosu history.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/1/kudosu?limit=20&offset=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/1/kudosu" ); const params = { "limit": "20", "offset": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 [ { "id": 1, "other": "attributes..." }, { "id": 2, "other": "attributes..." } ] 

Request

GET /users/{user}/kudosu

Response format

Array of KudosuHistory.

Get User Scores

OAuth public

This endpoint returns the scores of specified user.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/1/scores/best?legacy_only=0&include_fails=0&mode=osu&limit=9&offset=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/1/scores/best" ); const params = { "legacy_only": "0", "include_fails": "0", "mode": "osu", "limit": "9", "offset": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 [ { "id": 1, "other": "attributes..." }, { "id": 2, "other": "attributes..." } ] 

Request

GET /users/{user}/scores/{type}

Response format

Array of Score. Following attributes are included in the response object when applicable.

Attribute Notes
beatmap
beatmapset
weight Only for type best.

Get User Beatmaps

OAuth public

Returns the beatmaps of specified user.

Type Notes
favourite
graveyard
guest
loved
most_played
nominated
pending Previously unranked
ranked Previously ranked_and_approved
Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/1/beatmapsets/favourite?limit=11&offset=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/1/beatmapsets/favourite" ); const params = { "limit": "11", "offset": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 [ { "id": 1, "other": "attributes..." }, { "id": 2, "other": "attributes..." } ] 

Request

GET /users/{user}/beatmapsets/{type}

Response format

Array of BeatmapPlaycount when type is most_played; array of BeatmapsetExtended, otherwise.

Get User Recent Activity

OAuth public

Returns recent activity.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/1/recent_activity?limit=18&offset=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/1/recent_activity" ); const params = { "limit": "18", "offset": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 [ { "id": 1, "other": "attributes..." }, { "id": 2, "other": "attributes..." } ] 

Request

GET /users/{user}/recent_activity

Response format

Array of Event.

Search Beatmaps Passed

OAuth public

Searches for the Beatmaps a User has passed by Beatmapset.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/20/beatmaps-passed?beatmapset_ids[]=1&beatmapset_ids[]=2" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/20/beatmaps-passed" ); const params = { "beatmapset_ids[0]": "1", "beatmapset_ids[1]": "2", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /users/{user}/beatmaps-passed

Response format

Returns the list of Beatmaps completed matching the criteria.

Field Type
beatmaps_passed Beatmap[]

Get User

OAuth public

This endpoint returns the detail of specified user.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users/1/osu?key=qui" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users/1/osu" ); const params = { "key": "qui", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 "See User object section" 

Request

GET /users/{user}/{mode?}

Response format

Returns UserExtended object. The following optional attributes on User are included:

Get Users

OAuth public

Returns list of users.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/users?ids%5B%5D=1" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/users" ); const params = { "ids[]": "1", }; Object.keys(params) .forEach(key => url.searchParams.append(key, params[key])); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 { "users": [ { "id": 1, "other": "attributes..." }, { "id": 2, "other": "attributes..." } ] } 

Request

GET /users

Response format

Field Type Description
users User[] Includes country, cover, groups, and statistics_rulesets.

Get Own Data

requires user OAuth identify

Similar to Get User but with authenticated user (token owner) as user id.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/me/osu" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/me/osu" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Example response (200):

 "See User object section" 

Request

GET /me/{mode?}

Response format

See Get User.

session_verified attribute is included. Additionally, statistics_rulesets is included, containing statistics for all rulesets.

Wiki

Get Wiki Page

The wiki article or image data.

Example request:
curl --request GET \ --get "https://osu.ppy.sh/api/v2/wiki/en/Welcome" \ --header "Content-Type: application/json" \ --header "Accept: application/json"
const url = new URL( "https://osu.ppy.sh/api/v2/wiki/en/Welcome" ); const headers = { "Content-Type": "application/json", "Accept": "application/json", }; fetch(url, { method: "GET", headers, }).then(response => response.json());

Request

GET /wiki/{locale}/{path}

Response Format

Returns WikiPage.

Using Chat

TODO: better title

Chat consists of HTTP-based and websocket-based APIs.

The Chat websocket API allows receiving updates in real-time; this requires a connection to the Notification Websocket. Sending messages is still performed through the HTTP-based API.

To begin receiving chat messages, clients should send the chat.start event across the socket connection. To stop receiving chat messages, send chat.end.

Public channels and activity timeout

To continue receiving chat messages in PUBLIC channels, clients must peridiocally request the Chat Keepalive endpoint to remain active; 30 seconds is a reasonable interval. When a client is no longer considered active, the server will stop sending messages in public channels to the client.

Private messages are not affected by this activity timeout.

Getting the user's channel list

TODO: update default parameter

To get the list of channels the user is in, make a request to Get Updates with the presence as part of the includes parameter. e.g. GET /chat/updates?includes[]=presence

Creating a channel

Make a request to the Create Channel endpoint.

Only PM and ANNOUNCE type channels may be created. Creating a channel will automatically join it. Re-creating a PM channel will simply rejoin the existing channel.

Joining a channel

Make a request to the Join Channel endpoint where channel is the channel_id.

A chat.channel.join event is sent over the websocket when the user joins a channel.

Leaving a channel

Make a request to the Leave Channel endpoint.

Leaving a channel will remove it from the User's channel list.

A chat.channel.part event is sent over the websocket when the user leaves a channel.

Sending messages

Channels should be joined or created before messages are sent to them. To send a message to a channel, make a request to the Send Message to Channel endpoint.

A chat.message.new event is sent over the websocket when the user receives a message.

Getting info about a channel

Make a request to the Get Channel endpoint.

Getting channel message history

Make a request to the Get Channel Messages endpoint.

Websocket

Connection

wscat -c "{notification_endpoint}" -H "Authorization: Bearer {{token}}"
// Requires nodejs with using ESM. // Browser WebSocket does not support header option. import WebSocket from 'ws'; const url = 'notification-endpoint'; const token = 'some-token'; const headers = { Authorization: `Bearer ${token}`}; const ws = new WebSocket(url, [], { headers }); ws.on('message', (buffer) => console.log(buffer.toString()));

The above command will wait and display new notifications as they arrive

This endpoint allows you to receive notifications and chat events without constantly polling the server.

See Websocket Events for the structure of websocket messages.

Websocket Events

Websocket events generally have the following standard format:

{ "data": {}, "event": "some.event" }
Field Type Description
event string Name of the event.
data object? Event payload.

logout event

User session using same authentication key has been logged out (not yet implemented for OAuth authentication). Server will disconnect session after sending this event so don't try to reconnect.

new event

Sent when a new notification is received.

Payload Format

See Notification object for notification types.

read event

Sent when a notification has been read.

TODO: ids should be moved to data to match other events.

Field Type Description
event string read
ids integer[] id of Notifications which are read

chat.channel.join

Broadcast to the user when the user joins a chat channel.

Payload Format

ChatChannel with current_user_attributes, last_message_id, users additional attributes.

chat.channel.part

Broadcast to the user when the user leaves a chat channel.

Payload Format

ChatChannel with current_user_attributes, last_message_id, users additional attributes.

chat.message.new

Sent to the user when the user receives a chat message.

Payload Format

Field Type Description
messages ChatMessage[] The messages received.
users User[] The related users who sent the messages.

Messages intented for a user are always sent even if the user does not currently have the channel open. Such messages include PM and Announcement messages.

Other messages, e.g. public channel messages are not sent if the user is no longer present in the channel.

Websocket Commands

Websocket commands have the format:

{ "event": "some.event" }
Field Type Description
event string Name of the event.

Commands currently do not have any payload.

chat.start

Send to the websocket to start receiving chat messages.

webSocket.send(JSON.stringify({ event: 'chat.start' }));

chat.end

Send to the websocket to stop receiving chat messages.

webSocket.send(JSON.stringify({ event: 'chat.end' }));

Object Structures

Beatmap

Represent a beatmap.

Field Type Description
beatmapset_id integer
difficulty_rating float
id integer
mode Ruleset
status string See Rank status for list of possible values.
total_length integer
user_id integer
version string

Optional attributes:

Field Type Description
beatmapset Beatmapset|BeatmapsetExtended|null Beatmapset for Beatmap object, BeatmapsetExtended for BeatmapExtended object. null if the beatmap doesn't have associated beatmapset (e.g. deleted).
checksum string?
current_user_playcount integer
failtimes Failtimes
max_combo integer
owners BeatmapOwner[] List of owners (mappers) for the Beatmap.

Failtimes

All fields are optional but there's always at least one field returned.

Field Type Description
exit integer[]? Array of length 100.
fail integer[]? Array of length 100.

BeatmapDifficultyAttributes

Represent beatmap difficulty attributes. Following fields are always present and then there are additional fields for different rulesets.

Field Type
star_rating float
max_combo integer

osu

Field Type
aim_difficulty float
aim_difficult_slider_count float
speed_difficulty float
speed_note_count float
slider_factor float
aim_difficult_strain_count float
speed_difficult_strain_count float

taiko

Field Type
mono_stamina_factor float

BeatmapExtended

Represent a beatmap. This extends Beatmap with additional attributes.

Additional attributes:

Field Type Description
accuracy float
ar float
beatmapset_id integer
bpm float?
convert boolean
count_circles integer
count_sliders integer
count_spinners integer
cs float
deleted_at Timestamp?
drain float
hit_length integer
is_scoreable boolean
last_updated Timestamp
mode_int integer
passcount integer
playcount integer
ranked integer See Rank status for list of possible values.
url string

BeatmapOwner

Field Type Description
id integer User id of the Beatmap owner.
username string Username of the Beatmap owner.

Users that are no longer visible will still appear as a BeatmapOwner but have the username set to [deleted user].

BeatmapPack

Represent a beatmap pack.

Field Type Description
author string
date Timestamp
name string
no_diff_reduction boolean Whether difficulty reduction mods may be used to clear the pack.
ruleset_id integer
tag string The tag of the beatmap pack. Starts with a character representing the type (See the Tag column of BeatmapPackType) followed by an integer.
url string The download url of the beatmap pack.

Optional Attributes

Field Type Description
beatmapsets Beatmapset[]
user_completion_data.beatmapset_ids integer[] IDs of beatmapsets completed by the user (according to the requirements of the pack)
user_completion_data.completed boolean Whether all beatmapsets are completed or not

BeatmapPackType

Available beatmap pack types:

Name Tag Description
standard S Standard
featured F Featured Artist
tournament P Tournament
loved L Project Loved
chart R Spotlights
theme T Theme
artist A Artist/Album

BeatmapPlaycount

Represent the playcount of a beatmap.

Field Type Description
beatmap_id integer
beatmap Beatmap?
beatmapset Beatmapset?
count integer

BeatmapScores

{ "scores": [], "userScore": {} }
Field Type Description
scores Score[] The list of top scores for the beatmap in descending order.
userScore BeatmapUserScore? The score of the current user. This is not returned if the current user does not have a score. Note: will be moved to user_score in the future

BeatmapUserScore

{ "position": 1, "score": {} }
Field Type Description
position integer The position of the score within the requested beatmap ranking.
score Score The details of the score.

Beatmapset

Represents a beatmapset.

Field Type Description
artist string
artist_unicode string
covers Covers
creator string
favourite_count integer
id integer
nsfw boolean
offset integer
play_count integer
preview_url string
source string
status string
spotlight boolean
title string
title_unicode string
user_id integer
video boolean

Those fields are optional.

Field Type Description
beatmaps (Beatmap|BeatmapExtended)[]
converts
current_nominations Nomination[]
current_user_attributes
description
discussions
events
genre
has_favourited boolean
language
nominations
pack_tags string[]
ratings
recent_favourites
related_users
user
track_id integer

Covers

Field Type
cover string
cover@2x string
card string
card@2x string
list string
list@2x string
slimcover string
slimcover@2x string

Rank status

The possible values are denoted either as integer or string.

Integer String
-2 graveyard
-1 wip
0 pending
1 ranked
2 approved
3 qualified
4 loved

BeatmapsetDiscussion

Represents a Beatmapset modding discussion.

Field Type Description
beatmap Beatmap?
beatmap_id integer?
beatmapset Beatmapset?
beatmapset_id integer
can_be_resolved boolean
can_grant_kudosu boolean
created_at Timestamp
current_user_attributes CurrentUserAttributes
deleted_at Timestamp?
deleted_by_id integer?
id integer
kudosu_denied boolean
last_post_at Timestamp
message_type MessageType
parent_id integer?
posts BeatmapsetDiscussionPost[]?
resolved boolean
starting_post BeatmapsetDiscussionPost?
timestamp integer?
updated_at Timestamp
user_id integer

MessageType

Name Description
hype
mapper_note
praise
problem
review
suggestion

BeatmapsetDiscussionPost

Represents a post in a BeatmapsetDiscussion.

Field Type Description
beatmapset_discussion_id integer
created_at Timestamp
deleted_at Timestamp?
deleted_by_id integer?
id integer
last_editor_id integer?
message string
system boolean
updated_at Timestamp
user_id integer

BeatmapsetDiscussionVote

Represents a vote on a BeatmapsetDiscussion.

Field Type Description
beatmapset_discussion_id integer
created_at Timestamp
id integer
score integer
updated_at Timestamp
user_id integer

BeatmapsetExtended

Represents a beatmapset. This extends Beatmapset with additional attributes.

Field Type Description
availability.download_disabled boolean
availability.more_information string?
bpm float
can_be_hyped boolean
deleted_at Timestamp?
discussion_enabled boolean Deprecated, all beatmapsets now have discussion enabled.
discussion_locked boolean
hype.current integer
hype.required integer
is_scoreable boolean
last_updated Timestamp
legacy_thread_url string?
nominations_summary.current integer
nominations_summary.required integer
ranked integer See Rank status for list of possible values.
ranked_date Timestamp?
rating float
source string
storyboard boolean
submitted_date Timestamp?
tags string

The following attributes are always included as well:

Field
has_favourited

Build

{ "id": 5778, "version": "20210520.2", "display_version": "20210520.2", "users": 22059, "created_at": "2021-05-20T14:28:04+00:00", "update_stream": { "id": 5, "name": "stable40", "display_name": "Stable", "is_featured": true } }
Field Type
created_at Timestamp
display_version string
id integer
update_stream UpdateStream?
users integer
version string?
youtube_id string?

Optional Attributes

The following are attributes which may be additionally included in responses. Relevant endpoints should list them if applicable.

Field Type Notes
changelog_entries ChangelogEntry[] If the build has no changelog entries, a placeholder is generated.
versions Versions

Versions

Field Type
next Build?
previous Build?

ChangelogEntry

{ "id": null, "repository": null, "github_pull_request_id": null, "github_url": null, "url": "https://osu.ppy.sh/home/news/2021-05-20-spring-fanart-contest-results", "type": "fix", "category": "Misc", "title": "Spring is here!", "message_html": "<div class='changelog-md'><p class=\"changelog-md__paragraph\">New seasonal backgrounds ahoy! Amazing work by the artists.</p>\n</div>", "major": true, "created_at": "2021-05-20T10:56:49+00:00" }
Field Type
category string
created_at Timestamp?
github_pull_request_id integer?
github_url string?
id integer?
major boolean
repository string?
title string?
type string
url string?

Optional Attributes

The following are attributes which may be additionally included in responses. Relevant endpoints should list them if applicable.

Field Type Notes
github_user GithubUser If the changelog entry has no GitHub user, a placeholder is generated.
message string? Entry message in Markdown format. Embedded HTML is allowed.
message_html string? Entry message in HTML format.

ChatChannel

{ "channel_id": 1337, "current_user_attributes": { "can_message": true, "can_message_error": null, "last_read_id": 9150005005, }, "name": "test channel", "description": "wheeeee", "icon": "/images/layout/avatar-guest@2x.png", "type": "GROUP", "last_read_id": 9150005005, "last_message_id": 9150005005, "moderated": false, "users": [ 2, 3, 102 ] }

Represents an individual chat "channel" in the game.

Field Type Description
channel_id integer
name string
description string?
icon string? display icon for the channel
type ChannelType type of channel
message_length_limit integer
moderated boolean user can't send message when the value is true
uuid string? value from requests that is relayed back to the sender.

Optional attributes:

Field Type Description
current_user_attributes CurrentUserAttributes? only present on some responses
last_read_id integer? Deprecated; use current_user_attributes.last_read_id.
last_message_id integer? message_id of last known message (only returned in presence responses)
recent_messages ChatMessage[]? Deprecated; up to 50 most recent messages
users integer[]? array of user_id that are in the channel (not included for PUBLIC channels)

ChannelType

Type Permission Check for Joining/Messaging
PUBLIC
PRIVATE is player in the allowed groups? (channel.allowed_groups)
MULTIPLAYER is player currently in the mp game?
SPECTATOR
TEMPORARY deprecated
PM see below (user_channels)
GROUP is player in channel? (user_channels)
ANNOUNCE is user in the announce group?

For PMs, two factors are taken into account:

ChatMessage

{ "channel_id": 5, "content": "i am a lazerface", "is_action": false, "message_id": 9150005004, "sender_id": 2, "timestamp": "2018-07-06T06:33:34+00:00", "type": "plain", "uuid": "some-uuid-string", "sender": { "id": 2, "username": "peppy", "profile_colour": "#3366FF", "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "is_active": true, "is_bot": false, "is_online": true, "is_supporter": true } }

Represents an individual Message within a ChatChannel.

Field Type Description
channel_id integer channel_id of where the message was sent
content string message content
is_action boolean was this an action? i.e. /me dances
message_id integer unique identifier for message
sender_id integer user_id of the sender
timestamp Timestamp when the message was sent, ISO-8601
type string type of message; 'action', 'markdown' or 'plain'
uuid string? message identifier originally sent by client

Optional attributes:

Field Type Description
sender User embedded User object to save additional api lookups

Comment

{ "commentable_id": 407, "commentable_type": "news_post", "created_at": "2019-09-05T06:31:20+00:00", "deleted_at": null, "edited_at": null, "edited_by_id": null, "id": 276, "legacy_name": null, "message": "yes", "message_html": "<div class='osu-md-default'><p class=\"osu-md-default__paragraph\">yes</p>\n</div>", "parent_id": null, "pinned": true, "replies_count": 0, "updated_at": "2019-09-05T06:31:20+00:00", "user_id": 1, "votes_count": 0 }

Represents a single comment.

Field Type Description
commentable_id integer ID of the object the comment is attached to
commentable_type string type of object the comment is attached to
created_at Timestamp ISO 8601 date
deleted_at Timestamp? ISO 8601 date if the comment was deleted; null, otherwise
edited_at Timestamp? ISO 8601 date if the comment was edited; null, otherwise
edited_by_id integer? user id of the user that edited the post; null, otherwise
id integer the ID of the comment
legacy_name string? username displayed on legacy comments
message string? markdown of the comment's content
message_html string? html version of the comment's content
parent_id integer? ID of the comment's parent
pinned boolean Pin status of the comment
replies_count integer Number of replies to the comment
updated_at Timestamp ISO 8601 date
user_id integer user ID of the poster
votes_count integer Number of votes

CommentBundle

{ "commentable_meta": [ { "id": 407, "title": "Clicking circles linked to increased performance", "type": "news_post", "url": "https://osu.ppy.sh/home" } ], "comments": [ { "commentable_id": 407, "commentable_type": "news_post", "created_at": "2019-09-05T06:31:20+00:00", "deleted_at": null, "edited_at": null, "edited_by_id": null, "id": 276, "legacy_name": null, "message": "yes", "message_html": "<div class='osu-md-default'><p class=\"osu-md-default__paragraph\">yes</p>\n</div>", "parent_id": null, "replies_count": 0, "updated_at": "2019-09-05T06:31:20+00:00", "user_id": 1, "votes_count": 1337 }, { "commentable_id": 407, "commentable_type": "news_post", "created_at": "2019-09-05T07:31:20+00:00", "deleted_at": null, "edited_at": null, "edited_by_id": null, "id": 277, "legacy_name": null, "message": "absolutely", "message_html": "<div class='osu-md-default'><p class=\"osu-md-default__paragraph\">absolutely</p>\n</div>", "parent_id": null, "replies_count": 0, "updated_at": "2019-09-05T07:31:20+00:00", "user_id": 2, "votes_count": 1337 } ], "has_more": true, "has_more_id": 276, "included_comments": [], "pinned_comments": [], "sort": "new", "user_follow": false, "user_votes": [277], "users": [ { "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "default_group": "pippi", "id": 1, "is_active": true, "is_bot": false, "is_online": true, "is_supporter": true, "last_visit": "2025-09-05T08:35:00+00:00", "pm_friends_only": false, "profile_colour": null, "username": "pippi" }, { "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "default_group": "yuzu", "id": 2, "is_active": true, "is_bot": false, "is_online": false, "is_supporter": true, "last_visit": "2025-09-04T09:28:00+00:00", "pm_friends_only": false, "profile_colour": null, "username": "yuzu" } ] }

Comments and related data.

Field Type Description
commentable_meta CommentableMeta[] ID of the object the comment is attached to
comments Comment[] Array of comments ordered according to sort;
cursor Cursor
has_more boolean If there are more comments or replies available
has_more_id integer?
included_comments Comment[] Related comments; e.g. parent comments and nested replies
pinned_comments Comment[]? Pinned comments
sort string one of the CommentSort types
top_level_count integer? Number of comments at the top level. Not returned for replies.
total integer? Total number of comments. Not retuned for replies.
user_follow boolean is the current user watching the comment thread?
user_votes integer[] IDs of the comments in the bundle the current user has upvoted
users User[] array of users related to the comments

CommentSort

Available sort types are new, old, top.

Type Sort Fields
new created_at (descending), id (descending)
old created_at (ascending), id (ascending)
top votes_count (descending), created_at (descending), id (descending)

Building cursor for comments listing

The returned response will be for comments after the specified sort fields.

For example, use last loaded comment for the fields value to load more comments. Also make sure to use same sort and parent_id values.

CommentableMeta

{ "id": 407, "title": "Clicking circles linked to increased performance", "type": "news_post", "url": "https://osu.ppy.sh/home/" }

Metadata of the object that a comment is attached to.

If object is available:

Field Type Description
current_user_attributes CurrentUserAttributes
id integer the ID of the object
owner_id integer? User ID which owns the object
owner_title string? Object owner type, used for display (MAPPER for beatmapset)
title string display title
type string the type of the object
url string url of the object

Otherwise if object has been deleted:

Field Type Description
title string display title

CurrentUserAttributes

Field Type Description
can_new_comment_reason string? null if current user can comment on it, reason sentence otherwise

CurrentUserAttributes

An object listing various related permissions and states for the current user, related to the object it is attached to.

BeatmapsetDiscussionPermissions

TODO: needs a better name.

Name Description
can_destroy Can delete the discussion.
can_reopen Can reopen the discussion.
can_moderate_kudosu Can allow or deny kudosu.
can_resolve Can resolve the discussion.
vote_score Current vote given to the discussion.

ChatChannelUserAttributes

Name Type Description
can_message boolean Can send messages to this channel.
can_message_error string? Reason messages cannot be sent to this channel
last_read_id integer message_id of last message read.

Cursor

{ "_id": 5, "_score": 36.234 } // query string: cursor[_id]=5&cursor[_score]=36.234
{ "page": 2, } // query string: cursor[page]=2

A structure included in some API responses containing the parameters to get the next set of results.

The values of the cursor should be provided to next request of the same endpoint to get the next set of results.

If there are no more results available, a cursor with a value of null is returned: "cursor": null.

Note that sort option should also be specified for it to work.

CursorString

A string value included in some API responses containing the parameter to get the next set of results.

Its value will be null (or not defined) if there are no more results available.

Note that all parameters used in previous request also need to be passed.

Event

The object has different attributes depending on its type. Following are attributes available to all types.

Field Type Description
created_at Timestamp
id integer
type Event.Type

Additional objects

Beatmap

Field Type
title string
url string

Beatmapset

Field Type
title string
url string

User

Field Type Description
username string
url string
previousUsername string? Only for usernameChange event.

Available Types

achievement

When user obtained an achievement.

Field Type
achievement Achievement
user Event.User

beatmapPlaycount

When a beatmap has been played for certain number of times.

Field Type
beatmap Event.Beatmap
count integer

beatmapsetApprove

When a beatmapset changes state.

Field Type Description
approval string ranked, approved, qualified, loved.
beatmapset Event.Beatmapset
user Event.User Beatmapset owner.

beatmapsetDelete

When a beatmapset is deleted.

Field Type
beatmapset Event.Beatmapset

beatmapsetRevive

When a beatmapset in graveyard state is updated.

Field Type Description
beatmapset Event.Beatmapset
user Event.User Beatmapset owner.

beatmapsetUpdate

When a beatmapset is updated.

Field Type Description
beatmapset Event.Beatmapset
user Event.User Beatmapset owner.

beatmapsetUpload

When a new beatmapset is uploaded.

Field Type Description
beatmapset Event.Beatmapset
user Event.User Beatmapset owner.

rank

When a user achieves a certain rank on a beatmap.

Field Type Description
scoreRank string (FIXME)
rank integer
mode Ruleset
beatmap Event.Beatmap
user Event.User

rankLost

When a user loses first place to another user.

Field Type
mode Ruleset
beatmap Event.Beatmap
user Event.User

userSupportAgain

When a user supports osu! for the second and onwards.

Field Type
user Event.User

userSupportFirst

When a user becomes a supporter for the first time.

Field Type
user Event.User

userSupportGift

When a user is gifted a supporter tag by another user.

Field Type Description
user Event.User Recipient user.

usernameChange

When a user changes their username.

Field Type Description
user Event.User Includes previousUsername.

Forum

Field Type Notes
id integer
name string
description string
subforums Forum[]? Maximum 2 layers of subforums from the top-level Forum

Forum Post

Field Type Description
created_at Timestamp
deleted_at Timestamp?
edited_at Timestamp?
edited_by_id integer?
forum_id integer
id integer
topic_id integer
user_id integer

Following fields are optional.

Field Type Description
body.html string Post content in HTML format.
body.raw string Post content in BBCode format.

Forum Topic

Field Type
created_at Timestamp
deleted_at Timestamp?
first_post_id integer
forum_id integer
id integer
is_locked boolean
last_post_id integer
poll Poll?
post_count integer
title string
type normal | sticky | announcement
updated_at Timestamp
user_id integer
views integer

Poll

Field Type
allow_vote_change boolean
ended_at Timestamp?
hide_incomplete_results boolean
last_vote_at Timestamp?
max_votes integer
options PollOption[]
started_at Timestamp
title.bbcode string
title.html string
total_vote_count integer

PollOption

Field Type Notes
id integer Unique only per-topic.
text.bbcode string
text.html string
vote_count integer? Not present if the poll is incomplete and results are hidden.

GithubUser

{ "display_name": "bdach", "github_url": "https://github.com/bdach", "github_username": "bdach", "id": 218, "osu_username": null, "user_id": null, "user_url": null }
Field Type
display_name string
github_url string?
github_username string?
id integer?
osu_username string?
user_id integer?
user_url string?

Group

This object is not returned by any endpoints yet. It is here only as a reference for UserGroup.

Field Type Description
colour string?
has_listing boolean Whether this group displays a listing at /groups/{id}.
has_playmodes boolean Whether this group associates Rulesets with users' memberships.
id integer
identifier string Unique string to identify the group.
is_probationary boolean Whether members of this group are considered probationary.
name string
short_name string Short name of the group for display.

Optional Attributes

The following are attributes which may be additionally included in responses. Relevant endpoints should list them if applicable.

Field Type
description Description?

Description

Field Type
html string
markdown string

KudosuHistory

Field Type Description
id integer
action string One of give, vote.give, reset, vote.reset, revoke, or vote.revoke.
amount integer
model string Object type which the exchange happened on (forum_post, etc).
created_at Timestamp
giver Giver? Simple detail of the user who started the exchange.
post Post Simple detail of the object for display.

Giver

Field Type
url string
username string

Post

Field Type Description
url string? Url of the object.
title string Title of the object. It'll be "[deleted beatmap]" for deleted beatmaps.

Match

Field Type Description
id integer
start_time Timestamp
end_time Timestamp?
name string

MatchEvent

Field Type Description
id integer
detail.type MatchEventType
detail.text string
timestamp Timestamp
user_id integer?

Optional Attributes

Field Type Description
game MatchGame The game associated with the MatchEvent

MatchEventType

Name Description
host-changed
match-created
match-disbanded
other
player-joined
player-kicked
player-left

MatchGame

Field Type Description
id integer
beatmap Beatmap Includes beatmapset.
beatmap_id integer
start_time Timestamp
end_time Timestamp?
mode Ruleset
mode_int integer
mods string[] Mod combination used for this match game as an array of mod acronyms.
scores Score[] List of scores set by each player for this match game.
scoring_type string accuracy, combo, score, scorev2.
team_type string head-to-head, tag-coop, tag-team-vs, team-vs.

Mod

{ "acronym": "MU", "settings": { "inverse_muting": true, "mute_combo_count": 1, "affects_hit_sounds": false } }

Represents a single configured gameplay mod.

Field Type Description
acronym string The acronym of the mod.
settings ModSetting[]? The list of configured settings on the mod, if any.

ModSetting

Field Type Description
name string The name of the setting.
value object The value of the setting.

The names and types of values of possible settings depend on which mod they pertain to. You can find a list of possible mods, and possible settings for each mod, in mods.json.

MultiplayerScores

An object which contains scores and related data for fetching next page of the result.

Field Type Description
cursor_string CursorString To be used to fetch the next page.
params object Parameters used for score listing.
scores Score[]
total integer? Index only. Total scores of the specified playlist item.
user_score Score? Index only. Score of the accessing user if exists.

MultiplayerScoresAround

Field Type Description
higher MultiplayerScores
lower MultiplayerScores

MultiplayerScoresCursor

An object which contains pointer for fetching further results of a request. It depends on the sort option.

Field Type Description
score_id integer Last score id of current result (score_asc, score_desc).
total_score integer Last score's total score of current result (score_asc, score_desc).

MultiplayerScoresSort

Sort option for multiplayer scores index.

Name Description
score_asc Sort by scores, ascending.
score_desc Sort by scores, descending.

NewsPost

Field Type Description
author string
edit_url string Link to the file view on GitHub.
first_image string? Link to the first image in the document.
first_image@2x string? Larger size version of first_image.
id integer
published_at Timestamp
slug string Filename without the extension, used in URLs.
title string
updated_at Timestamp

Optional Attributes

Field Type Description
content string HTML post content.
navigation Navigation Navigation metadata.
preview string First paragraph of content with HTML markup stripped.

Navigation

Field Type Description
newer NewsPost? Next post.
older NewsPost? Previous post.

Nomination

Field Type
beatmapset_id integer
rulesets Ruleset[]
reset boolean
user_id integer

Notification

{ "id": 1, "name": "channel_message", "created_at": "2019-04-24T07:12:43+00:00", "object_type": "channel", "object_id": 1, "source_user_id": 1, "is_read": true, "details": { "username": "someone", ... } }

Represents a notification object.

Field Type Description
id integer
name string Name of the event
created_at Timestamp ISO 8601 date
object_type string
object_id integer
source_user_id integer?
is_read boolean
details object message_id of last known message (only returned in presence responses)

Event Names

Name Description
beatmapset_discussion_lock Discussion on beatmap has been locked
beatmapset_discussion_post_new New discussion post on beatmap
beatmapset_discussion_unlock Discussion on beatmap has been unlocked
beatmapset_disqualify Beatmap was disqualified
beatmapset_love Beatmap was promoted to loved
beatmapset_nominate Beatmap was nominated
beatmapset_qualify Beatmap has gained enough nominations and entered the ranking queue
beatmapset_remove_from_loved Beatmap was removed from Loved
beatmapset_reset_nominations Nomination of beatmap was reset
channel_message Someone sent chat message
forum_topic_reply Someone replied on forum topic

beatmapset_discussion_lock

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who locked discussion

Details object:

Field Type Description
cover_url string Beatmap cover
title string Beatmap title
username string Username of source_user_id

beatmapset_discussion_post_new

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer Poster of the discussion

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
discussion_id integer
post_id integer
beatmap_id integer? null if posted to general all
username string Username of source_user_id

beatmapset_discussion_unlock

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who unlocked discussion

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

beatmapset_disqualify

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who disqualified beatmapset

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

beatmapset_love

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who promoted beatmapset to loved

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

beatmapset_nominate

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who nominated beatmapset

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

beatmapset_qualify

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User whom beatmapset nomination triggered qualification

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

beatmapset_remove_from_loved

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who removed beatmapset from Loved

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

beatmapset_reset_nominations

Field Type Description
object_id integer Beatmapset id
object_type string beatmapset
source_user_id integer User who triggered nomination reset

Details object:

Field Type Description
title string Beatmap title
cover_url string Beatmap cover
username string Username of source_user_id

channel_message

Field Type Description
object_id integer Channel id
object_type string channel
source_user_id integer User who posted message

Details object:

Field Type Description
title string Up to 36 characters of the message (ends with ... when exceeding 36 characters)
cover_url string Avatar of source_user_id
username string Username of source_user_id

forum_topic_reply

Field Type Description
object_id integer Topic id
object_type string forum_topic
source_user_id integer User who posted message

Details object:

Field Type Description
title string Title of the replied topic
cover_url string Topic cover
post_id integer Post id
username string? Username of source_user_id

RankingType

Available ranking types:

Name Description
charts Spotlight
country Country
performance Performance
score Score

Rankings

{ "cursor": { }, "ranking": [ { "grade_counts": { "a": 3, "s": 2, "sh": 6, "ss": 2, "ssh": 3 }, "hit_accuracy": 92.19, "is_ranked": true, "level": { "current": 30, "progress": 0 }, "maximum_combo": 3948, "play_count": 228050, "play_time": null, "pp": 990, "global_rank": 87468, "ranked_score": 1502995536, "replays_watched_by_others": 0, "total_hits": 5856573, "total_score": 2104193750, "user": { "avatar_url": "/images/layout/avatar-guest.png", "country": { "code": "GF", "name": "French Guiana" }, "country_code": "GF", "cover": { "custom_url": null, "id": "3", "url": "http://osuweb.test/images/headers/profile-covers/c3.jpg" }, "default_group": "default", "id": 458402, "is_active": false, "is_bot": false, "is_online": false, "is_supporter": true, "last_visit": "2017-02-22T11:07:10+00:00", "pm_friends_only": false, "profile_colour": null, "username": "serdman" } } ], "total": 100 }
Field Type Description
beatmapsets BeatmapsetExtended[]? The list of beatmaps in the requested spotlight for the given mode; only available if type is charts
cursor Cursor A cursor
ranking UserStatistics[]

User statistics for the requested ruleset in order of descending rank.

Includes user, user.country, and user.cover.
Includes rank_change_since_30_days if the ranking type is performance with no additional filters applied.
spotlight Spotlight? Spotlight details; only available if type is charts
total integer An approximate count of ranks available

Ruleset

Available rulesets:

Name Description
fruits osu!catch
mania osu!mania
osu osu!standard
taiko osu!taiko

Score

The following is the format returned when API v2 version header is 20220705 or higher. Exceptions apply (f.ex. doesn't apply for legacy match score).

Field Type Description
accuracy float The accuracy achieved by the user, as a decimal in [0, 1] range.
beatmap_id integer The ID of the beatmap that the score was set on.
best_id integer? No longer used.
build_id integer? The ID of the game build the score was set on.
classic_total_score integer Total score calculated using lazer's "classic scoring" variant. Only present for solo_score type.
ended_at Timestamp When the user finished playing.
has_replay boolean Whether an online replay exists for this score.
id integer The ID of this score.
is_perfect_combo boolean Whether the user has achieved perfect combo using lazer logic.1
legacy_perfect boolean Whether the user has achieved perfect combo using stable logic.1
legacy_score_id integer? Only present on scores set in stable. The ID of this score in the old ID scheme preceding lazer.
legacy_total_score integer Only present on scores set in stable (will be 0 on scores set in lazer). The total score achieved by the user in stable's scoring algorithm (colloquially known as "score V1").
max_combo integer The largest combo achieved by the user during gameplay.
maximum_statistics ScoreStatistics The best possible score statistics achievable on the map the user played.
mods Mod[] The mods used when setting this score.
passed boolean Whether the user passed or failed gameplay.
playlist_item_id integer The ID of the multiplayer playlist item associated with this score. Only present for multiplayer scores.
pp float? Number of performance points awarded to this score.
preserve boolean Whether or not the score may eventually be deleted. Only present for solo_score type.
processed boolean Whether or not the score has been fully processed server-side (most notably including calculation of pp). Only present for solo_score type.
rank string The letter rank achieved by the score. Notably, this always follows lazer's updated rank criteria.
ranked boolean Whether or not this score can be visible on beatmap leaderboards. Notably, presence of unranked mods does not influence the value of this flag, and whether a score gives PP does not depend on this flag. Only for solo_score type.
room_id integer The ID of the room that the user was in when setting this score. Only present for multiplayer scores.
ruleset_id integer The ID of the ruleset that this score was set on.
started_at Timestamp? When the user started playing.
statistics ScoreStatistics The score statistics achieved in this score,
total_score integer Total score calculated using lazer's "standardised scoring" variant.
type string
user_id integer The ID of the user that achieved this score.

1 These two values are in most cases the same. The one exception where they may differ is osu!mania, because of the removal of the mechanic in which holding down a hold note grants combo for "ticks". See relevant wiki article for details.

Initial version

The following is the format returned when API v2 version header is 20220704 or lower.

Field Type Description
id
best_id
user_id
accuracy
mods
score
max_combo
perfect
statistics.count_50
statistics.count_100
statistics.count_300
statistics.count_geki
statistics.count_katu
statistics.count_miss
passed boolean
pp
rank
created_at
mode
mode_int
replay

Optional attributes

The following fields may also be present in particular contexts. These optional fields apply to both aforementioned formats.

Field Type Description
beatmap BeatmapExtended The beatmap that the score was set on.
beatmapset BeatmapSet The beatmapset that the score was set on.
current_user_attributes CurrentUserAttributes? Contains information about whether the user in whose name the API request is performed has this score pinned on their profile.
match Contains data relevant to multiplayer matches (slot, team, pass). Only for legacy match scores.
position integer? The position of the score on the leaderboard of the current playlist item. Only for multiplayer scores.
rank_country The rank of the score on the user's country leaderboards.
rank_global The rank of the score on global leaderboards.
scores_around MultiplayerScoresAround? Scores around the specified score. Only for multiplayer scores.
user The user that set this score.
weight Information about this score's weighted contribution to the user's total PP.

ScoreStatistics

{ "ok": 3, "great": 211, "ignore_hit": 56, "slider_tail_hit": 56 }

ScoreStatistics is a dictionary whose keys are hit results as defined by the lazer client, and whose values are counts of said hit results. This structure is used to represent both the counts of each result achieved by the user on a particular score, and best possible counts of each result on a perfect play.

Spotlight

{ "end_date": "2019-03-22T00:00:00+00:00", "id": 1, "mode_specific": false, "name": "Best spinning circles 2019", "start_date": "2019-02-22T00:00:00+00:00", "type": "yearly", }

The details of a spotlight.

Field Type Description
end_date Timestamp The end date of the spotlight.
id integer The ID of this spotlight.
mode_specific boolean If the spotlight has different mades specific to each Ruleset.
participant_count integer? The number of users participating in this spotlight. This is only shown when viewing a single spotlight.
name string The name of the spotlight.
start_date Timestamp The starting date of the spotlight.
type string The type of spotlight.

Spotlights

{ "spotlights": [ { "end_date": "2019-03-22T00:00:00+00:00", "id": 1, "mode_specific": false, "name": "Best spinning circles 2019", "start_date": "2019-02-22T00:00:00+00:00", "type": "yearly", }, { "end_date": "2019-03-22T00:00:00+00:00", "id": 2, "mode_specific": true, "name": "Ultimate fruit collector February 2019", "start_date": "2019-02-22T00:00:00+00:00", "type": "monthly", } ], }
Field Type Description
spotlights Spotlight[] An array of spotlights

Timestamp

 "2020-01-01T00:00:00+00:00"

Timestamp string in ISO 8601 format.

UpdateStream

{ "id": 7, "name": "lazer", "display_name": "Lazer", "is_featured": false }
Field Type
display_name string?
id integer
is_featured boolean
name string

Optional Attributes

The following are attributes which may be additionally included in responses. Relevant endpoints should list them if applicable.

Field Type
latest_build Build?
user_count integer

User

{ "id": 2, "username": "peppy", "profile_colour": "#3366FF", "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country_code": "AU", "is_active": true, "is_bot": false, "is_deleted": false, "is_online": true, "is_supporter": true }

Represents a user.

Field Type Description
avatar_url string url of user's avatar
country_code string two-letter code representing user's country
default_group string? Identifier of the default Group the user belongs to.
id integer unique identifier for user
is_active boolean has this account been active in the last x months?
is_bot boolean is this a bot account?
is_deleted boolean
is_online boolean is the user currently online? (either on lazer or the new website)
is_supporter boolean does this user have supporter?
last_visit Timestamp? last access time. null if the user hides online presence
pm_friends_only boolean whether or not the user allows PM from other than friends
profile_colour string? colour of username/profile highlight, hex code (e.g. #333333)
username string user's display name

Optional attributes

Following are attributes which may be additionally included in the response. Relevant endpoints should list them if applicable.

Field Type Notes
account_history User.UserAccountHistory[]
active_tournament_banner User.ProfileBanner? Deprecated, use active_tournament_banners instead.
active_tournament_banners User.ProfileBanner[]
badges User.UserBadge[]
beatmap_playcounts_count integer
blocks
country
cover
favourite_beatmapset_count integer
follow_user_mapping integer[]
follower_count integer
friends
graveyard_beatmapset_count integer
groups UserGroup[]
guest_beatmapset_count integer
is_restricted boolean?
kudosu User.Kudosu
loved_beatmapset_count integer
mapping_follower_count integer
monthly_playcounts UserMonthlyPlaycount[]
nominated_beatmapset_count integer
page
pending_beatmapset_count
previous_usernames
rank_highest User.RankHighest?
rank_history
ranked_beatmapset_count
replays_watched_counts
scores_best_count integer
scores_first_count integer
scores_recent_count integer
session_verified boolean
statistics UserStatistics
statistics_rulesets UserStatisticsRulesets
support_level
unread_pm_count
user_achievements
user_preferences

Kudosu

Field Type
available integer
total integer

ProfileBanner

Field Type Description
id integer
tournament_id integer
image string?
image@2x string?

ProfilePage

Section
me
recent_activity
beatmaps
historical
kudosu
top_ranks
medals

RankHighest

Field Type
rank integer
updated_at Timestamp

UserAccountHistory

Field Type Description
description string?
id integer
length integer In seconds.
permanent boolean
timestamp Timestamp
type string note, restriction, or silence.

UserBadge

Field Type Description
awarded_at Timestamp
description string
image@2x_url string
image_url string
url string

UserExtended

{ "avatar_url": "https://a.ppy.sh/1?1501234567.jpeg", "country_code": "AU", "default_group": "default", "id": 1, "is_active": true, "is_bot": false, "is_deleted": false, "is_online": false, "is_supporter": true, "last_visit": "2020-01-01T00:00:00+00:00", "pm_friends_only": false, "profile_colour": "#000000", "username": "osuuser", "cover_url": "https://assets.ppy.sh/user-profile-covers/1/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef.jpeg", "discord": "osuuser#1337", "has_supported": true, "interests": null, "join_date": "2010-01-01T00:00:00+00:00", "kudosu": { "total": 20, "available": 10 }, "location": null, "max_blocks": 50, "max_friends": 500, "occupation": null, "playmode": "osu", "playstyle": [ "mouse", "touch" ], "post_count": 100, "profile_hue": 42, "profile_order": [ "me", "recent_activity", "beatmaps", "historical", "kudosu", "top_ranks", "medals" ], "title": null, "twitter": "osuuser", "website": "https://osu.ppy.sh", "country": { "code": "AU", "name": "Australia" }, "cover": { "custom_url": "https://assets.ppy.sh/user-profile-covers/1/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef.jpeg", "url": "https://assets.ppy.sh/user-profile-covers/1/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef.jpeg", "id": null }, "is_restricted": false, "account_history": [], "active_tournament_banner": null, "badges": [ { "awarded_at": "2015-01-01T00:00:00+00:00", "description": "Test badge", "image@2x_url": "https://assets.ppy.sh/profile-badges/test@2x.png", "image_url": "https://assets.ppy.sh/profile-badges/test.png", "url": "" } ], "favourite_beatmapset_count": 10, "follower_count": 100, "graveyard_beatmapset_count": 10, "groups": [ { "id": 1, "identifier": "gmt", "name": "gmt", "short_name": "GMT", "description": "", "colour": "#FF0000" } ], "loved_beatmapset_count": 0, "monthly_playcounts": [ { "start_date": "2019-11-01", "count": 100 }, { "start_date": "2019-12-01", "count": 150 }, { "start_date": "2020-01-01", "count": 20 } ], "page": { "html": "<div class='bbcode bbcode--profile-page'><center>Hello</center></div>", "raw": "[centre]Hello[/centre]" }, "pending_beatmapset_count": 0, "previous_usernames": [], "ranked_beatmapset_count": 10, "replays_watched_counts": [ { "start_date": "2019-11-01", "count": 10 }, { "start_date": "2019-12-01", "count": 12 }, { "start_date": "2020-01-01", "count": 1 } ], "scores_first_count": 0, "statistics": { "level": { "current": 60, "progress": 55 }, "pp": 100, "global_rank": 2000, "ranked_score": 2000000, "hit_accuracy": 90.5, "play_count": 1000, "play_time": 100000, "total_score": 3000000, "total_hits": 6000, "maximum_combo": 500, "replays_watched_by_others": 270, "is_ranked": true, "grade_counts": { "ss": 10, "ssh": 5, "s": 50, "sh": 0, "a": 40 }, "rank": { "global": 15000, "country": 30000 } }, "support_level": 3, "user_achievements": [ { "achieved_at": "2020-01-01T00:00:00+00:00", "achievement_id": 1 } ], "rank_history": { "mode": "osu", "data": [ 16200, 15500, 15000 ] } }

Represents a user. Extends User object with additional attributes.

Field Type Description
cover_url string url of profile cover. Deprecated, use cover.url instead.
discord string?
has_supported boolean Whether or not the user has a current or past osu!supporter tag.
interests string?
join_date Timestamp
location string?
max_blocks integer maximum number of users allowed to be blocked
max_friends integer maximum number of friends allowed to be added
occupation string?
playmode Ruleset
playstyle string[] Device choices of the user.
post_count integer Number of forum posts
profile_hue integer? Custom colour hue in HSL degrees (0–359).
profile_order ProfilePage[] ordered array of sections in user profile page
title string? user-specific title
title_url string?
twitter string?
website string?

In addition, the following optional attributes on User are included:

UserGroup

Describes a Group membership of a User. It contains all of the attributes of the Group, in addition to what is listed here.

Field Type Description
playmodes string[]? Rulesets associated with this membership (null if has_playmodes is unset).

UserSilence

{ "id": 1, "user_id": 5 }

A record indicating a User was silenced.

Field Type Description
id integer id of this object.
user_id integer id of the User that was silenced

UserStatistics

{ "count_100": 0, "count_300": 0, "count_50": 0, "count_miss": 0, "grade_counts": { "a": 3, "s": 2, "sh": 6, "ss": 2, "ssh": 3 }, "hit_accuracy": 92.19, "is_ranked": true, "level": { "current": 30, "progress": 0 }, "maximum_combo": 3948, "play_count": 228050, "play_time": null, "pp": 990, "global_rank": 87468, "ranked_score": 1502995536, "replays_watched_by_others": 0, "total_hits": 5856573, "total_score": 2104193750, "user": { "avatar_url": "https://a.ppy.sh/2?1519081077.png", "country": { "code": "AU", "name": "Australia" }, "country_code": "AU", "cover": { "custom_url": null, "id": "3", "url": "https://assets.ppy.sh/user-profile-covers/2/baba245ef60834b769694178f8f6d4f6166c5188c740de084656ad2b80f1eea7.jpeg" }, "default_group": "ppy", "id": 2, "is_active": false, "is_bot": false, "is_online": false, "is_supporter": true, "last_visit": "2019-02-22T11:07:10+00:00", "pm_friends_only": false, "profile_colour": "#3366FF", "username": "peppy" } }

A summary of various gameplay statistics for a User. Specific to a Ruleset

Field Type Description
count_100 integer
count_300 integer
count_50 integer
count_miss integer
country_rank integer? Current country rank according to pp.
grade_counts.a integer Number of A ranked scores.
grade_counts.s integer Number of S ranked scores.
grade_counts.sh integer Number of Silver S ranked scores.
grade_counts.ss integer Number of SS ranked scores.
grade_counts.ssh integer Number of Silver SS ranked scores.
hit_accuracy float Hit accuracy percentage
is_ranked boolean Is actively ranked
level.current integer Current level.
level.progress float Progress to next level.
maximum_combo integer Highest maximum combo.
play_count integer Number of maps played.
play_time integer Cumulative time played.
pp float Performance points
pp_exp float Experimental (lazer) performance points. Deprecated; it's now always 0.
global_rank integer? Current rank according to pp.
global_rank_exp integer? Current rank according to experimental (lazer) pp. Deprecated; it's now always null.
ranked_score integer Current ranked score.
replays_watched_by_others integer Number of replays watched by other users.
total_hits integer Total number of hits.
total_score integer Total score.

Optional attributes

Field Type Description
rank_change_since_30_days integer? Difference between current rank and rank 30 days ago, according to pp.
user User

WikiPage

{ "available_locales": ["en", "id", "ja", "pt-br"], "layout": "markdown_page", "locale": "en", "markdown": "# osu! (game mode)\n\n![Gameplay of osu!](/wiki/shared/Interface_osu.jpg \"osu! Interface\")\n\nMarkdownMarkdownTruncated", "path": "Game_Modes/osu!", "subtitle": "Game Modes", "tags": ["tap", "circles"], "title": "osu! (game mode)" }

Represents a wiki article

Field Type Description
available_locales string[] All available locales for the article.
layout string The layout type for the page.
locale string All lowercase BCP 47 language tag.
markdown string Markdown content.
path string Path of the article.
subtitle string? The article's subtitle.
tags string[] Associated tags for the article.
title string The article's title.