---
title: "Get the Pick of the Day commitment ledger"
method: GET
path: "/api/v1/pick-of-the-day/ledger"
tags: ["Pick of the Day"]
---

# Get the Pick of the Day commitment ledger

`GET /api/v1/pick-of-the-day/ledger`

Returns the pre-game commitment for every published pick, so the public track record can be checked by someone who was not watching when the pick dropped.

One entry per (pick_date, pick_rank), ascending by pick_date then pick_rank, in one of three states. `sealed` is a live pick: the hash, the algorithm, the seal instant and the kickoff, and nothing that states a side or a price. `opened` is a settled pick: the nonce and the exact canonical payload the hash was taken over. `uncommitted` is a pick with no commitment -- published before the scheme existed, or one that reached kickoff unsealed -- named rather than omitted.

To verify an opened entry: serialize nothing. Take the bytes of the `payload` object exactly as received, append the `commitment_nonce` decoded from hex, and sha256 the result; it equals `commitment_hash`. The payload is canonical JSON -- keys sorted by UTF-8 byte value, no insignificant whitespace, decimals as strings at full stored precision, timestamps whole-second UTC with a literal Z -- and it is served byte for byte as it was hashed.

This is a proof contract, not the archive's display contract: nothing here is formatted for rendering, so an entry changes only when the pick does. A commitment is never written after kickoff and never rewritten by an outcome correction; `resolved_at` moving under an unchanged `commitment_hash` is a corrected market re-mapping an already-settled pick.

## Headers

- `X-Query-Validation` 'strict'
- `If-None-Match` string

## Response `200`

Pick of the Day commitment ledger

