# Search the IGP24 leaderboard

```http
GET /api/public/v1/competitions/{competitionId}/leaderboard/search
```

**Scope**: `competition.read`.

Returns public team placements matching an IGP24 transitive group index `t`, a real-root count `r`, or both. This endpoint is not paginated.

The public team DTO deliberately redacts account and member identities: `ownerSairId` is always `null` and `members` is always empty.

## Parameters

| Parameter | Location | Type | Required | Default | Description |
| :--- | :--- | :--- | :---: | :--- | :--- |
| `competitionId` | path | string | Yes | — | IGP24 competition ID. Use `igp24`. |
| `t` | query | integer | Conditionally | — | Transitive group index. At least one of `t` or `r` is required. |
| `r` | query | integer | Conditionally | — | Real-root count/signature. At least one of `t` or `r` is required. |

## Response DTO

```ts
type SearchIgp24LeaderboardResponse = {
  ok: true;
  data: {
    query: {
      t: number | null;
      r: number | null;
    };
    placements: Igp24SearchPlacement[];
    meta: Igp24PublicationMeta;
  };
};

type Igp24PublicationMeta = {
  published: boolean;
  publishedAt: string | null;
};

type Igp24SearchPlacement = {
  t: number;
  r: number;
  label: string;
  points: number;
  kTeams: number;
  scoringDiscAbs: string;
  minScoringDiscAbs: string;
  discSource: 'exact_nfdisc' | 'mixed_disc';
  isSolvable: boolean | null;
  baselineUnlocked?: boolean;
  baselineDiscAbs?: string | null;
  team: Igp24PublicTeam;
};

type Igp24PublicTeam = {
  teamId: string;
  teamNumber: string | null;
  teamName: string;
  ownerSairId: null;
  members: [];
};
```

## Response fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `query.t` | integer \| null | Applied `t` filter, or `null` when omitted. |
| `query.r` | integer \| null | Applied `r` filter, or `null` when omitted. |
| `placements[]` | `Igp24SearchPlacement[]` | Matching participant and baseline-team placements. Empty when no pair matches or the leaderboard is unpublished. |
| `meta.published` | boolean | Whether the leaderboard is published. |
| `meta.publishedAt` | string \| null | ISO 8601 UTC publication time, or `null` before publication. |

### `Igp24SearchPlacement`

| Field | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| `t` | integer | Yes | Transitive group index. |
| `r` | integer | Yes | Real-root count/signature. |
| `label` | string | Yes | Transitive group label, for example `24T105`. |
| `points` | number | Yes | Points credited for the placement, rounded to six decimal places. |
| `kTeams` | integer | Yes | Number of participant teams credited for the pair. |
| `scoringDiscAbs` | string | Yes | Team's absolute scoring discriminant as a decimal string. |
| `minScoringDiscAbs` | string | Yes | Minimum absolute scoring discriminant currently credited for the pair. |
| `discSource` | `exact_nfdisc` \| `mixed_disc` | Yes | Source used for the scoring discriminant. |
| `isSolvable` | boolean \| null | Yes | Known solvability flag for the label, or `null`. |
| `baselineUnlocked` | boolean | No | Present when baseline information is exposed. |
| `baselineDiscAbs` | string \| null | No | Absolute baseline discriminant as a decimal string. |
| `team` | `Igp24PublicTeam` | Yes | Public, identity-redacted team display data. |

### `Igp24PublicTeam`

| Field | Type | Description |
| :--- | :--- | :--- |
| `teamId` | string | Stable team ID. |
| `teamNumber` | string \| null | Public competition team number; `null` for the display-only baseline team. |
| `teamName` | string | Public team display name. May be empty when display enrichment is unavailable. |
| `ownerSairId` | null | Always `null`; owner identity is not public. |
| `members` | empty array | Always empty; member identities are not public. |

## Example

```bash
curl "https://api.sair.foundation/api/public/v1/competitions/igp24/leaderboard/search?t=105&r=12" \
  -H "Authorization: Bearer $SAIR_API_KEY"
```

```json
{
  "ok": true,
  "data": {
    "query": {
      "t": 105,
      "r": 12
    },
    "placements": [
      {
        "t": 105,
        "r": 12,
        "label": "24T105",
        "points": 1,
        "kTeams": 1,
        "scoringDiscAbs": "100",
        "minScoringDiscAbs": "100",
        "discSource": "exact_nfdisc",
        "isSolvable": true,
        "team": {
          "teamId": "teamv2_public",
          "teamNumber": "IGP24-T00001",
          "teamName": "Public Team",
          "ownerSairId": null,
          "members": []
        }
      }
    ],
    "meta": {
      "published": true,
      "publishedAt": "2026-05-19T00:00:00Z"
    }
  }
}
```

## Before publication

```json
{
  "ok": true,
  "data": {
    "query": {
      "t": 105,
      "r": 12
    },
    "placements": [],
    "meta": {
      "published": false,
      "publishedAt": null
    }
  }
}
```

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `400` | `MALFORMED_BODY` | `t` or `r` cannot be parsed as an integer. |
| `404` | `NOT_FOUND` | The competition is unavailable or is not an IGP24 competition. |
| `422` | `RESOURCE_FIELD_INVALID` | Neither `t` nor `r` was supplied, or a filter is semantically invalid. |

See [Errors](../../../errors.md) for authentication, authorization, and common errors.
