# Race results

# Race results

Full results for one race — the same page a club's public "race results" tab shows.

## Headers

| Header | Required | Value |
|---|---|---|
| `Authorization` | yes | `Bearer <access_token>` |

## Path parameters

| Param | Type | Note |
|---|---|---|
| `raceId` | UUID | from [List races](/docs/api/races); unknown, non-UUID, or another club's ⇒ `404 RACE_NOT_FOUND` |

#### Request

```bash
curl -s https://sail-club-server.cloud.run/api/v1/races/9a1c0d2b-7e4a-46f8-9c1d-2e3f9a1c0d2b/results \
  -H "Authorization: Bearer $TOKEN"
```

```javascript
const res = await fetch(`https://sail-club-server.cloud.run/api/v1/races/${raceId}/results`, {
  headers: { Authorization: `Bearer ${token}` },
});
const results = await res.json();
```

```python
res = requests.get(
    f"https://sail-club-server.cloud.run/api/v1/races/{race_id}/results",
    headers={"Authorization": f"Bearer {token}"},
    timeout=10,
)
results = res.json()
```

```csharp
var res = await client.GetAsync($"/api/v1/races/{raceId}/results");
res.EnsureSuccessStatusCode();
var page = await res.Content.ReadFromJsonAsync<PublicRaceResultPageDto>();
```

```php
<?php
$ch = curl_init("https://sail-club-server.cloud.run/api/v1/races/$raceId/results");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]);
$results = json_decode(curl_exec($ch), true);
```

#### Response

```json
{
  "id": "9a1c0d2b-7e4a-46f8-9c1d-2e3f9a1c0d2b",
  "name": "Race 1",
  "eventId": "1143117a-9c2b-4e7a-8f1d-2b6a9c4e7f10",
  "eventName": "Cup Regatta 2026",
  "scheduledDate": "2026-10-11T10:00:00Z",
  "isOfficial": true,
  "courseDistanceNm": 12.4,
  "resultsUpdatedAtUtc": "2026-10-11T12:41:09Z",
  "results": [
    {
      "id": "e3f9a1c0-d2b7-4e4a-96f8-c1d2e3f9a1c0",
      "rank": 1,
      "sailNumber": "TUR 501",
      "boatName": "Poyraz",
      "teamName": null,
      "skipper": "A. Demir",
      "owner": "A. Demir",
      "className": "ORC",
      "divisionId": "d1c0e9f8-a7b6-4c5d-8e3f-2a1b0c9d8e7f",
      "divisionName": "ORC A",
      "modelClass": "First 40.7",
      "status": "Finished",
      "startTimeUtc": "2026-10-11T10:00:00Z",
      "finishTimeUtc": "2026-10-11T12:14:33Z",
      "elapsedTime": "02:14:33",
      "correctedTime": "02:01:47",
      "listOrderNo": 1,
      "raiting": 0.912,
      "raitingLabel": "0.912",
      "points": 1,
      "penaltyPoints": null,
      "timePenaltySeconds": null
    }
  ]
}
```

`200` — same caching headers as every data endpoint; see [Caching & freshness](/docs/caching).

## Response fields

Full field table: [Data models → Race results](/docs/data-models#race-results). `status` values include
`Finished`, `DNF`, `DNS`, `OCS`, `RET`, `DSQ` — see [Data models](/docs/data-models#scoring-vocabulary) for
what each means.

## Errors

| HTTP | Code |
|---|---|
| 404 | [`RACE_NOT_FOUND`](/docs/errors#RACE_NOT_FOUND) |
| 401 | [`API_TOKEN_MISSING`](/docs/errors#API_TOKEN_MISSING) / [`API_TOKEN_INVALID`](/docs/errors#API_TOKEN_INVALID) |
| 403 | [`API_SCOPE_MISSING`](/docs/errors#API_SCOPE_MISSING) / [`API_IP_NOT_ALLOWED`](/docs/errors#API_IP_NOT_ALLOWED) |
| 429 | [`RATE_LIMIT_EXCEEDED`](/docs/errors#RATE_LIMIT_EXCEEDED) |
| 503 | [`API_UNAVAILABLE`](/docs/errors#API_UNAVAILABLE) |

## Caching

One entry per `raceId`. A race stops changing once its jury window closes, at which point you can safely
stop revalidating it altogether.

```prompt
Implement getRaceResults(raceId) for the Sail Club API.

GET https://sail-club-server.cloud.run/api/v1/races/{raceId}/results
Authorization: Bearer <access_token>

Success 200: { id, name, eventId, eventName, scheduledDate, results: [ { id, rank, sailNumber, boatName,
  skipper, owner, divisionName, status ("Finished"|"DNF"|"DNS"|"OCS"|"RET"|"DSQ"|...), startTimeUtc,
  finishTimeUtc, elapsedTime, correctedTime, points, penaltyPoints, timePenaltySeconds, ... } ], ... } —
  fetch GET /api/v1/openapi.json for the exact schema.
404 RACE_NOT_FOUND: unknown/malformed/another club's race id — not retryable.
401/403/429/503: same handling as every other call — see the full-integration prompt on /docs/overview.

Requirements: a typed client function, and a small render/format helper that turns one result row into a
human-readable line, e.g. "1. Poyraz (TUR 501) — 02:01:47 corrected — 1 pt", treating a non-Finished status
(DNF/DNS/OCS/RET/DSQ) as its own label instead of a time.

Adapt to your actual stack — the endpoint, path param and response shape above are language-neutral.
```
