> For the complete documentation index, see [llms.txt](https://docs.domino.run/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.domino.run/docs/developer-resources/portal-leaderboard-api.md).

# Portal Leaderboard API

The Leaderboard API lets you retrieve your community's merged leaderboard rankings and look up individual user positions. All leaderboard data is aggregated across every visible leaderboard in your community.

## Overview

Use the Leaderboard API to:

* Display leaderboard rankings in your own UI or application
* Look up a specific user's rank and points by user ID
* Look up a user by social ID or username (Discord, Twitter, Telegram, Reddit, email, etc.)
* Filter rankings by time period (daily, weekly, bi-weekly, monthly, or all-time)
* Paginate through large leaderboards with cursor-based pagination

## Authentication

All Leaderboard API requests require a **Community API Key** passed in the `x-api-key` header. You can create Community API Keys from your community dashboard.

```
x-api-key: <community-api-key>
```

{% hint style="info" %}
Replace `your-community` in the URLs below with your community slug. For example, if your portal is at `acme.domino.page`, your slug is `acme`.
{% endhint %}

***

## Get Leaderboard Rankings

Retrieves the full leaderboard with all users ranked by the default leaderboard, including per-leaderboard point breakdowns.

```
GET https://your-community.domino.page/api/v1/leaderboard
```

### Query Parameters

| Parameter       | Type   | Required | Description                                                                                           |
| --------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `limit`         | Number | No       | Number of users to return per page (1–100, default `20`)                                              |
| `cursor`        | Number | No       | Pagination cursor returned from a previous request. Omit for the first page                           |
| `view`          | String | No       | Time period filter. One of `all_time`, `daily`, `weekly`, `bi_weekly`, `monthly` (default `all_time`) |
| `leaderboardId` | String | No       | Rank by this leaderboard instead of the default one. Must be a visible leaderboard in your community  |

### Response

| Property                           | Type    | Description                                                                                                         |
| ---------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `leaderboards`                     | Array   | All visible leaderboards for the community                                                                          |
| `leaderboards[].id`                | String  | Leaderboard ID                                                                                                      |
| `leaderboards[].name`              | String  | Leaderboard name                                                                                                    |
| `leaderboards[].portalColumnLabel` | String  | Custom column label displayed in the portal (may be `null`)                                                         |
| `leaderboards[].isDefault`         | Boolean | Whether this is the default leaderboard used for ranking                                                            |
| `users`                            | Array   | Ranked list of users                                                                                                |
| `users[].user`                     | Object  | User profile                                                                                                        |
| `users[].user.id`                  | String  | User ID                                                                                                             |
| `users[].user.name`                | String  | Display name                                                                                                        |
| `users[].user.imageUrl`            | String  | Profile image URL (may be `null`)                                                                                   |
| `users[].rank`                     | Number  | User's rank based on the default leaderboard                                                                        |
| `users[].pointsByLeaderboard`      | Object  | Points keyed by leaderboard ID, e.g. `{ "lb-id-1": 500, "lb-id-2": 120 }`                                           |
| `users[].rankByLeaderboard`        | Object  | Rank keyed by leaderboard ID, e.g. `{ "lb-id-1": 1, "lb-id-2": 3 }`                                                 |
| `users[].tierData`                 | Object  | The user's tier on the ranking leaderboard, or `null` if that leaderboard has tiers turned off. See [Tiers](#tiers) |
| `nextCursor`                       | Number  | Cursor value for the next page, or `null` if there are no more results                                              |
| `hasMore`                          | Boolean | Whether more results are available                                                                                  |

### Example Response

```json
{
  "leaderboards": [
    {
      "id": "lb-abc123",
      "name": "Main Leaderboard",
      "portalColumnLabel": "XP",
      "isDefault": true,
      "startDate": null,
      "endDate": null
    },
    {
      "id": "lb-def456",
      "name": "Bonus Points",
      "portalColumnLabel": "Bonus",
      "isDefault": false,
      "startDate": null,
      "endDate": null
    }
  ],
  "users": [
    {
      "user": {
        "id": "user-001",
        "name": "Alice",
        "imageUrl": "https://cdn.example.com/alice.png"
      },
      "rank": 1,
      "pointsByLeaderboard": {
        "lb-abc123": 1500,
        "lb-def456": 300
      },
      "rankByLeaderboard": {
        "lb-abc123": 1,
        "lb-def456": 3
      }
    },
    {
      "user": {
        "id": "user-002",
        "name": "Bob",
        "imageUrl": null
      },
      "rank": 2,
      "pointsByLeaderboard": {
        "lb-abc123": 1200,
        "lb-def456": 450
      },
      "rankByLeaderboard": {
        "lb-abc123": 2,
        "lb-def456": 1
      }
    }
  ],
  "nextCursor": 2,
  "hasMore": true
}
```

### Example Request

```javascript
const response = await fetch('https://acme.domino.page/api/v1/leaderboard?limit=10&view=weekly', {
  headers: {
    'x-api-key': 'your-community-api-key'
  }
});

const leaderboard = await response.json();
console.log(leaderboard.users); // Top 10 users for this week
```

### Paginating Through Results

```javascript
async function getAllRankings() {
  const allUsers = [];
  let cursor = undefined;

  do {
    const params = new URLSearchParams({ limit: '100' });
    if (cursor) params.set('cursor', String(cursor));

    const response = await fetch(
      `https://acme.domino.page/api/v1/leaderboard?${params}`,
      { headers: { 'x-api-key': 'your-community-api-key' } }
    );

    const page = await response.json();
    allUsers.push(...page.users);
    cursor = page.nextCursor;
  } while (cursor);

  return allUsers;
}
```

***

## Get User Ranking

Retrieves a single user's rank and points across all visible leaderboards.

```
GET https://your-community.domino.page/api/v1/leaderboard/:userId
```

### Path Parameters

| Parameter | Type   | Required | Description   |
| --------- | ------ | -------- | ------------- |
| `userId`  | String | Yes      | The user's ID |

### Query Parameters

| Parameter       | Type   | Required | Description                                                                                           |
| --------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `view`          | String | No       | Time period filter. One of `all_time`, `daily`, `weekly`, `bi_weekly`, `monthly` (default `all_time`) |
| `leaderboardId` | String | No       | Rank by this leaderboard instead of the default one. Must be a visible leaderboard in your community  |

### Response

| Property                              | Type   | Description                                                                                                         |
| ------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `user`                                | Object | User profile                                                                                                        |
| `user.id`                             | String | User ID                                                                                                             |
| `user.name`                           | String | Display name                                                                                                        |
| `user.imageUrl`                       | String | Profile image URL (may be `null`)                                                                                   |
| `rank`                                | Number | User's rank based on the default leaderboard                                                                        |
| `pointsByLeaderboard`                 | Object | Points keyed by leaderboard ID, e.g. `{ "lb-id-1": 500, "lb-id-2": 120 }`                                           |
| `rankByLeaderboard`                   | Object | Rank keyed by leaderboard ID, e.g. `{ "lb-id-1": 1, "lb-id-2": 3 }`                                                 |
| `tierData`                            | Object | The user's tier on the ranking leaderboard, or `null` if that leaderboard has tiers turned off. See [Tiers](#tiers) |
| `referrals`                           | Object | What this user's invites earned them, or `null` if the figures could not be read. See [Referrals](#referrals)       |
| `referrals.invitedCount`              | Number | Members who signed up with this user's referral                                                                     |
| `referrals.sharedPoints`              | Number | Points this user received as a share of their invitees' claims, over the leaderboards in this response              |
| `referrals.sharedPointsByLeaderboard` | Object | Shared points keyed by leaderboard ID                                                                               |
| `completions`                         | Object | The user's successful quest claims, or `null` if the figures could not be read. See [Completions](#completions)     |
| `completions.total`                   | Number | Successful claims across every quest in the community                                                               |
| `completions.byQuest`                 | Array  | Per-quest breakdown with `questId`, `name`, `title`, `times`, and `lastCompletedAt`, highest `times` first          |

### Example Response

```json
{
  "user": {
    "id": "user-001",
    "name": "Alice",
    "imageUrl": "https://cdn.example.com/alice.png"
  },
  "rank": 1,
  "pointsByLeaderboard": {
    "lb-abc123": 1500,
    "lb-def456": 300
  },
  "rankByLeaderboard": {
    "lb-abc123": 1,
    "lb-def456": 3
  },
  "tierData": {
    "level": 12,
    "tier": { "name": "Silver", "color": "slate" },
    "xpForCurrentLevel": 1440,
    "xpForNextLevel": 1690,
    "xpProgress": 24
  },
  "referrals": {
    "invitedCount": 10,
    "sharedPoints": 500,
    "sharedPointsByLeaderboard": {
      "lb-abc123": 480,
      "lb-def456": 20
    }
  },
  "completions": {
    "total": 42,
    "byQuest": [
      {
        "questId": "quest-001",
        "name": "daily-check-in",
        "title": "Daily Check-in",
        "times": 14,
        "lastCompletedAt": "2026-10-06T08:12:44.000Z"
      },
      {
        "questId": "quest-002",
        "name": "follow-on-x",
        "title": "Follow us on X",
        "times": 1,
        "lastCompletedAt": "2026-09-21T17:03:10.000Z"
      }
    ]
  }
}
```

### Example Request

```javascript
const userId = 'user-001';
const response = await fetch(`https://acme.domino.page/api/v1/leaderboard/${userId}?view=monthly`, {
  headers: {
    'x-api-key': 'your-community-api-key'
  }
});

