---
title: "List ranked pre-game sports-edge signals"
method: GET
path: "/api/v1/sports-edge-signals"
tags: ["Markets"]
---

# List ranked pre-game sports-edge signals

`GET /api/v1/sports-edge-signals`

Pro-tier. Ranked list of upcoming pre-game sports markets (moneyline + props) where graded (S/A/B) sharp money is piled on one side, each row carrying signal_created_at (the UTC time its immutable snapshot was computed), the piled side, its grade distribution, kickoff, piled-side Polymarket CLOB token id, and required shadow-only category_skill evidence. signal_created_at is not provider market creation time or request time. Eligibility and ranking remain the funded FLOW/HOLDER contract; category skill cannot change membership, order, rank, cursor, routing, or sizing. meta exposes explicit category source/model/status plus independently computed pre/post SHA-256 base-vector hashes that must match. Served from a shared server-side snapshot cache (healthy TTL ~180s, degraded ~30s) computed once per category at the maximum 48h horizon and filtered at request time; the cursor pins to that snapshot and a stale cursor is rejected. Polymarket only; source coverage is partial first-observed post-launch Goldsky primary taker BUY fills admitted by the canonical whale-alert thresholds, never reconstructed history. Deliberately not the editorial Pick of the Day ranker.

## Query parameters

- `category` string
- `limit` integer
- `cursor` string
- `horizon_hours` integer
- `min_grade` 'S' | 'A' | 'B'

## Headers

- `If-None-Match` string

## Response `200`

Ranked pre-game sports-edge signals

