Skip to content

Race API

Endpoints for managing auto racing, keirin and horse racing race information. This data feeds AI article generation (article) and AI blog generation (blog), and is referenced through article.race_id / blog.race_id.

Racing type

Races are stored in three collections by racing type: auto_racing / bicycle_racing / horse_racing. Race documents themselves carry no type โ€” the collection expresses the type.

Value Sport
auto_racing Auto racing
bicycle_racing Keirin
horse_racing Horse racing

Because race_id (the _id) is unique across racing types, fetching, updating and deleting a single race needs no type: the API searches all three collections. Only listing (GET /v1/races) requires type, since it must decide which collection to read.

Race schema

Field Type Notes
id string _id; the race_id inside the race result JSON
type enum auto_racing / bicycle_racing / horse_racing
name string Uploaded race result JSON filename without .json
race_result object Contents of the race result JSON
prediction object | null Pre-race entry list and AI prediction marks. null when not registered
article_status enum | null Status of the latest article for this race; null if none (set only in list responses)
created_at number UNIX timestamp (ms)
updated_at number UNIX timestamp (ms)

POST /v1/races

Overview

Registers a new race result.

No JSON payload is sent in the body; instead the racing type (type) and the race result JSON file are uploaded as multipart/form-data. The contents of the uploaded JSON file are stored verbatim as race_result.

Request and Response

Headers

Request

  • Content-Type: multipart/form-data

Response

  • Content-Type: application/json

URI

POST /v1/races

Request body

Field Type Required Notes
type enum โ—ฏ auto_racing / bicycle_racing / horse_racing
file binary โ—ฏ Race result JSON file

The server derives each stored field as follows.

  • _id: the race_id value inside the JSON
  • name: the uploaded filename minus .json (e.g. 20260504_้ฃฏๅกš_05R.json โ†’ 20260504_้ฃฏๅกš_05R)
  • race_result: the whole JSON body, stored as-is

Example of a race result JSON (the format follows the scraping output):

{
  "race_id": "string",
  "race_date": "string",
  "track_name": "string",
  "race_number": "string",
  "race_name": "string",
  "weather": "string",
  "entries": []
}

Response

201 Created

{
  "id": "string",
  "type": "horse_racing",
  "name": "string",
  "race_result": {},
  "prediction": null,
  "article_status": null,
  "created_at": 1234567890000,
  "updated_at": 1234567890000
}

Error handling

Description Status code Status
No file supplied 400 Bad Request
Body cannot be parsed as JSON 400 Bad Request
JSON has no race_id field 400 Bad Request
Invalid type 422 Unprocessable Entity
A race with the same race_id already exists 409 Conflict
Internal server error 500 Internal Server Error

Processing flow

  1. Receive the request
  2. Parse the uploaded JSON file and extract race_id
  3. Search all three race collections by race_id to confirm the race does not exist yet
  4. Store the race in the collection matching type
  5. Return the stored race
flowchart TD
    Start([POST /v1/races]) --> Parse[Parse JSON file]
    Parse -->|invalid| E400[400 Bad Request]
    Parse -->|no race_id| E400
    Parse -->|OK| Exists[Search three collections by race_id]
    Exists -->|already exists| E409[409 Conflict]
    Exists -->|not found| Save[Store into the collection for type]
    Save --> Success[201 Created]

PUT /v1/races/prediction

Overview

Attaches an AI prediction (pre-race entry list and prediction marks) to an existing race.

Predictions are sometimes collected after the results, so this is a separate operation from race registration. The stored prediction is the input for prediction blog generation (POST /v1/blogs/generate with kind=prediction).

Request and Response

Headers

Request

  • Content-Type: multipart/form-data

Response

  • Content-Type: application/json

URI

PUT /v1/races/prediction

Request body

Field Type Required Notes
file binary โ—ฏ Prediction JSON file; must contain race_id and entries

The target race is identified by the race_id inside the JSON, so no path or form parameter is needed.

Response

200 OK

Returns the race schema with prediction populated.

Error handling

Description Status code Status
No file supplied 400 Bad Request
Body cannot be parsed as JSON 400 Bad Request
JSON has no race_id field 400 Bad Request
JSON has no entries field 400 Bad Request
Race not found 404 Not Found
Internal server error 500 Internal Server Error

Processing flow

flowchart TD
    Start([PUT /v1/races/prediction]) --> Parse[Parse JSON file]
    Parse -->|invalid| E400[400 Bad Request]
    Parse -->|OK| Find[Search three collections by race_id]
    Find -->|not found| E404[404 Not Found]
    Find -->|found| Save[Store prediction]
    Save --> Success[200 OK]

Route ordering

/races/prediction is declared before /races/{race_id}. Declaring it later would make prediction be interpreted as a race_id.


GET /v1/races

Overview

Lists races for the given racing type.

The list also returns the status of the latest article for each race (article_status). It backs the status badge in the admin list; fetching it per race would be an N+1, so it is resolved in bulk.

Request and Response

URI

GET /v1/races

Query parameters

Parameter Type Required Notes
type enum โ—ฏ auto_racing / bicycle_racing / horse_racing
page number Page number. Default: 1
size number Items per page. Default: 10
order enum asc / desc (by name). Default: desc

Ordering is by name

Unlike other resources, the race list sorts by name (e.g. 20260504_้ฃฏๅกš_05R). Because the name starts with the date, lexical order equals race-date order.

Response

200 OK

{
  "list": [
    {
      "id": "string",
      "type": "horse_racing",
      "name": "string",
      "race_result": {},
      "prediction": null,
      "article_status": "draft",
      "created_at": 1234567890000,
      "updated_at": 1234567890000
    }
  ],
  "current_page": 1,
  "total_count": 10
}

Error handling

Description Status code Status
type missing or invalid 422 Unprocessable Entity
Internal server error 500 Internal Server Error

GET /v1/races/{race_id}

Overview

Fetches a single race. race_id is unique across racing types, so no type is required.

Request and Response

URI

GET /v1/races/{race_id}
Path parameter Type Notes
race_id string The race _id

Response

200 OK

Returns the race schema (article_status is null).

Error handling

Description Status code Status
Race not found 404 Not Found
Internal server error 500 Internal Server Error

PUT /v1/races/{race_id}

Overview

Updates a race.

Request and Response

Headers

Request

  • Content-Type: application/json

URI

PUT /v1/races/{race_id}

Request body

{
  "name": "string",
  "race_result": {}
}
Field Type Required Notes
name string Race name
race_result object Contents of the race result JSON

Fields left out (null) are not updated. prediction cannot be updated here โ€” use PUT /v1/races/prediction.

Response

200 OK

Returns the updated race schema.

Error handling

Description Status code Status
Invalid request body 422 Unprocessable Entity
Race not found 404 Not Found
Internal server error 500 Internal Server Error

DELETE /v1/races/{race_id}

Overview

Soft-deletes a race.

Request and Response

URI

DELETE /v1/races/{race_id}

Response

204 No Content

Error handling

Description Status code Status
Race not found 404 Not Found
Internal server error 500 Internal Server Error

Processing flow

flowchart TD
    Start([DELETE /v1/races/race_id]) --> Find[Search three collections by race_id]
    Find -->|not found| E404[404 Not Found]
    Find -->|found| Delete[Set deleted_at]
    Delete --> Success[204 No Content]