const ranking = await response.json();
console.log(`Rank #${ranking.rank}`);
```

***

## Look Up User Ranking by Social ID

Finds a user by a social platform ID or username and returns their ranking. This is useful when you don't have the Domino user ID but know the user's social identity.

```
GET https://your-community.domino.page/api/v1/leaderboard/lookup
```

At least one identifier query parameter is required. If multiple identifiers are provided, a user matching **any** of them will be returned.

### Query Parameters — Identifiers

| Parameter          | Type   | Description                  |
| ------------------ | ------ | ---------------------------- |
| `discordId`        | String | Discord user ID              |
| `discordUsername`  | String | Discord username             |
| `twitterId`        | String | Twitter/X user ID            |
| `twitterUsername`  | String | Twitter/X username           |
| `telegramId`       | String | Telegram user ID             |
| `telegramUsername` | String | Telegram username            |
| `redditId`         | String | Reddit user ID               |
| `redditUsername`   | String | Reddit username              |
| `externalId`       | String | External ID from your system |
| `email`            | String | User's email address         |

### Query Parameters — Options

| Parameter       | Type   | Required | Description                                                                                           |
| --------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `view`          | String | No       | Time period filter. One of `all_time`, `daily`, `weekly`, `bi_weekly`, `monthly` (default `all_time`) |
| `leaderboardId` | String | No       | Rank by this leaderboard instead of the default one. Must be a visible leaderboard in your community  |

### Response

The response shape is the same as [Get User Ranking](#get-user-ranking).

### Example Response

```json
{
  "user": {
    "id": "user-001",
    "name": "Alice",
    "imageUrl": "https://cdn.example.com/alice.png"
  },
  "rank": 1,
  "pointsByLeaderboard": {
    "lb-abc123": 1500,
    "lb-def456": 300
  },
  "rankByLeaderboard": {
    "lb-abc123": 1,
    "lb-def456": 3
  },
  "tierData": {
    "level": 12,
    "tier": { "name": "Silver", "color": "slate" },
    "xpForCurrentLevel": 1440,
    "xpForNextLevel": 1690,
    "xpProgress": 24
  },
  "referrals": {
    "invitedCount": 10,
    "sharedPoints": 500,
    "sharedPointsByLeaderboard": {
      "lb-abc123": 480,
      "lb-def456": 20
    }
  },
  "completions": {
    "total": 42,
    "byQuest": [
      {
        "questId": "quest-001",
        "name": "daily-check-in",
        "title": "Daily Check-in",
        "times": 14,
        "lastCompletedAt": "2026-10-06T08:12:44.000Z"
      },
      {
        "questId": "quest-002",
        "name": "follow-on-x",
        "title": "Follow us on X",
        "times": 1,
        "lastCompletedAt": "2026-09-21T17:03:10.000Z"
      }
    ]
  }
}
```

### Example Requests

**Look up by Discord ID:**

```javascript
const response = await fetch(
  'https://acme.domino.page/api/v1/leaderboard/lookup?discordId=123456789012345678',
  { headers: { 'x-api-key': 'your-community-api-key' } }
);

