# Leaderboards

**URL:** https://heroiclabs.com/docs/hiro/unreal/leaderboards/
**Keywords:** Unreal leaderboards SDK, UHiroClientLeaderboardList, FHiroLeaderboardList, player rankings API C++
**Categories:** hiro, unreal, leaderboards

---


# Leaderboards

Read more about the Leaderboards system in Hiro [here](../../concepts/leaderboards/).

{{< note "important" >}}
The methods below require Hiro 1.33 or later. If you are on an earlier version, see [Hiro 1.32 and earlier](#hiro-132-and-earlier).
{{< /note >}}

## Get all leaderboards

List all available leaderboards.

```cpp
FHiroLeaderboardListRequest Request;

UHiroClientLeaderboardList* Action = UHiroClientLeaderboardList::LeaderboardList(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardList);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();

void AMyActor::OnLeaderboardList(const FHiroLeaderboardList& LeaderboardList, FNakamaError Error)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *LeaderboardList.ToJson());
}
```

Filter by category:

```cpp
FHiroLeaderboardListRequest Request;
Request.Categories = {"Tournament"};

UHiroClientLeaderboardList* Action = UHiroClientLeaderboardList::LeaderboardList(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardList);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();
```

## Get a leaderboard

Get a specified leaderboard by ID.

```cpp
FHiroLeaderboardGet Request;
Request.Id = TEXT("GalacticChampions");

UHiroClientLeaderboardGet* Action = UHiroClientLeaderboardGet::LeaderboardGet(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardGet);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();

void AMyActor::OnLeaderboardGet(const FHiroLeaderboard& Leaderboard, FNakamaError Error)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *Leaderboard.ToJson());
}
```

## Submit a score

Submit a score to a leaderboard.

```cpp
FHiroLeaderboardUpdate Request;
Request.Id = TEXT("GalacticChampions");
Request.Score = TEXT("1500");
Request.Subscore = TEXT("0");

UHiroClientLeaderboardUpdate* Action = UHiroClientLeaderboardUpdate::LeaderboardUpdate(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardUpdate);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();

void AMyActor::OnLeaderboardUpdate(const FHiroLeaderboardScore& LeaderboardScore, FNakamaError Error)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *LeaderboardScore.ToJson());
}
```

## List scores

List scores on a leaderboard. Pass `OwnerIds` to retrieve scores for specific owners.

```cpp
FHiroLeaderboardScoresListRequest Request;
Request.Id = TEXT("GalacticChampions");
Request.OwnerIds = {TEXT("userId1"), TEXT("userId2")};
Request.Limit = 100;

UHiroClientLeaderboardScoresList* Action = UHiroClientLeaderboardScoresList::LeaderboardScoresList(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardScoresList);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();

void AMyActor::OnLeaderboardScoresList(const FHiroLeaderboardScoreList& ScoreList, FNakamaError Error)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *ScoreList.ToJson());
}
```

Filter by region:

```cpp
FHiroLeaderboardScoresListRequest Request;
Request.Id = TEXT("GalacticChampions");
Request.Region = TEXT("EU");
Request.Limit = 100;

UHiroClientLeaderboardScoresList* Action = UHiroClientLeaderboardScoresList::LeaderboardScoresList(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardScoresList);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();
```

## List scores around owner

List scores centred around a specific owner.

```cpp
FHiroLeaderboardScoresListAroundOwnerRequest Request;
Request.Id = TEXT("GalacticChampions");
Request.OwnerId = Session->SessionData.UserId;
Request.Limit = 10;

UHiroClientLeaderboardScoresListAroundOwner* Action = UHiroClientLeaderboardScoresListAroundOwner::LeaderboardScoresListAroundOwner(HiroClient, Session, Request);
Action->OnSuccess.AddDynamic(this, &AMyActor::OnLeaderboardScoresListAroundOwner);
Action->OnError.AddDynamic(this, &AMyActor::OnError);
Action->Activate();

void AMyActor::OnLeaderboardScoresListAroundOwner(const FHiroLeaderboardScoreList& ScoreList, FNakamaError Error)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *ScoreList.ToJson());
}
```

## Hiro 1.32 and earlier

Prior to Hiro 1.33, `LeaderboardsConfigGet` was the only way to retrieve available leaderboards, returning a `FHiroLeaderboardConfigList` with basic configuration data. This method is deprecated in Hiro 1.33 and later. Use `LeaderboardList` instead.

```cpp
FHiroOnLeaderboardsConfigGet OnLeaderboardsConfigGet;
OnLeaderboardsConfigGet.AddDynamic(this, &AMyActor::OnLeaderboardsConfigGet);
FOnError OnError;

HiroClient->LeaderboardsConfigGet(Session, OnLeaderboardsConfigGet, OnError);

void AMyActor::OnLeaderboardsConfigGet(const FHiroLeaderboardConfigList& LeaderboardConfigList)
{
    UE_LOG(LogTemp, Log, TEXT("%s"), *LeaderboardConfigList.ToJson());
}
```

## Online Subsystem

Hiro backs Unreal Engine's standard [IOnlineLeaderboards](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/OnlineSubsystem/Interfaces/IOnlineLeaderboards) interface, so you can read and write Hiro leaderboard scores using the same code patterns you'd use with any other Online Subsystem provider. Scores are stored in Nakama and inherit all of Hiro's leaderboard features — regional scoring, friend queries, and configurable operators — without any additional setup.

This interface is separate from the Hiro client API documented above. For setup instructions, see [Online Subsystem](../getting-started/online-subsystem/).

Retrieve the interface from the subsystem after initialization:

```cpp
IOnlineLeaderboardsPtr LeaderboardsInterface = OnlineSubsystem->GetLeaderboardsInterface();
```

For setup instructions, see [Online Subsystem](../getting-started/online-subsystem/).

### Writing scores

Writing a score is a two-step operation: stage the record with `WriteLeaderboards`, then commit it to the server with `FlushLeaderboards`. Only `"score"` and `"subscore"` are valid property names.

```cpp
FOnlineLeaderboardWrite WriteObject = FOnlineLeaderboardWrite();
WriteObject.UpdateMethod = ELeaderboardUpdateMethod::Force;
WriteObject.SortMethod = ELeaderboardSort::None;
WriteObject.LeaderboardNames = { TEXT("my-leaderboard") };
WriteObject.Properties.Emplace(TEXT("score"), 100);
WriteObject.Properties.Emplace(TEXT("subscore"), 10);

LeaderboardsInterface->WriteLeaderboards(TEXT(""), *LocalUserId, WriteObject);

FDelegateHandle FlushHandle = LeaderboardsInterface->AddOnLeaderboardFlushCompleteDelegate_Handle(
    FOnLeaderboardFlushCompleteDelegate::CreateLambda([](const FName& Name, bool bWasSuccessful)
    {
        if (bWasSuccessful)
        {
            // Score submitted.
        }
    })
);
LeaderboardsInterface->FlushLeaderboards(TEXT(""));
```

Calling `WriteLeaderboards` while a flush is already in progress may cause the new value to be lost if a record for the same player and leaderboard is currently being submitted.

#### Score merging behavior

`UpdateMethod` and `SortMethod` on `FOnlineLeaderboardWrite` control how a new staged value merges with an existing cached value for the same player and leaderboard:

| Setting | Behavior |
|---|---|
| `ELeaderboardUpdateMethod::Force` or `ELeaderboardSort::None` | Always overwrites the cached value |
| `ELeaderboardSort::Ascending` | Keeps the cached value if it's lower than the new score |
| `ELeaderboardSort::Descending` | Keeps the cached value if it's higher than the new score |

### Reading scores for specific players

`ReadLeaderboards` fetches records for the given player IDs and populates the `FOnlineLeaderboardRead` object asynchronously. All player IDs must be Hiro IDs (`FUniqueNetIdHiro`).

```cpp
FOnlineLeaderboardReadPtr LeaderboardRead = MakeShared<FOnlineLeaderboardRead>();
LeaderboardRead->LeaderboardName = TEXT("my-leaderboard");
FOnlineLeaderboardReadRef LeaderboardReadRef = LeaderboardRead.ToSharedRef();

FDelegateHandle ReadHandle = LeaderboardsInterface->AddOnLeaderboardReadCompleteDelegate_Handle(
    FOnLeaderboardReadCompleteDelegate::CreateLambda([LeaderboardReadRef](bool bWasSuccessful)
    {
        if (bWasSuccessful)
        {
            for (const FOnlineStatsRow& Row : LeaderboardReadRef->Rows)
            {
                // Row.PlayerId, Row.Rank, Row.Columns
            }
        }
    })
);
LeaderboardsInterface->ReadLeaderboards({ LocalUserId.ToSharedRef() }, LeaderboardReadRef);
```

Each row in `Rows` contains three columns:

- `"score"` — the player's primary score (`Int32`)
- `"subscore"` — the player's secondary score (`Int32`)
- `"num_score"` — the number of times the player has submitted a score (`Int32`)

### Reading scores around a player

`ReadLeaderboardsAroundUser` returns scores for the players ranked closest to the given player. The `Range` parameter controls the window size: the interface requests `2 × Range + 1` records centered on the player.

```cpp
FOnlineLeaderboardReadPtr LeaderboardRead = MakeShared<FOnlineLeaderboardRead>();
LeaderboardRead->LeaderboardName = TEXT("my-leaderboard");
FOnlineLeaderboardReadRef LeaderboardReadRef = LeaderboardRead.ToSharedRef();

FDelegateHandle ReadHandle = LeaderboardsInterface->AddOnLeaderboardReadCompleteDelegate_Handle(
    FOnLeaderboardReadCompleteDelegate::CreateLambda([LeaderboardReadRef](bool bWasSuccessful)
    {
        if (bWasSuccessful)
        {
            // LeaderboardReadRef->Rows contains players around the target.
        }
    })
);

uint32 Range = 5; // Returns up to 11 rows (5 above, player, 5 below).
LeaderboardsInterface->ReadLeaderboardsAroundUser(LocalUserId.ToSharedRef(), Range, LeaderboardReadRef);
```

### Reading a friends' leaderboard

`ReadLeaderboardsForFriends` fetches leaderboard scores for all friends of the specified local player. It automatically paginates through the friends list (up to 1,000 per page) before issuing the leaderboard read.

`LocalUserNum` is the local player index used to look up the player's session and friends list.

```cpp
FOnlineLeaderboardReadPtr LeaderboardRead = MakeShared<FOnlineLeaderboardRead>();
LeaderboardRead->LeaderboardName = TEXT("my-leaderboard");
FOnlineLeaderboardReadRef LeaderboardReadRef = LeaderboardRead.ToSharedRef();

FDelegateHandle ReadHandle = LeaderboardsInterface->AddOnLeaderboardReadCompleteDelegate_Handle(
    FOnLeaderboardReadCompleteDelegate::CreateLambda([LeaderboardReadRef](bool bWasSuccessful)
    {
        if (bWasSuccessful)
        {
            // LeaderboardReadRef->Rows contains the friends' scores.
        }
    })
);

int32 LocalUserNum = 0;
LeaderboardsInterface->ReadLeaderboardsForFriends(LocalUserNum, LeaderboardReadRef);
```

### Unsupported operations

`ReadLeaderboardsAroundRank` is not supported. It always returns `false` and fires `OnLeaderboardReadComplete` with a failure state.
