---
title: "Grade a pregame price against the Sorqen record"
method: POST
path: "/v1/grade"
tags: ["Grading"]
---

# Grade a pregame price against the Sorqen record

`POST /v1/grade`

## Request

- Base URL: `https://sorqen.flashodds.live`
- URL: `https://sorqen.flashodds.live/v1/grade`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Give Sorqen a price and the time you saw it. It answers what its fair-value record said at that exact point in market time, compares the price with an immutable Sorqen Close, and attaches the settled result when there is one.

NO LOOK-AHEAD: `fair_at_time` is the last priced observation at or BEFORE `observed_at`. A nearer later observation is deliberately not used.

A PARTIAL GRADE IS A NORMAL ANSWER: before kickoff there is no close and no result, so those anchors come back `pending` and the metrics that depend on them are null rather than zero.

Sorqen never fetches your venue. `venue` is a label for your own records.

## Request body

- GradeRequest
  - `outcome_id` string, required: Canonical Sorqen outcome id.
  - `observed_at` string, date-time, required: When you saw the price. Must be before the scheduled start: Sorqen grades pregame prices and publishes no live-market benchmark. Only information that existed at or before this instant is used.
  - `price` GradePrice, required: The price you accepted, and what kind of number it is. The format is DECLARED, never inferred: a bare 1.91 is decimal odds to one caller and a nonsense probability to another, and guessing from magnitude produces a plausible wrong grade rather than an error.
    - `format` 'american' | 'decimal' | 'probability', required
    - `value` number, required: American: any non-zero integer (-150 is 0.600000). Decimal: greater than 1. Probability: strictly between 0 and 1.
  - `venue` string, nullable: Optional label for where your price came from. METADATA ONLY: Sorqen never fetches the venue and no code path reads this to decide anything.

## Response `200`

The grade, including partial grades where the close or the result does not exist yet.

- object
  - `data` GradeResponse
    - `outcome_id` string, required
    - `claim` object, required
      - `observed_at` string, date-time
      - `probability` number: Your price, normalized to an implied probability.
      - `venue` string, nullable
    - `fair_at_time` GradeBenchmarkPoint, required: One anchor. `available` carries the numbers; `pending` means ask again later; `unavailable` means it will never exist. A missing anchor is never reported as zero.
      - `state` 'available' | 'pending' | 'unavailable', required
      - `reason` string: Present when the state is not available.
      - `probability` number
      - `confidence` number, nullable
      - `dispersion` number, nullable
      - `books` integer
      - `method_version` string
      - `observed_at` string, date-time
      - `frozen_at` string, date-time: When Sorqen named this observation a benchmark. Close only.
      - `policy_version` string: Which benchmark selection policy chose it. Close only.
      - `inputs_hash` string: Recomputable from the stored benchmark row. Close only.
    - `fair_close` GradeBenchmarkPoint, required: One anchor. `available` carries the numbers; `pending` means ask again later; `unavailable` means it will never exist. A missing anchor is never reported as zero.
      - `state` 'available' | 'pending' | 'unavailable', required
      - `reason` string: Present when the state is not available.
      - `probability` number
      - `confidence` number, nullable
      - `dispersion` number, nullable
      - `books` integer
      - `method_version` string
      - `observed_at` string, date-time
      - `frozen_at` string, date-time: When Sorqen named this observation a benchmark. Close only.
      - `policy_version` string: Which benchmark selection policy chose it. Close only.
      - `inputs_hash` string: Recomputable from the stored benchmark row. Close only.
    - `edge_at_time_pts` number, nullable: (fair at time - your probability) x 100. Positive means you took the outcome cheaper than the record said it was worth at that moment. Null when the anchor is unavailable; never zero.
    - `clv_pts` number, nullable: (Sorqen Close - your probability) x 100. Closing-line value. Null until the close is frozen; never zero.
    - `result` GradeResult, required
      - `state` 'settled' | 'pending' | 'unavailable', required
      - `reason` string
      - `won` boolean, nullable: Null on a push, where nobody won.
      - `push` boolean
      - `final_score_home` integer, nullable
      - `final_score_away` integer, nullable
      - `settled_at` string, date-time
    - `provenance` object, required: Deterministic: the same claim graded against the same record yields the same hash. No signature field is emitted, because Sorqen does not yet publish a verification key and a cryptographic-looking placeholder would be worse than none.
      - `grade_method_version` string
      - `grade_inputs_hash` string
  - `meta` object

## Other responses

- `400`: The claim is malformed, or `observed_at` is at or after the scheduled start. Machine-readable codes: invalid_body, invalid_outcome_id, invalid_observed_at, invalid_price, unknown_price_format, invalid_venue, claim_after_start.
- `401`: Missing or invalid API key.
- `403`: The authenticated plan does not include this entitlement.
- `404`: No such canonical outcome.
- `429`: Plan request limit exceeded. Retry-After and X-RateLimit headers are returned.

---

[API](https://skmtc.dev/flashodds/apis/sorqen-api.md) · [All operations](https://skmtc.dev/flashodds/apis/sorqen-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flashodds/apis/sorqen-api/revisions/ec67a9e22813?raw)
