# Get leaderboard

```http
GET /api/public/v1/competitions/acc/leaderboard
```

**Scope**: `competition.read`. An API key is required.

AC and Stable AC have independent rankings, scores and counts of challenges with points; there is no combined leaderboard.

Returns authoritative team ranks and exact-score display values from one published scoring snapshot.

## Query parameters

| Parameter    | Type and rules                                                                                                    |
| :----------- | :---------------------------------------------------------------------------------------------------------------- |
| `problem`    | Required exactly once: `ac` or `stable_ac`. Missing, empty, unknown, or repeated values return `400 E_MALFORMED`. |
| `limit`      | Integer `1`–`100`; default `25`.                                                                                  |
| `snapshotId` | Optional nonempty opaque snapshot returned by any leaderboard operation. Pins the score version across endpoints. |
| `cursor`     | Opaque `nextCursor` from the preceding page; omit for a fresh snapshot.                                           |

Preserve both `snapshotId` and the cursor while continuing a snapshot, and render server order. Start without either to refresh all related views. The [scoring rules](../acc.md#scoring-and-snapshots) explain ties and why rounded score strings cannot be used to recompute ranks.

## Response fields

| Field         | Type and meaning                                                                                       |
| :------------ | :----------------------------------------------------------------------------------------------------- |
| `problem`     | `"ac"` or `"stable_ac"` — The selected problem. All rows and counts belong to it.                      |
| `published`   | `boolean` — Whether public results may be shown.                                                       |
| `generatedAt` | UTC ISO 8601 `string` or `null` — Snapshot generation time; null before a scoring run.                 |
| `snapshotId`  | Nonempty `string` when published, even for empty boards and final pages; `null` when unpublished.      |
| `items`       | Array of [LeaderboardEntry](#leaderboardentry) objects. Empty when unpublished.                        |
| `nextCursor`  | `string` or `null`. Null means no next page.                                                           |
| `totalCount`  | Non-negative `integer` — Total matching items in the snapshot, not page length; zero when unpublished. |

### LeaderboardEntry

| Field              | Type and meaning                                                                                                                       |
| :----------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
| `rank`             | Positive `integer` — Official unique rank in this snapshot.                                                                            |
| `team`             | [PublicTeam](../acc.md#publicteam).                                                                                                    |
| `teamDetails`      | [TeamMemberDetails](#teammemberdetails) or `null` — Always present; the team's currently effective information sharing, beside `team`. |
| `score`            | `string` with exactly four fractional digits, such as `"0.2500"`.                                                                      |
| `currentBestCount` | Non-negative `integer` — Challenges where it currently holds a shortest solution, including ties.                                      |

Teams without a successful scored solution have no row. A team that has lost every minimum can remain present with score `"0.0000"`.

### TeamMemberDetails

`teamDetails` follows the [shared team disclosure rules](../../competitions.md#leaderboard-team-details-and-privacy). It is `null` when sharing is not effective or no members can be publicly shown. When present, it contains at least one publicly shared active member. Render only the fields returned.

| Field                 | Type and meaning                                                                                                                                                                      |
| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `members`             | Required non-empty array of publicly shared active members, in the team's chosen order. |
| `memberCount`         | Optional non-negative `integer` — Total active members, including those not shared. Omitted when competition policy does not allow disclosure; do not infer it from `members.length`. |
| `members[].name`      | Required non-empty `string` — The currently permitted display or full legal name.                                                                                                     |
| `members[].avatarUrl` | Optional non-empty `string` — Included only when a custom avatar is available and its sharing is permitted; otherwise omitted. |
| `members[].email`     | Optional `string` — Included only when the current verified email is permitted and its disclosure confirmation remains valid.                                                         |

Scores and pagination remain tied to one scoring snapshot. Shared member information is evaluated on every request, including requests with an older valid cursor: withdrawals and changes to membership, account details, or sharing policy take effect without another scoring run. Internal member IDs, roles, private members, and sharing settings are not included.

## Example request

```bash
export SAIR_API_BASE="https://api.sair.foundation/api/public/v1"

curl "$SAIR_API_BASE/competitions/acc/leaderboard?problem=ac&limit=25" \
  -H "Authorization: Bearer $SAIR_API_KEY"
```

## Example response

```json
{
  "ok": true,
  "data": {
    "problem": "ac",
    "published": true,
    "snapshotId": "example-snapshot",
    "generatedAt": "2026-09-10T12:00:02Z",
    "items": [
      {
        "rank": 1,
        "team": {
          "teamId": "team-17",
          "teamName": "Example Team",
          "teamNumber": "ACC-0017"
        },
        "teamDetails": {
          "memberCount": 2,
          "members": [
            {
              "name": "Example Researcher"
            }
          ]
        },
        "score": "1.0000",
        "currentBestCount": 1
      }
    ],
    "nextCursor": null,
    "totalCount": 1
  }
}
```

## Errors

Missing, empty, unknown, or repeated `problem` returns `400 E_MALFORMED`. Cursors cannot be reused across problems.

| HTTP  | Code               | Meaning                                                                                                                                                               |
| :---: | :----------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | `E_MALFORMED`      | Invalid query value or out-of-range page size. Missing, unknown, or repeated `problem` is rejected. Preserve the returned error details.                              |
| `400` | `INVALID_CURSOR`   | Unknown cursor, wrong operation or problem, or a cursor inconsistent with the supplied snapshot ID.                                                                   |
| `409` | `SNAPSHOT_EXPIRED` | Snapshot or cursor expired after 15 minutes, or the score version is unavailable. Discard its snapshot ID and all related cursors and reload from the latest version. |

See [shared errors](../acc.md#shared-errors) for authentication, scope, availability, and configuration errors.