const ranking = await response.json();
console.log(`Rank #${ranking.rank}`);
```

**Look up by Twitter username:**

```javascript
const response = await fetch(
  'https://acme.domino.page/api/v1/leaderboard/lookup?twitterUsername=alice_dev',
  { headers: { 'x-api-key': 'your-community-api-key' } }
);
```

**Look up by email with weekly view:**

```javascript
const response = await fetch(
  'https://acme.domino.page/api/v1/leaderboard/lookup?email=alice@example.com&view=weekly',
  { headers: { 'x-api-key': 'your-community-api-key' } }
);
```

**Look up by external ID (your system's user ID):**

```javascript
const response = await fetch(
  'https://acme.domino.page/api/v1/leaderboard/lookup?externalId=usr_abc123',
  { headers: { 'x-api-key': 'your-community-api-key' } }
);
```

***

## Referrals

`referrals` reports a single user's referral results, so you can show a member what their invites earned them rather than only their blended points total.

| Property                              | Type   | Description                                                                                            |
| ------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `referrals.invitedCount`              | Number | Members who signed up with this user's referral                                                        |
| `referrals.sharedPoints`              | Number | Points this user received as a share of their invitees' claims, over the leaderboards in this response |
| `referrals.sharedPointsByLeaderboard` | Object | The same points keyed by leaderboard ID, for the leaderboards in this response                         |

Referral sharing is off until you turn it on in your community settings, where you also set the percentage of an invitee's points that goes to their referrer. `sharedPoints` counts only claims made after sharing was turned on, excludes shares that were later reversed because a claim was revoked, and reflects the percentage that was in force at the time of each claim. Changing the percentage never rewrites shares already granted.

Referral points are granted into the same leaderboards as the user's own quest points, so they are already counted in the user's leaderboard points. Show `sharedPoints` as a breakdown of that total, not as something to add on top of it. Shares granted into a hidden leaderboard are left out, because hidden leaderboards are absent from `pointsByLeaderboard` too.

`referrals` is always all-time. Unlike `rank` and `pointsByLeaderboard`, it is **not** narrowed by `view`, so `?view=weekly` returns weekly points beside an all-time referral total.

***

## Completions

`completions` reports how many times the user successfully claimed each quest. Repeatable quests count once per completion, so `times` can be greater than 1. Failed and pending claims are not counted.

| Property                                | Type   | Description                                           |
| --------------------------------------- | ------ | ----------------------------------------------------- |
| `completions.total`                     | Number | Successful claims across every quest in the community |
| `completions.byQuest`                   | Array  | Per-quest breakdown, highest `times` first            |
| `completions.byQuest[].questId`         | String | Quest ID                                              |
| `completions.byQuest[].name`            | String | Quest name                                            |
| `completions.byQuest[].title`           | String | Quest title as shown in the portal (may be empty)     |
| `completions.byQuest[].times`           | Number | How many times the user completed this quest          |
| `completions.byQuest[].lastCompletedAt` | String | ISO timestamp of the most recent successful claim     |

Quests the user has never completed are omitted rather than reported with `times: 0`.

`times` counts a quest across all of its deployments, so redeploying a quest does not reset or split the count. Only successful claims count, so these numbers are lower than the claim count on the dashboard's Members page, which also includes failed and pending attempts. They are also not the per-deployment counts that drive claim limits and repeat gating.

`completions` is always all-time and, like `referrals`, is **not** narrowed by `view`.

`referrals` and `completions` are supplementary to the ranking, so if either one cannot be read the field comes back as `null` and the rest of the response is still served. A `null` therefore means "unknown", never "nothing earned" — treat it differently from `0`.

***

## Tiers

If a leaderboard has tiers turned on (in the leaderboard settings), every ranking response includes `tierData` for the ranking leaderboard. That's the default leaderboard, or the one passed as `leaderboardId`. It matches the tier shown in your portal. When tiers are off, `tierData` is `null`.

| Property                     | Type   | Description                                                  |
| ---------------------------- | ------ | ------------------------------------------------------------ |
| `tierData.level`             | Number | The user's level                                             |
| `tierData.tier`              | Object | The tier for that level (`null` if no tier covers the level) |
| `tierData.tier.name`         | String | Tier name, e.g. `Gold`                                       |
| `tierData.tier.color`        | String | Tier color as configured, e.g. `yellow`                      |
| `tierData.xpForCurrentLevel` | Number | Points needed to reach the current level                     |
| `tierData.xpForNextLevel`    | Number | Points needed to reach the next level                        |
| `tierData.xpProgress`        | Number | Progress towards the next level, in percent (0–100)          |

{% hint style="info" %}
Tiers are calculated from the user's points on the ranking leaderboard in the selected `view`. Use `view=all_time` (the default) for the user's overall tier.
{% endhint %}

***

## Error Responses

All endpoints return standard error responses:

| Status | Description                                      |
| ------ | ------------------------------------------------ |
| `400`  | Missing required parameters (e.g. no identifier) |
| `401`  | Missing or invalid API key                       |
| `404`  | Community or user not found                      |
| `500`  | Internal server error                            |

```json
{
  "error": "User not found in leaderboard"
}
```

***

## Related Resources

* [Portal API integration guide](/docs/developer-resources/portal-api-integration-guide.md)
* [Authenticating users with the portal API](/docs/developer-resources/authenticating-users-with-the-portal-api.md)
* [Create Leaderboards](/docs/domino-portal/creating-leaderboards.md)