- object
  - `object` 'pick_of_the_day_ledger', required
  - `data` PickOfTheDayLedger, required — The commitment ledger: every published pick, ascending, in the state its commitment is actually in. The counts are derived from entries in the same pass that builds it.
    - `entries` PickOfTheDayLedgerEntry[], required — One entry per published (pick_date, pick_rank), ascending by pick_date then pick_rank.
      - union — One ledger entry. Read `state` to know which shape you have; the three are disjoint.
        - PickOfTheDayLedgerSealedEntry — A published pick that has not settled. Carries the commitment and nothing that states a side or a price: no nonce, no payload, no outcome. Publishable the instant the pick releases.
          - `state` 'sealed', required
          - `pick_date` string, date, required — ET product day the pick belongs to (YYYY-MM-DD).
          - `pick_rank` integer, required — 1-based daily slot within the product day.
          - `commitment_hash` string, required — sha256(canonical_json(payload) || nonce), lowercase hex, no 0x prefix. Publishable the moment the pick releases: without the nonce it is not invertible.
          - `commitment_algo` 'sha256(canonical_json(payload)||nonce)', required — The construction the hash was taken with, stated in the response so a verifier never has to guess the serialization.
          - `sealed_at` string, date-time, required — When the hash was frozen. Always strictly before kickoff: a pick that reaches kickoff unsealed stays unsealed forever, because a seal written after the game started would be a backdated proof.
          - `kickoff` string, date-time, required — The frozen provider kickoff in the canonical payload form: whole seconds, UTC, literal Z. This exact string reappears inside payload.kickoff when the pick opens.
          - `permalink` string, uri, required — The pick's public page.
        - PickOfTheDayLedgerOpenedEntry — A settled pick whose commitment is open: the nonce plus the exact payload the hash was taken over. Concatenate the payload bytes as received with the decoded nonce and sha256 them to reproduce commitment_hash.
          - `state` 'opened', required
          - `pick_date` string, date, required — ET product day the pick belongs to (YYYY-MM-DD).
          - `pick_rank` integer, required — 1-based daily slot within the product day.
          - `commitment_hash` string, required — sha256(canonical_json(payload) || nonce), lowercase hex, no 0x prefix. Publishable the moment the pick releases: without the nonce it is not invertible.
          - `commitment_nonce` string, required — The 32-byte nonce the hash was taken over, lowercase hex, no 0x prefix. Secret while the pick is live: a pick payload is low entropy, so a published nonce on a live pick would hand out the backed side.
          - `commitment_algo` 'sha256(canonical_json(payload)||nonce)', required — The construction the hash was taken with, stated in the response so a verifier never has to guess the serialization.
          - `sealed_at` string, date-time, required — When the hash was frozen. Always strictly before kickoff: a pick that reaches kickoff unsealed stays unsealed forever, because a seal written after the game started would be a backdated proof.
          - `resolved_at` string, date-time, nullable, required — When outcome was LAST written to a settled value, or null when that instant is unknown. It moves with a corrected market re-mapping an already-settled pick, while commitment_hash stays untouched -- which is how a mirror that keeps history sees a correction.
          - `kickoff` string, date-time, required — The frozen provider kickoff in the canonical payload form: whole seconds, UTC, literal Z. This exact string reappears inside payload.kickoff when the pick opens.
          - `payload` PickOfTheDayCommitmentPayload, required — The frozen identity of the pick, exactly as the hash was taken over it. Served byte for byte as it was hashed -- keys sorted by UTF-8 byte value, no insignificant whitespace -- so a verifier concatenates and hashes with nothing to reconstruct. Property order below is the wire order. The outcome is deliberately NOT part of it: surviving a corrected outcome unchanged is the case the commitment exists for. Worked example: {"backed_price":"0.545000","condition_id":"0xabc","kickoff":"2026-09-20T23:05:00Z","pick_date":"2026-09-20","pick_outcome_index":1,"pick_outcome_label":"Lakers","pick_rank":1,"platform":"polymarket"} with the nonce 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f hashes to 44d18fa5e2aa3a2bf3c971dcc9317c8ccbdfd5480a4773b6d8ffd5fbeeea84dc.
            - `backed_price` string, required — Frozen pre-game price of the backed side, 0..1, as the plain decimal text of the stored NUMERIC at full stored precision, trailing zeros included. A string, never a number: a float round-trip would change the bytes and break the hash. Deliberately not normalized -- 0.545000 stays "0.545000".
            - `condition_id` string, required — Provider condition id of the backed market.
            - `kickoff` string, date-time, required — Frozen provider kickoff, whole seconds, UTC, literal Z. Fixed precision, never a shortest-lossless rendering.
            - `pick_date` string, date, required — ET product day (YYYY-MM-DD).
            - `pick_outcome_index` 0 | 1, required — Index of the backed outcome within the market.
            - `pick_outcome_label` string, required — Frozen display label of the backed outcome.
            - `pick_rank` integer, required — 1-based daily slot.
            - `platform` string, required — Provider platform.
          - `outcome` 'win' | 'loss' | 'void', required — How the pick settled. Never pending: a pending pick is a sealed entry.
          - `matchup` string, required — Frozen matchup, for a reader.
          - `category` string, required — Frozen canonical sport bucket used for selection calibration (Basketball, MMA), not the exact public league identity; the archive owns that.
          - `permalink` string, uri, required — The pick's public page.
        - PickOfTheDayLedgerUncommittedEntry — A published pick with no commitment: it predates the scheme, or it reached kickoff unsealed. Nothing here is evidence of WHEN the pick was made. It is emitted rather than skipped, because a ledger with holes where the unprovable picks were would silently flatter the record. Once the pick settles, payload names its market, side and price, so the outcome can still be checked against the market's own resolution.
          - `state` 'uncommitted', required
          - `pick_date` string, date, required — ET product day the pick belongs to (YYYY-MM-DD).
          - `pick_rank` integer, required — 1-based daily slot within the product day.
          - `pre_commitment` true, required — Always true: this pick has no commitment and never will.
          - `outcome` 'pending' | 'win' | 'loss' | 'void', required — How the pick settled, or pending.
          - `matchup` string, required — Frozen matchup, for a reader.
          - `category` string, required — Frozen canonical sport bucket used for selection calibration (Basketball, MMA), not the exact public league identity; the archive owns that.
          - `resolved_at` string, date-time, nullable, required — When outcome was LAST written to a settled value, or null when that instant is unknown. It moves with a corrected market re-mapping an already-settled pick, while commitment_hash stays untouched -- which is how a mirror that keeps history sees a correction.
          - `payload` PickOfTheDayUncommittedPayload, required — A settled uncommitted pick's market, side and price. The same eight fields as PickOfTheDayCommitmentPayload, in the same key order, so a settled pick's side and price sit under payload whatever the entry's state. It is NOT a commitment: no hash was taken over it before the game, and it proves nothing about when the pick was made.
            - `backed_price` string, required — Frozen pre-game price of the backed side, 0..1, as the plain decimal text of the stored NUMERIC at full stored precision, trailing zeros included -- rendered exactly as the commitment payload renders it.
            - `condition_id` string, required — Provider condition id of the backed market.
            - `kickoff` string, date-time, nullable, required — Frozen provider kickoff, UTC, literal Z; null when no kickoff was frozen. Whole seconds render exactly as the commitment payload does; a sub-second instant keeps its fraction rather than being truncated, since nothing here is hashed.
            - `pick_date` string, date, required — ET product day (YYYY-MM-DD).
            - `pick_outcome_index` 0 | 1, required — Index of the backed outcome within the market.
            - `pick_outcome_label` string, required — Frozen display label of the backed outcome.
            - `pick_rank` integer, required — 1-based daily slot.
            - `platform` string, required — Provider platform.
          - `permalink` string, uri, required — The pick's public page.
    - `entry_count` integer, required — Number of entries, all states included.
    - `sealed_count` integer, required — Entries committed to and not yet settled.
    - `opened_count` integer, required — Entries committed to and verifiable now.
    - `uncommitted_count` integer, required — Entries carrying no commitment, so provable by nothing: the honest size of the unprovable part of the record. It only stops growing; no pick is ever retro-sealed.
  - `meta` ResponseMeta, required
    - `request_id` string, required — Unique request ID (req_ prefix). The same value as the X-Request-Id response header, the request's usage accounting row and its log lines.
    - `cached` boolean, required
    - `cache_age_s` integer — Cache age in seconds. Omitted when the response was not cached, and also when it was cached but its age cannot be established (an entry stored before its cache carried a computed instant). Never a placeholder: an unknown age is reported as no value rather than as the cache TTL.
    - `cost` integer, required — Advisory request weight (relative compute cost). 1 for simple reads; higher for heavier endpoints. Not a credit/price.
    - `ranking_generation` integer — Committed PostgreSQL-owned leaderboard generation for the returned rows and cursor. Present on GET /api/v1/leaderboard; omitted on endpoints that do not read this ranking.
    - `ranking_as_of` string, date-time — Authoritative RFC3339 timestamp from cache_generations.updated_at for ranking_generation. It is read in the same repeatable-read snapshot as the leaderboard rows and is not request time, cache write time, or row insertion order.
    - `directional_source` 'live' | 'degraded' — Which path produced the team-directional read on this response. Only present on endpoints that compute one (today: GET /api/v1/sports-edge-signals). "live" means the read RAN. "degraded" means it FAILED, so nothing was measured and the ranking fell back to raw conviction. The flag describes the READ, not its consequence: a read that ran and found nothing groupable also leaves the directional fields null, and that is honestly "live" -- the per-signal nulls already say "nothing to enrich here", so this snapshot-level flag carries only what they cannot, namely whether the read ran at all. A degraded response is cached on the shorter degraded TTL so it self-heals. Reported SEPARATELY from ranking_source because the two degradations are independent -- a sharp-money DB miss weakens the ranking DATA, a directional failure removes a ranking WEIGHT -- and a consumer down-weighting a degraded response needs to know which input it lost. Omitted on endpoints that compute no directional read.
    - `ranking_source` 'live' | 'db_only' — Which ranking-data path produced this response. Only present on endpoints that can degrade a ranking (today: GET /api/v1/sports-edge-signals). "live" is the normal path (the current holder pile from the provider batch); "db_only" is the degraded fallback (a truthful but weaker trader_markets ranking) served when the live sharp-money ranking batch is unavailable (a sharp-money DB read failure, not a Polymarket outage) and cached on a shorter TTL, so a consumer can down-weight or skip it. Omitted on endpoints that never degrade.
    - `category_skill_source` 'live' | 'partial' | 'degraded' | 'unavailable' — Whole filtered snapshot category-evidence status before pagination. Operational live always remains partial source coverage.
    - `category_skill_model_version` string
    - `category_skill_taxonomy_version` string
    - `category_skill_platform` 'polymarket'
    - `category_skill_scope` 'observed_goldsky_primary_taker_fill'
    - `category_skill_source_coverage` 'partial_whale_threshold_fills' | 'graded_wallet_fills'
    - `category_skill_observation_started_at` string, date-time
    - `category_skill_model_operationally_degraded` boolean — Whole-model operational readiness captured with the category model snapshot. Present on category-enriched responses even when the filtered signal list is empty. When true, category_skill_source is degraded and sports-edge-signals uses the shorter degraded cache TTL.
    - `category_skill_status_counts` object
      - `live` integer, required
      - `insufficient` integer, required
      - `stale` integer, required
      - `unknown` integer, required
      - `degraded` integer, required
    - `category_skill_base_payload_hash` string — SHA-256 of the funded signal membership/order/rank/cursor vector immediately before category-skill enrichment. Sports-edge-signals only.
    - `category_skill_enriched_base_payload_hash` string — Independent SHA-256 recomputation over the same base fields immediately after category-skill enrichment. Equality with category_skill_base_payload_hash proves shadow enrichment did not change funded inputs. Sports-edge-signals only.

