# Search leaderboard

```http
GET /api/public/v1/competitions/igp24/leaderboard/search
```

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

Searches the frozen official IGP24 placement index by transitive group index `t`, real-root count `r`, or both. Results contain only placements credited to public participant or LMFDB baseline leaderboard rows; they never include unscored pairs, post-event validation, or Discovery-only records.

Use [Get label progress](./get-label-progress.md) to inspect aggregate coverage, including valid pairs without a public scoring placement. Use [Get remaining pairs](./get-remaining-pairs.md) for valid pairs that remain unsolved in the official result.

## Query parameters

| Parameter | Type | Required | Description |
| :--- | :--- | :---: | :--- |
| `t` | integer | Conditional | Transitive group index from `1` through `25000`. At least one of `t` or `r` is required. |
| `r` | integer | Conditional | Even real-root count from `0` through `24`. At least one of `t` or `r` is required. |
| `cursor` | string | No | Opaque cursor returned by the previous page. |
| `limit` | integer | No | Page size from `1` through `100`; defaults to `25`. |

Pass `nextCursor` unchanged with the same normalized `t` and `r` filters. Do not decode, modify, construct, or reuse it for another search.

When both filters are supplied, both must match. A valid filter with no credited placement returns an empty published page, not an error.

## Publication, ordering, and pagination

- `meta.published: false` is an information boundary: `items` is empty, `nextCursor` is `null`, and `meta.publishedAt` is `null`.
- When results are published, each `entry`, placement score, discriminant value, scoring input, and result order comes from the frozen official leaderboard. Post-event processing cannot add or change a result.
- Results are ordered by `placement.t` and `placement.r` ascending, then by unrounded canonical `placement.points` descending, and finally by stable `entry.teamId` ascending.
- A cursor is bound to the published result and normalized filters. Replaying a page returns the same frozen results in the same order.
- Current `entry.teamName`, `entry.teamNumber`, `entry.teamDetails`, and reference-only `placement.isSolvable` values may be corrected without changing result order or cursor meaning.

The endpoint remains readable while IGP24 is publicly visible, including after its status changes to closed. Draft competitions are not publicly visible.

## Response fields

### Data

| 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. |
| `items` | [`Igp24SearchResult`](#igp24searchresult)[] | Current page in frozen official search order. |
| `nextCursor` | string \| null | Opaque cursor for the next page, or `null` when traversal is complete. |
| `meta.published` | boolean | Whether the frozen official leaderboard is public. |
| `meta.publishedAt` | string \| null | ISO 8601 UTC publication time; non-null exactly when `meta.published` is `true`. |

### `Igp24SearchResult`

| Field | Type | Description |
| :--- | :--- | :--- |
| `entry` | [Leaderboard entry](./get-leaderboard.md#igp24leaderboardentry) | Matching participant or baseline row from the frozen official leaderboard. |
| `placement` | [`Igp24Placement`](./get-team-placements.md#igp24placement) | Matching official placement. Its scoring fields follow the shared discriminant and baseline rules. |

`entry.entryKind` explicitly distinguishes participant and LMFDB baseline results. The baseline always has `entry.teamDetails: null`. Participant details follow the shared [privacy rules](../../competitions.md#leaderboard-team-details-and-privacy); no account owner ID, membership record, or full user object is returned.

Use `entry.teamId` with [Get team placements](./get-team-placements.md) to traverse all placements for one result owner.

## Example request

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

## Published response

```json
{
  "ok": true,
  "data": {
    "query": {
      "t": 105,
      "r": 12
    },
    "items": [
      {
        "entry": {
          "rank": 7,
          "entryKind": "participant",
          "teamId": "teamv2_a7a863ce80e6447881ad068bab82498e",
          "teamNumber": "IGP24-T00007",
          "teamName": "Galois Wranglers",
          "score": 4.7321,
          "scoreablePairs": 12,
          "teamDetails": {
            "members": [
              {
                "name": "Alice Example"
              }
            ]
          }
        },
        "placement": {
          "t": 105,
          "r": 12,
          "label": "24T105",
          "points": 1,
          "kTeams": 2,
          "scoringDiscAbs": "100",
          "minScoringDiscAbs": "100",
          "discSource": "exact_nfdisc",
          "isSolvable": true,
          "baselineUnlocked": true,
          "baselineDiscAbs": "120"
        }
      }
    ],
    "nextCursor": null,
    "meta": {
      "published": true,
      "publishedAt": "2026-08-18T12:00:00Z"
    }
  }
}
```

## Published response without matches

```json
{
  "ok": true,
  "data": {
    "query": {
      "t": 25000,
      "r": 24
    },
    "items": [],
    "nextCursor": null,
    "meta": {
      "published": true,
      "publishedAt": "2026-08-18T12:00:00Z"
    }
  }
}
```

## Unpublished response

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

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `400` | `MALFORMED_BODY` | A filter or `limit` cannot be parsed, `limit` is outside `1` through `100`, or `cursor` is invalid or belongs to another search. |
| `404` | `NOT_FOUND` | IGP24 does not exist or is not publicly visible as active or closed. |
| `422` | `RESOURCE_FIELD_INVALID` | Neither `t` nor `r` was supplied, or a filter is outside its documented domain. |
| `503` | `IGP24_SERVICE_UNAVAILABLE` | The frozen official leaderboard service is temporarily unavailable. |

See [Errors](../../../errors.md) for authentication, scope, rate-limit, and standard error-envelope behavior.
