# Get challenge progress

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

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

Returns published progress for scored official challenges, including unsolved challenges. Filter by challenge ID or status and read any page directly. Training challenges are excluded.

For the full pool without pagination, use [Get complete challenge snapshot](./get-challenge-snapshot.md). Both endpoints support `include=initialRelators` to add original words.

## Query parameters

| Parameter | Type and rules |
| :--- | :--- |
| `problem` | Required exactly once: `ac` or `stable_ac`. |
| `pageIndex` | Integer starting at `1`; default `1`. |
| `pageSize` | Integer `1`–`100`; default `25`. |
| `snapshotId` | Optional nonempty snapshot token from a published page. Omit to read the latest score version. |
| `challengeId` | Optional exact, case-sensitive ID. No prefix or substring search. Unknown, wrong-problem, or unscored IDs produce no items. |
| `status` | Optional `"solved"` or `"unsolved"`; omit for both. |
| `include` | Optional, at most once: `initialRelators`. Adds the original words to each returned item. Omit for compact progress. |

Results are ordered by `challengeId` ascending. Filters apply before pagination. Pages past the end return empty `items`, the actual matching `totalCount`, and the requested page index. Unknown, repeated, empty, or invalid query parameters return `400 E_MALFORMED`.

A published response contains an opaque `snapshotId` valid for 15 minutes. Reuse it to read multiple pages in parallel from the same score version; page reads do not extend its expiry. If the selected version expires or becomes unavailable, the request returns `409 SNAPSHOT_EXPIRED`. Refresh by starting a new set of reads without `snapshotId`. Before the first scoring run, a published frozen pool is entirely unsolved, with a nonempty token and null `generatedAt`.

## 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` — Score generation time; null before a scoring run or when unpublished. |
| `snapshotId` | Nonempty `string` when published; `null` when unpublished. |
| `items` | Array of [ChallengeProgress](#challengeprogress) objects. Empty when unpublished. |
| `totalCount` | Non-negative `integer` — Total matching items in the snapshot, not page length; zero when unpublished. |
| `pageIndex` | Positive `integer` — Requested page index. |
| `pageSize` | Integer `1`–`100` — Requested page size. |

### ChallengeProgress

| Field | Type and meaning |
| :--- | :--- |
| `challengeId` | `string` — Official challenge ID. |
| `problem` | `"ac"` or `"stable_ac"`. |
| `status` | `"solved"` or `"unsolved"`. |
| `currentBestLength` | Non-negative `integer` or `null` — Shortest verified move count; zero is valid. |
| `kTeams` | Non-negative `integer` — Distinct teams tied at the current minimum; `0` when unsolved. |
| `initialRelators` | `[number[], number[]]` — Present only with `include=initialRelators`: the two original words as ordered arrays of signed integers. |

Unsolved challenges have null `currentBestLength` and zero `kTeams`; solved challenges have a non-null length and at least one team.

The expansion applies to both solved and unsolved challenges and does not change the filters, order, or pagination. In the official rank-2 pool, `1` and `2` represent `x` and `y`; negative values represent their inverses. An empty word is an empty array. Preserve word order and signs. Without `include`, `initialRelators` is omitted, not returned as null. Expansion does not add `targetRelators` or submitted solution paths.

Responses do not contain submitted moves, original submission files, or a full tied-team list.

## Example request

Read progress for one challenge:

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

curl "$SAIR_API_BASE/competitions/acc/discoveries?problem=ac&challengeId=ac-00001&pageIndex=1&pageSize=1" \
  -H "Authorization: Bearer $SAIR_API_KEY"
```

To read the original words for one challenge, add `include=initialRelators` to an exact challenge-ID query:

```bash
curl "$SAIR_API_BASE/competitions/acc/discoveries?problem=ac&challengeId=ac-08491&pageSize=1&include=initialRelators" \
  -H "Authorization: Bearer $SAIR_API_KEY"
```

Each returned item then includes `initialRelators`; the surrounding response fields are unchanged. See the [expanded item example](./get-challenge-snapshot.md#example-response). Empty, invalid, or repeated `include` values return `400 E_MALFORMED`.

## Example response

Values illustrate the contract, not a live challenge or reusable snapshot token.

```json
{
  "ok": true,
  "data": {
    "problem": "ac",
    "published": true,
    "generatedAt": null,
    "snapshotId": "example-score-snapshot",
    "items": [
      {
        "challengeId": "ac-00001",
        "problem": "ac",
        "status": "unsolved",
        "currentBestLength": null,
        "kTeams": 0
      }
    ],
    "totalCount": 1,
    "pageIndex": 1,
    "pageSize": 1
  }
}
```

## Errors

| HTTP | Code | Meaning |
| :---: | :--- | :--- |
| `400` | `E_MALFORMED` | Invalid, unknown, or repeated query parameter; out-of-range page index or size; malformed snapshot token. |
| `409` | `SNAPSHOT_EXPIRED` | The selected score version expired or is no longer available. |
| `503` | `ACC_NOT_READY` | Challenge progress is unavailable or inconsistent. |

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