## Other responses

- `304` — Not Modified. Returned when If-None-Match matches the current Pick of the Day ledger payload.
- `408` — The handler did not answer inside the server's 30-second timeout. error.code is request_timeout. On GET and HEAD the response carries Retry-After and error.retry_at; on a mutation it carries neither, because the request may have completed on the server: check its state before repeating it, and reuse its Idempotency-Key.
- `429` — Rate limit exceeded. Three independent budgets. (1) 100 requests/minute per user (sliding window), on every authenticated route. (2) On the BATCH routes only: 2500 batch item units/minute per user, reserved before any item is executed. A batch with N requested items costs N item units, including duplicate and invalid items. 2500 = 100 requests x 25 items per batch, which is the most item work a key can buy through the request limiter at all: a caller may spend their entire 100-request minute on full 25-item batches without the item budget being what stops them. The REQUEST budget is the effective ceiling, and batching is never the more expensive choice. The item budget can still deny at a sliding-window boundary (both counters carry the previous window forward with a floor, and the item counter runs 25x the request counter), so honor a 429 from either. Over-quota batches return 429 with Retry-After plus RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset before any item work is done. (3) The monthly quota: Pro includes 250,000 authenticated requests per UTC calendar month, whatever the billing cadence. Over 250,000: with pay as you go on, requests keep answering and the excess bills at USD 0.20 per 1,000 on a monthly invoice, up to 1,000,000 requests a month; without it, from October 1, 2026, the next request answers 429 rate_limited with error.reason monthly_quota_exceeded and a Retry-After to the month's reset, and from the same day a pay-as-you-go account answers the same past 1,000,000. A refused request is not counted. Every authenticated response carries X-Monthly-Quota-Limit, X-Monthly-Quota-Remaining, and X-Monthly-Quota-Reset (unix seconds, the first of next month). (4) The per-address budget: 1200 requests/minute per IP, shared by every caller behind one address and counted before authentication, on every route. A 429 from it carries error.reason ip_rate_limited and describes that bucket in RateLimit-*; a throttled address (sustained over-limit traffic) carries error.reason ip_throttled with a Retry-After of minutes to days, and a request before it does not shorten the cooldown. Every 429 is the standard error envelope with meta.request_id equal to X-Request-Id.
- `503` — Redis-backed authenticated rate limiter unavailable; retry after the per-process outage cooldown