- object
  - `object` 'list', required
  - `data` SportsEdgeSignal[], required
    - `condition_id` string, required — Polymarket condition id.
    - `signal_created_at` string, date-time, required — UTC time at which the immutable signal snapshot was computed. Every row from one snapshot shares this value; it is not provider market creation time and is not rewritten at request time.
    - `token_id` string, nullable — Polymarket CLOB token id (ERC1155 asset id, decimal string) for the PILED outcome; null when unavailable.
    - `category` string, nullable — Canonical sport bucket (e.g. Basketball, Tennis); null when the raw category has no canonical mapping.
    - `raw_category` string, nullable — Raw provider category as stored (e.g. NBA, EPL).
    - `title` string, nullable
    - `event_slug` string, nullable
    - `game_start_time` string, date-time, nullable — Kickoff (UTC). In the future at SNAPSHOT time and within the requested horizon; because the response is served from a shared snapshot cached up to the ~180s TTL, a served kickoff can be up to ~180s in the past relative to the response time. Not a live guarantee that the game has not yet started.
    - `piled_side` string, nullable — Nullable provider-backed piled-outcome display label. When provider group context is unavailable, it may remain a bare Yes/No/Over/Under; do not use it alone as participant identity.
    - `piled_outcome_index` integer, required — Provider binary-column selector: 0 selects outcome_yes/token_id_yes; 1 selects outcome_no/token_id_no. It does not identify home/away or a participant. Use piled_side together with title/event context for display.
    - `sharp_pct` number, nullable — Piled-side dollar concentration backed_usd / (yes_usd + no_usd), in (0.5, 1] for a real pile; null when there is no sharp USD.
    - `backed_sharp_usd` number, required — Raw piled-side sharp-money USD.
    - `s_count` integer, required — S-grade graded holders on the piled side.
    - `a_count` integer, required — A-grade graded holders on the piled side.
    - `b_count` integer, required — B-grade graded holders on the piled side.
    - `graded_holders` integer, required — Piled-side graded holder count (s_count + a_count + b_count).
    - `top_grade` 'S' | 'A' | 'B', nullable — Best grade present on the piled side; null when none.
    - `smart_score` number, nullable — Canonical sharp-money score (yes_usd - no_usd)/(yes_usd + no_usd) in [-1, 1] (piled-yes positive, piled-no negative); a lower-order ranking tiebreak (after directional_rank_score and conviction_score).
    - `volume` number, nullable — Market volume (USD).
    - `net_side` 'BUY' | 'SELL', nullable — Aggregate recent flow direction on the market; null when unavailable.
    - `conviction_score` number, required — Grade-weighted pile score (5*s + 4*a + 3*b) * sharp_pct; the raw conviction input to the ranking (see directional_rank_score).
    - `one_way_holder_count` integer, nullable — Piled-side graded holders whose open legs across the signal game's markets (cross-market within the one game; moneyline+spread family only) all back the same team; counted only when the wallet's leg set is fresh and groupable. Null when the directional read was not computed (no groupable game, no holder-level data on this ranking path, or the enrichment read failed).
    - `hedged_holder_count` integer, nullable — Piled-side graded holders classified HEDGED across the game (they back two or more distinct teams). Null when the directional read was not computed.
    - `one_way_graded_usd` number, nullable — Piled-side graded USD held by genuinely one-way wallets (share-weighted allocation of backed_sharp_usd). Null when the directional read was not computed.
    - `directional_confidence` number, nullable — One-way fraction of the piled graded dollars, in [0, 1] -- the metric orthogonal to sharp_pct. Stale/unknown/hedged dollars dilute it toward zero (conservative). Null when the directional read was not computed.
    - `directional_rank_score` number, required — The ranking key, descending: conviction_score * (1 + 0.25 * directional_confidence). Equals conviction_score when the directional read is null/zero, so signals without the read rank exactly as before.
    - `category_skill` SportsEdgeSignalCategorySkill, required — Shadow-only category evidence over the full uncapped piled-side S/A/B holder allocation. It never changes signal membership, ordering, routing, or sizing.
      - `status` 'live' | 'insufficient' | 'stale' | 'unknown' | 'degraded', required
      - `model_version` string, required
      - `taxonomy_version` string, nullable, required
      - `platform` 'polymarket', required
      - `scope` 'observed_goldsky_primary_taker_fill', required
      - `source_coverage` 'partial_whale_threshold_fills', required
      - `observation_started_at` string, date-time, required
      - `as_of` string, date-time, required
      - `canonical_category` string, nullable, required
      - `eligible_holders` integer, required
      - `covered_holders` integer, required
      - `backed_sharp_usd` number, nullable, required
      - `covered_backed_usd` number, nullable, required
      - `coverage_pct` number, nullable, required
      - `weighted_edge_mean` number, nullable, required
      - `weighted_holder_lower_mean` number, nullable, required
      - `specialist_backed_usd` number, nullable, required
      - `specialist_backed_usd_pct` number, nullable, required
      - `largest_holder_backed_usd_pct` number, nullable, required
      - `minimum_holder_event_count` integer, nullable, required
    - `rank` integer, required — 1-based rank within the (min_grade-filtered) ranked result.
  - `has_more` boolean, required
  - `next_cursor` string, nullable
  - `total` integer, nullable
  - `meta` ResponseMeta, required
    - `request_id` string, required — Unique request ID (req_ prefix).
    - `cached` boolean, required
    - `cache_age_s` integer, nullable — 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.
    - `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 smart-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 smart-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'
    - `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 payload.
- `400` — Bad request. On this cursor-paginated route a 400 has TWO distinct causes; branch on error.reason. (1) error.reason="cursor_expired" (with error.param="cursor"): the pagination cursor was invalidated by an upstream data change mid-walk (e.g. the ranking snapshot behind the page refreshed). It is NOT a malformed parameter and NOT a reason to stop: recovery is mechanical -- re-request the first page and walk forward again. There is deliberately no Retry-After and no error.retry_at, because waiting changes nothing. (2) no error.reason: an ordinary invalid request parameter -- check error.param when present, otherwise error.message. Both carry error.code="bad_request" (a FROZEN contract value), so error.reason is the discriminator.
- `401` — Missing or invalid API key
- `402` — Active Pro subscription required
- `403` — Account access denied
- `408` — Request exceeded the server's 30-second transport timeout. The timeout response has an empty body because it is generated before handler-level JSON error shaping.
- `423` — Account is locked
- `429` — Rate limit exceeded. Two 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 before any item work is done.
- `503` — Redis-backed authenticated rate limiter unavailable; retry after the per-process outage cooldown

---

[API](https://skmtc.net/0xinsider/apis/0xinsider-api.md) · [All operations](https://skmtc.net/0xinsider/apis/0xinsider-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/0xinsider/0xinsider-api/revisions/be0e25f93c7d/schema)
