> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trophy.so/llms.txt
> Use this file to discover all available pages before exploring further.

# How To Build A Leaderboards Feature

> Learn how to use Trophy to add a leaderboards feature to your web or mobile app.

The guide outlines the full process of adding a leaderboards feature to your web or mobile app using Trophy.

For illustration purposes we'll use the example of a study platform that uses a daily leaderboard to create friendly competition around viewing flashcards.

<Tip>
  To see a fully working example of this in practice, check out the [live
  demo](https://examples.trophy.so) or [github
  repo](https://github.com/trophyso/example-study-platform/tree/demo).
</Tip>

<h2 id="pre-requisites">
  Pre-requisites
</h2>

* A [Trophy](https://app.trophy.so/sign-up) account
* About 10 minutes

<h2 id="trophy-setup">
  Trophy Setup
</h2>

In Trophy, [Metrics](/features/metrics) are the building blocks of gamification and model the different interactions users make with your product.

In this guide the interaction we're interested in is `flashcards-viewed`, but you can create a metric that best represents the interaction you want to build leaderboards around.

In the Trophy dashboard, head to the [metrics page](https://app.trophy.so/metrics) and create a metric.

<Frame>
  <video autoPlay muted loop playsInline className="w-full aspect-video" src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/achievements-feature/create_new_metric.mp4?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=8e66d7276d9648d449c4556340febf45" data-path="assets/guides/achievements-feature/create_new_metric.mp4" />
</Frame>

Once you've created your metric, head to the [leaderboards page](https://app.trophy.so/leaderboards) and create the leaderboards you want. You can find all the details on the types of leaderboards and the different use cases in the [leaderboards docs](/features/leaderboards).

<Frame>
  <video autoPlay muted loop playsInline className="w-full aspect-video" src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/leaderboards-feature/creating_leaderboards.mp4?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=dfa86dba0df581d14e9edeed17f1c919" data-path="assets/guides/leaderboards-feature/creating_leaderboards.mp4" />
</Frame>

For the purposes of this guide we've set up a daily leaderboard that tracks the total XP a user earns by viewing flashcards.

<Tip>
  For a full guide on adding an XP feature to your web or mobile app, check out
  our [full guide](/guides/how-to-build-an-xp-feature).
</Tip>

In Trophy you track user interactions by sending [Events](/features/events) from your code to Trophy APIs against a specific metric.

When events are recorded for a specific user, Trophy automatically updates their ranking in each leaderboard they are a part of.

This is what makes building gamified experiences with Trophy so easy, it does all the work for you behind the scenes.

<h2 id="installing-trophy-sdk">
  Installing Trophy SDK
</h2>

To interact with Trophy from your code you'll use the Trophy SDK available in most major [programming languages](/api-reference/client-libraries).

Install the Trophy SDK:

<CodeGroup>
  ```bash Node theme={null}
  npm install @trophyso/node
  ```

  ```bash Ruby theme={null}
  gem install trophy_api_client
  ```

  ```bash Python theme={null}
  pip install trophy
  ```

  ```bash PHP theme={null}
  composer require trophyso/php
  ```

  ```bash Java (Gradle) theme={null}
  implementation 'so.trophy:trophy-java:1.0.0'
  ```

  ```bash Java (Maven) theme={null}
  <dependency>
    <groupId>so.trophy</groupId>
    <artifactId>trophy-java</artifactId>
    <version>1.0.0</version>
  </dependency>
  ```

  ```bash Go theme={null}
  go get github.com/trophy-so/trophy-go
  ```

  ```bash .NET (C#) theme={null}
  // .NET Core CLI
  dotnet add package Trophy

  // Nuget Package Manager
  nuget install Trophy

  // Visual Studio
  Install-Package Trophy
  ```
</CodeGroup>

Next, grab your API key from the Trophy [integration page](https://app.trophy.so/integration) and add this as a **server-side only** environment variable.

```bash theme={null}
TROPHY_API_KEY='*******'
```

<Warning>
  Make sure you **don't** expose your API key in client-side code.
</Warning>

<h2 id="tracking-user-interactions">
  Tracking User Interactions
</h2>

To track an event (user interaction) against your metric, use the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.trophy.so/api/metrics/flashcards-flipped/event \
       -H "X-API-KEY: <apiKey>" \
       -H "Content-Type: application/json" \
       -d '{
    "user": {
      "id": "18",
      "email": "user@example.com",
      "tz": "Europe/London"
    },
    "value": 750
  }'
  ```

  ```typescript Node theme={null}
  trophy.metrics.event("flashcards-flipped", {
    user: {
      id: "18",
      email: "user@example.com",
      tz: "Europe/London",
    },
    value: 750,
  });
  ```

  ```python Python theme={null}
  client.metrics.event(
      key="flashcards-flipped",
      user=EventRequestUser(
          id="18",
          email="user@example.com",
          tz="Europe/London",
      ),
      value=750.0,
  )
  ```

  ```php PHP theme={null}
  $user = new EventRequestUser([
      'id' => '18',
      'email' => 'user@example.com'
  ]);

  $request = new MetricsEventRequest([
      'user' => $user,
      'value' => 750
  ]);

  $trophy->metrics->event("flashcards-flipped", $request);
  ```

  ```java Java theme={null}
  MetricsEventRequest request = MetricsEventRequest.builder()
        .user(
          EventRequestUser.builder()
            .id("18")
            .email("user@example.com")
            .build()
        )
        .value(750)
        .build();

  EventResponse response = client.metrics().event("flashcards-flipped", request);
  ```

  ```go Go theme={null}
  response, err := client.Metrics.Event(
      "flashcards-flipped",
      &api.MetricsEventRequest{
          User: &api.EventRequestUser{
              Id: "18",
              Email: "user@example.com",
          },
          Value: 750,
      },
  )
  ```

  ```csharp C# theme={null}
  var user = new EventRequestUser {
     Id = "18",
     Email = "user@example.com"
  };

  var request = new MetricsEventRequest {
     User = user,
     Value = 750
  };

  await trophy.Metrics.EventAsync("flashcards-flipped", request);
  ```

  ```ruby Ruby theme={null}
  result = client.metrics.event(
    :key => 'flashcards-flipped',
    :user => {
      :id => '18',
      :email => 'user@example.com'
    },
    :value => 750
  )
  ```
</CodeGroup>

The response to this API call is the complete set of changes to any features you've built with Trophy, including any changes to their ranking in any leaderboards they are a part of.

```json Response [expandable] theme={null}
{
  "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623",
  "eventId": "0040fe51-6bce-4b44-b0ad-bddc4e123534",
  "total": 750,
  ...,
  "leaderboards": {
    "daily_champions": {
      "id": "0040fe51-6bce-4b44-b0ad-bddc4e123535",
      "key": "daily_champions",
      "name": "Daily Champions",
      "description": null,
      "rankBy": "metric",
      "runUnit": null,
      "runInterval": 0,
      "maxParticipants": 100,
      "metricName": "Flashcards Flipped",
      "metricKey": "flashcards-flipped",
      "threshold": 10,
      "start": "2025-01-01",
      "end": null,
      "previousRank": 50,
      "rank": 12
    }
  }
}
```

Validate this is working by checking the Trophy [dashboard](https://app.trophy.so).

<Frame>
  <img height="200" width="100%" noZoom src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/achievements-feature/top-users.png?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=bb6f28c04ba9e48d6bf8f93ed1271337" data-path="assets/guides/achievements-feature/top-users.png" />
</Frame>

<h2 id="displaying-leaderboards">
  Displaying Leaderboards
</h2>

You have a number of options for displaying leaderboards in your application. Here we'll look at the most common options.

<h3 id="pop-up-notifications">
  Pop-up Notifications
</h3>

We can use the response of the [metric change API](/api-reference/endpoints/metrics/send-a-metric-change-event) to show pop-up notifications (or 'toasts') when users move up the rankings.

Here's an example of this in action:

```ts Leaderboard Rank Up Pop-up theme={null}
// Sends event to Trophy
const response = await viewFlashcard();

if (!response) {
  return;
}

const leaderboard = response.leaderboards["daily_champions"];

if (!leaderboard) {
  return;
}

// Show toasts if the user moved up the leaderboard
if (leaderboard.rank > leaderboard.previousRank) {
  toast({
    title: "You're on the move!,
    description: `You moved up ${leaderboard.previousRank - leaderboard.rank} places!,
  });
}
```

<Tip>
  If you want to play sound effects, use the [HTML5 Audio
  API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and feel
  free to steal these [audio
  files](https://github.com/trophyso/example-study-platform/tree/demo/public/sounds)
  we recommend.
</Tip>

<h3 id="displaying-leaderboard-rankings">
  Displaying Leaderboard Rankings
</h3>

To fetch a leaderboard and its most up to date rankings, use the [leaderboard API](/api-reference/endpoints/leaderboards/get-leaderboard).

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.trophy.so/v1/leaderboards/{key} \
    --header 'X-API-KEY: <api-key>'
  ```

  ```typescript Node theme={null}
  trophy.leaderboards.get("daily_champions", {
    offset: 0,
    limit: 10,
    run: "2025-01-15"
  });
  ```

  ```python Python theme={null}
  client.leaderboards.get(
      key="daily_champions",
      offset=0,
      limit=10,
      run="2025-01-15"
  )
  ```

  ```php PHP theme={null}
  $trophy->leaderboards->get("daily_champions");
  ```

  ```java Java theme={null}
  client.leaderboards().get("daily_champions");
  ```

  ```go Go theme={null}
  response, err := client.Leaderboards.Get("daily_champions")
  ```

  ```csharp C# theme={null}
  trophy.Leaderboards.GetAsync("daily_champions");
  ```

  ```ruby Ruby theme={null}
  result = client.Leaderboards.Get(
    :key => "daily_champions"
  )
  ```
</CodeGroup>

<Tip>
  You can also use the `run` parameter with the date of the specific past 'run'
  of a leaderboard you want to fetch data for.
</Tip>

Here's an example of the data returned from the leaderboard API:

```json Response [expandable] theme={null}
{
  "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682",
  "name": "Weekly Flashcard Challenge",
  "key": "weekly-challenge",
  "rankBy": "metric",
  "metricKey": "flashcards-flipped",
  "metricName": "Flashcards Flipped",
  "pointsSystemKey": null,
  "pointsSystemName": null,
  "description": "Compete weekly to see who views the most flashcards",
  "status": "active",
  "start": "2025-01-01",
  "end": null,
  "maxParticipants": 100,
  "runUnit": "day",
  "runInterval": 7,
  "rankings": [
    {
      "userId": "user-123",
      "userName": "Alice Johnson",
      "rank": 1,
      "value": 5000
    },
    {
      "userId": "user-456",
      "userName": "Bob Smith",
      "rank": 2,
      "value": 4500
    },
    {
      "userId": "user-789",
      "userName": "Charlie Brown",
      "rank": 3,
      "value": 4200
    }
  ]
}
```

<h3 id="displaying-user-rank-history">
  Displaying User Rank History
</h3>

Use the [user leaderboard API](/api-reference/endpoints/users/get-a-users-leaderboard) to fetch data on how a specific user's rank has changed over time in a particular leaderboard.

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.trophy.so/v1/users/{id}/leaderboards/{key} \
    --header 'X-API-KEY: <api-key>'
  ```

  ```typescript Node theme={null}
  trophy.users.leaderboards("user-id", "daily_champions", {
    run: "2025-01-15"
  });
  ```

  ```python Python theme={null}
  client.users.leaderboards("user-id", "daily_champions", run="2025-01-15")
  ```

  ```php PHP theme={null}
  $trophy->users->leaderboards("user-id", "daily_champions")
  ```

  ```java Java theme={null}
  client.users().leaderboards("user-id", "daily_champions")
  ```

  ```go Go theme={null}
  response, err := client.Users.Leaderboards("user-id", "daily_champions")
  ```

  ```csharp C# theme={null}
  trophy.Users.Leaderboards("user-id", "daily_champions");
  ```

  ```ruby Ruby theme={null}
  result = client.Leaderboards.Get(
    :id => "user-id",
    :key => "daily_champions"
  )
  ```
</CodeGroup>

Here's an example of the data returned from the user leaderboard rankings API which includes the users current rank in the `rank` attribute and an array of previous ranks in the `history` attribute:

```json Response [expandable] theme={null}
{
  "id": "5100fe51-6bce-6j44-b0hs-bddc4e123682",
  "name": "Weekly Word Count Challenge",
  "key": "weekly-words",
  "rankBy": "metric",
  "metricKey": "flashcards-flipped",
  "metricName": "Flashcards Flipped",
  "pointsSystemKey": null,
  "pointsSystemName": null,
  "description": "Compete weekly to see who writes the most words",
  "start": "2025-01-01",
  "end": null,
  "maxParticipants": 100,
  "runUnit": "day",
  "runInterval": 7,
  "rank": 2,
  "value": 4500,
  "history": [
    {
      "timestamp": "2025-01-15T10:30:00Z",
      "previousRank": null,
      "rank": 5,
      "previousValue": null,
      "value": 1000
    },
    {
      "timestamp": "2025-01-15T14:15:00Z",
      "previousRank": 5,
      "rank": 3,
      "previousValue": 1000,
      "value": 3000
    },
    {
      "timestamp": "2025-01-15T18:45:00Z",
      "previousRank": 3,
      "rank": 2,
      "previousValue": 3000,
      "value": 4500
    }
  ]
}
```

<h2 id="analytics">
  Analytics
</h2>

The [leaderboards page](https://app.trophy.so/achievements) in Trophy shows how many users are actively participating in a leaderboard, as well as a measure of competitiveness based on how many rank changes are occurring.

The analytics page also shows a distribution of users scores to help identify clusters of users within rankings.

<Frame>
  <img height="200" width="100%" noZoom src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/leaderboards-feature/analytics.png?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=e5afcc20cf2595eecd0189b949d737f2" data-path="assets/guides/leaderboards-feature/analytics.png" />
</Frame>

Additionally the leaderboard rankings page shows current and past rankings of a leaderboard:

<Frame>
  <img height="200" width="100%" noZoom src="https://mintcdn.com/trophy/2khPIyZFxPyE7xgA/assets/guides/leaderboards-feature/rankings.png?fit=max&auto=format&n=2khPIyZFxPyE7xgA&q=85&s=28fcf9fef25ad6389a5bbc7903ba0e77" data-path="assets/guides/leaderboards-feature/rankings.png" />
</Frame>

<h2 id="get-support">
  Get Support
</h2>

Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help!