## Changes

- **2026-09-23** `ece7a25b7a44` — 3 warning, 3 info
  - added the new `freshness_ceiling_unsatisfied` enum value to the `error/reason` response property for the response status `408`
  - added the new `freshness_ceiling_unsatisfied` enum value to the `error/reason` response property for the response status `429`
  - added the new `freshness_ceiling_unsatisfied` enum value to the `error/reason` response property for the response status `503`
  - added the optional property `error/freshness` to the response with the `408` status
  - …2 more
- **2026-09-23** `8462acf80f8c` — 3 warning
  - added the new `export_expired` enum value to the `error/reason` response property for the response status `408`
  - added the new `export_expired` enum value to the `error/reason` response property for the response status `429`
  - added the new `export_expired` enum value to the `error/reason` response property for the response status `503`
- **2026-09-22** `e0f39b9ecc6f` — 4 info
  - removed the non-success response with the status `401`
  - removed the non-success response with the status `402`
  - removed the non-success response with the status `403`
  - removed the non-success response with the status `423`
- …earlier changes not shown

[Full history](https://skmtc.dev/0xinsider/apis/0xinsider-api/changes/api/v1/pick-of-the-day/ledger/get.md)

---

[API](https://skmtc.dev/0xinsider/apis/0xinsider-api.md) · [All operations](https://skmtc.dev/0xinsider/apis/0xinsider-api/llms.txt) · [OpenAPI document](https://skmtc.dev/0xinsider/apis/0xinsider-api/revisions/2035b3316329?raw)
