> ## Documentation Index
> Fetch the complete documentation index at: https://surf-dcinside-api.kr.ask.surf/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Cycle Scores

> The stored response for a cycle, byte for byte. Use it after a timeout or a lost response.

<div style={{ maxWidth: 760 }}>
  <h2 style={{ fontSize: "22px", marginBottom: 8 }}>Overview</h2>

  The **Get Cycle Scores** API returns the response that [`POST /v1/cycles`](/cycles) produced for a cycle, byte for byte. It does not score anything and never changes what was stored, so it is safe to call as often as needed.

  <Note>
    Use it when the cycle call timed out or the response was lost. Repeating `POST /v1/cycles` with the same `cycle_id` and the same body returns the same stored response, so either call works. This one needs no body.
  </Note>

  <h3 style={{ marginTop: 20 }}>When to use it</h3>

  <CardGroup cols={2}>
    <Card title="After a timeout" icon="hourglass">
      The scoring may have finished after DCinside stopped waiting. The stored result is here.
    </Card>

    <Card title="If scoring moves to polling" icon="rotate">
      If synchronous scoring proves too slow, the cycle call replies `202` and the result is polled from this endpoint.
    </Card>
  </CardGroup>

  <Warning>
    DCinside skips a cycle whose response does not arrive in time or does not match the cycle. Reading it later from here is for the record and for reconciliation, not for publishing late.
  </Warning>

  ***

  <h2 style={{ fontSize: "22px", marginBottom: 8 }}>Request Attributes</h2>

  <ParamField path="cycle_id" type="string" required>
    The cycle id sent in the original call, `YYYYMMDDTHHMM` in KST, for example `20260929T0205`. A cycle we never received returns `404 not_found`.
  </ParamField>

  ***

  <h2 style={{ fontSize: "22px", marginBottom: 8 }}>Response Attributes</h2>

  The body is the same `CycleResponse` object as in [Score a Cycle](/cycles#response-attributes): the envelope with counts and versions, and one row in `results` for every candidate that was pending in that cycle.

  ***
</div>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url "https://rtb.example.com/v1/scores/20260929T0205" \
    --header "Authorization: <api-key>"
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://rtb.example.com/v1/scores/20260929T0205",
      headers={"Authorization": "<api-key>"},
  )
  scores = response.json()
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://rtb.example.com/v1/scores/20260929T0205",
    { headers: { Authorization: "<api-key>" } },
  );
  const scores = await response.json();
  ```
</RequestExample>

<ResponseExample>
  ```json 200 — one post scored now, one waiting theme={null}
  {
    "cycle_id": "20260929T0205",
    "cycle_time": "2026-09-29T02:05:00+09:00",
    "received_at": "2026-09-29T02:05:03+09:00",
    "scored_at": "2026-09-29T02:05:09+09:00",
    "model_version": "m0924-1",
    "weights_version": "w-1",
    "counts": {
      "candidates": 1,
      "engagement": 2,
      "candidate_scores": 1,
      "removals": 1,
      "scored_now": 1,
      "waiting": 1,
      "not_scored": 0,
      "results": 2
    },
    "missing_feature_share": 0.11,
    "warnings": [],
    "results": [
      {
        "gall_id": "example_gallery",
        "post_no": 31899120,
        "status": "scored",
        "score": 0.62,
        "p_views": 0.62,
        "p_comments": 0.41,
        "p_up": 0.37,
        "downvote_share": 0.08,
        "scored_at_cycle": "2026-09-29T02:05:00+09:00"
      },
      {
        "gall_id": "example_gallery",
        "post_no": 31900001,
        "status": "waiting",
        "score": null,
        "due_cycle": "2026-09-29T02:20:00+09:00"
      }
    ]
  }
  ```

  ```json 404 — cycle never received theme={null}
  {
    "error": {
      "code": "not_found",
      "message": "no cycle 20260929T0205"
    }
  }
  ```
</ResponseExample>


## OpenAPI

````yaml openapi.json GET /v1/scores/{cycle_id}
openapi: 3.0.3
info:
  title: DCinside RTB API
  description: >-
    Publish scores for DCinside's Real-Time Best candidate pool. DCinside makes
    one call per five-minute cycle and reads the scores in the response. Version
    1 is a proposal for the technical exchange with DCinside: calls, field names
    and limits can change until both sides agree.
  version: 1.0.0
servers:
  - url: https://{host}
    description: Issued by Surf together with the API key.
    variables:
      host:
        default: rtb.example.com
        description: >-
          Host name issued by Surf for the environment (rehearsal or
          production). The default is a placeholder.
security:
  - ApiKey: []
tags:
  - name: Scoring
    description: One call per five-minute cycle, and re-reading a stored cycle.
  - name: Images
    description: Post images, uploaded once per post, separately from scoring.
  - name: Outcomes
    description: Not in MVP scope. How placed posts performed. Used for learning.
  - name: Weights
    description: Not in MVP scope. The weights of the publish score.
  - name: Health
    description: Service state.
paths:
  /v1/scores/{cycle_id}:
    get:
      tags:
        - Scoring
      summary: Get Cycle Scores
      description: >-
        The stored response for a cycle, byte for byte. Use it after a timeout
        or a lost response. It does not score anything.
      operationId: getCycleScores
      parameters:
        - name: cycle_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^\d{8}T\d{4}$
            example: 20260929T0205
          description: The cycle id sent in the original call.
      responses:
        '200':
          description: The response stored for this cycle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CycleResponse'
              example:
                cycle_id: 20260929T0205
                cycle_time: '2026-09-29T02:05:00+09:00'
                received_at: '2026-09-29T02:05:03+09:00'
                scored_at: '2026-09-29T02:05:09+09:00'
                model_version: m0924-1
                weights_version: w-1
                counts:
                  candidates: 1
                  engagement: 2
                  candidate_scores: 1
                  removals: 1
                  scored_now: 1
                  waiting: 1
                  not_scored: 0
                  results: 2
                missing_feature_share: 0.11
                warnings: []
                results:
                  - gall_id: example_gallery
                    post_no: 31899120
                    status: scored
                    score: 0.62
                    p_views: 0.62
                    p_comments: 0.41
                    p_up: 0.37
                    downvote_share: 0.08
                    scored_at_cycle: '2026-09-29T02:05:00+09:00'
                  - gall_id: example_gallery
                    post_no: 31900001
                    status: waiting
                    score: null
                    due_cycle: '2026-09-29T02:20:00+09:00'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  schemas:
    CycleResponse:
      type: object
      properties:
        cycle_id:
          type: string
          pattern: ^\d{8}T\d{4}$
          example: 20260929T0205
          description: Echoed from the request.
        cycle_time:
          type: string
          format: date-time
          pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:[0-5][05]:00\+09:00$
          description: Echoed from the request.
          example: '2026-09-29T02:05:00+09:00'
        received_at:
          type: string
          format: date-time
          pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+09:00$
          description: Our clock when the call arrived.
          example: '2026-09-29T02:05:03+09:00'
        scored_at:
          type: string
          format: date-time
          pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+09:00$
          description: >-
            Our clock when scoring finished. The gap from `received_at` is the
            scoring time.
          example: '2026-09-29T02:05:09+09:00'
        model_version:
          type: string
          example: m0924-1
          description: >-
            The model that produced the scores. Every score can be traced to it
            and to `weights_version`.
        weights_version:
          type: string
          example: w-1
          description: The publish-score weights in force for this cycle.
        counts:
          $ref: '#/components/schemas/Counts'
        missing_feature_share:
          type: number
          minimum: 0
          maximum: 1
          example: 0.11
          description: >-
            Share of the model's 62 tabular inputs that were missing across the
            posts scored in this cycle. A jump means bad data upstream.
        warnings:
          type: array
          items:
            type: string
          example: []
          description: >-
            Extra fields we ignored, and anything else worth a look. Empty when
            clean.
        results:
          type: array
          items:
            $ref: '#/components/schemas/ResultRow'
          description: >-
            One row per pending candidate. Scores given earlier are repeated
            unchanged, so DCinside keeps no score memory of its own. Removed
            posts are not returned.
      required:
        - cycle_id
        - cycle_time
        - received_at
        - scored_at
        - model_version
        - weights_version
        - counts
        - missing_feature_share
        - warnings
        - results
    Counts:
      type: object
      properties:
        candidates:
          type: integer
          minimum: 0
          example: 1
          description: Rows received in `candidates`.
        engagement:
          type: integer
          minimum: 0
          example: 2
          description: Rows received in `engagement`.
        candidate_scores:
          type: integer
          minimum: 0
          example: 1
          description: Rows received in `candidate_scores`.
        removals:
          type: integer
          minimum: 0
          example: 1
          description: Rows received in `removals`.
        scored_now:
          type: integer
          minimum: 0
          example: 1
          description: Posts whose score was taken in this cycle.
        waiting:
          type: integer
          minimum: 0
          example: 1
          description: Results with status `waiting`.
        not_scored:
          type: integer
          minimum: 0
          example: 0
          description: Results with status `not_scored`.
        results:
          type: integer
          minimum: 0
          example: 2
          description: >-
            Rows in `results`: every pending post, including earlier scores
            repeated unchanged.
      required:
        - candidates
        - engagement
        - candidate_scores
        - removals
        - scored_now
        - waiting
        - not_scored
        - results
      description: Row counts for this cycle.
    ResultRow:
      type: object
      properties:
        gall_id:
          type: string
          minLength: 1
          description: Post key, together with `post_no`.
          example: example_gallery
        post_no:
          type: integer
          minimum: 1
          description: >-
            Post number. Unique only within a gallery, so a post is always keyed
            by `gall_id` and `post_no` together.
          example: 31900001
        status:
          type: string
          enum:
            - scored
            - waiting
            - not_scored
          example: scored
          description: >-
            `scored` = the post has a score. `waiting` = it is still inside its
            15-minute wait. `not_scored` = it cannot be scored, see `reason`.
        score:
          type: number
          nullable: true
          example: 0.62
          description: >-
            The publish score: `w_v·P(views) + w_c·P(comments) + w_u·P(up) −
            w_d·downvote_share`, with the weights in force. `null` unless
            `status` is `scored`.
        p_views:
          type: number
          minimum: 0
          maximum: 1
          nullable: true
          example: 0.62
          description: >-
            Chance that the post lands in the top quarter for views within its
            tier, day and time block. `null` unless scored.
        p_comments:
          type: number
          minimum: 0
          maximum: 1
          nullable: true
          example: 0.41
          description: Same, for comments. `null` unless scored.
        p_up:
          type: number
          minimum: 0
          maximum: 1
          nullable: true
          example: 0.37
          description: Same, for up-votes. `null` unless scored.
        downvote_share:
          type: number
          minimum: 0
          maximum: 1
          nullable: true
          example: 0.08
          description: Predicted down-vote share. `null` unless scored.
        scored_at_cycle:
          type: string
          format: date-time
          pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:[0-5][05]:00\+09:00$
          description: >-
            The cycle at which the score was taken. Present when `status` is
            `scored`. A score is taken once and repeated unchanged in later
            cycles until the post is removed.
          example: '2026-09-29T02:05:00+09:00'
        due_cycle:
          type: string
          format: date-time
          pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:[0-5][05]:00\+09:00$
          description: >-
            The cycle at which a waiting post will be scored. Present when
            `status` is `waiting`.
          example: '2026-09-29T02:20:00+09:00'
        reason:
          type: string
          enum:
            - missing_title
            - missing_clock
            - post_unavailable
            - unknown_post
          example: missing_title
          description: >-
            Present when `status` is `not_scored`. `missing_title`: the title is
            empty. `missing_clock`: `created_at` or `extracted_at` is missing.
            `post_unavailable`: the latest engagement status is `missing` or
            `nomap`. `unknown_post`: the post appears in `engagement` without a
            candidate record.
      required:
        - gall_id
        - post_no
        - status
      description: The state of one pending post.
    Error:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
      required:
        - error
    ErrorBody:
      type: object
      properties:
        code:
          type: string
          example: bad_request
          description: Machine-readable code. See Errors.
        message:
          type: string
          example: 2 fields failed validation
          description: Human-readable summary.
        fields:
          type: array
          items:
            $ref: '#/components/schemas/ErrorField'
          description: Every failing field, when the error is about the body.
      required:
        - code
        - message
    ErrorField:
      type: object
      properties:
        path:
          type: string
          example: candidates[0].created_at
          description: Path of the failing field.
        problem:
          type: string
          example: missing +09:00 offset
          description: What is wrong with it.
      required:
        - path
        - problem
  responses:
    Unauthorized:
      description: The API key is missing or unknown.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unauthorized
              message: API key missing or unknown
    Forbidden:
      description: The key has no scope for this call.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: forbidden
              message: this key cannot call PUT /v1/weights
    NotFound:
      description: No such record.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: not_found
              message: no cycle 20260929T0205
    RateLimited:
      description: >-
        Too many calls. Wait for `Retry-After` seconds, then repeat the same
        call.
      headers:
        Retry-After:
          description: Seconds to wait.
          schema:
            type: integer
            example: 10
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: rate_limited
              message: too many calls, retry in 10 s
    ServerError:
      description: Our failure. No scores. DCinside skips the cycle and our alert fires.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: server_error
              message: internal error
  securitySchemes:
    ApiKey:
      type: apiKey
      name: Authorization
      in: header
      description: >-
        The API key issued for the environment, sent as the whole header value.
        Two keys are valid during a rotation.

````