---
title: "Explore markets"
method: GET
path: "/api/v1/markets/explore"
tags: ["Markets"]
---

# Explore markets

`GET /api/v1/markets/explore`

Browse whale-active titled markets with category, platform, status, and keyword filters. Explore is Polymarket-only: the platform parameter is accepted for backward-compatibility but every request returns Polymarket markets. Paginates visible discovery entries rather than raw market rows, returns live category/platform facets alongside grouped event clusters or standalone markets, and includes total on the first page only. Each grouped event contains at most 12 markets, retaining the selected representative within that cap. Categories come straight from provider metadata (Polymarket Gamma) and facets are flat value/label/count rows.

## Query parameters

- `category` string
- `status` 'active' | 'closed' | 'all'
- `platform` 'polymarket'
- `sort` 'trending' | 'hot' | 'expiring' | 'whales' | 'volume' | 'newest'
- `cursor` string
- `limit` integer
- `q` string

## Headers

- `If-None-Match` string

## Response `200`

Grouped market discovery results

- object
  - `object` 'list', required
  - `data` ExploreEntry[], required
    - union
      - ExploreGroup
        - `type` 'group', required
        - `event_slug` string, required
        - `parent_title` string, required
        - `image` string, nullable
        - `platform` string, nullable
        - `category` string, nullable
        - `markets` ExploreMarket[], required — Markets in the event cluster, ranked by volume with condition_id as the tie-breaker. The selected representative is retained within the 12-market cap.
          - `id` string, required
          - `condition_id` string, required
          - `title` string, required — Non-empty market title.
          - `slug` string, nullable — Provider-native market slug.
          - `url_slug` string, nullable — First-party market page slug used for internal links.
          - `image` string, nullable
          - `icon` string, nullable
          - `category` string, nullable
          - `platform` string, nullable
          - `status` 'active' | 'closed', required
          - `volume` number, nullable
          - `liquidity` number, nullable
          - `whale_trade_count` integer, nullable
          - `whale_distinct_wallets` integer, nullable
          - `whale_total_usd` number, nullable
          - `whale_last_trade_at` string, date-time, nullable
          - `end_date` string, date-time, nullable
          - `created_at` string, date-time, nullable
          - `outcome_yes` string, nullable
          - `token_id_yes` string, nullable — The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the YES outcome; null when unavailable (e.g. Kalshi markets, unsynced markets).
          - `outcome_no` string, nullable
          - `token_id_no` string, nullable — The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the NO outcome; null when unavailable (e.g. Kalshi markets, unsynced markets).
          - `event_slug` string, nullable
          - `kalshi_series_slug` string, nullable
          - `smart_score` number, nullable
          - `smart_count` integer, nullable
          - `smart_label` string, nullable
          - `outcome_yes_label` string, nullable — Display label for the YES/outcome_index=0 side, enriched from provider outcome metadata when available.
          - `outcome_no_label` string, nullable — Display label for the NO/outcome_index=1 side, enriched from provider outcome metadata when available.
          - `outcome_yes_provider_id` integer, nullable — Provider-owned YES/outcome_index=0 identifier when available for trade-ticket wiring.
          - `outcome_no_provider_id` integer, nullable — Provider-owned NO/outcome_index=1 identifier when available for trade-ticket wiring.
          - `open_interest` number, nullable
          - `oi_change_pct` number, nullable
          - `price_points` array[], nullable
            - number[]
          - `no_price_points` array[], nullable
            - number[]
          - `last_price` number, nullable
          - `no_last_price` number, nullable
          - `change_pct_24h` number, nullable
          - `no_change_pct_24h` number, nullable
          - `discover_score` number, nullable — Backend-owned deterministic market discovery score used by the hot sort.
          - `score_components` object
            - `volume_signal` number, nullable
            - `whale_signal` number, nullable
            - `liquidity_signal` number, nullable
            - `recency_signal` number, nullable
            - `smart_money_signal` number, nullable
            - `price_move_signal` number, nullable
            - `missing_price_penalty` number, required
          - `freshness` object
            - `enrichment_status` 'available' | 'unavailable', required
            - `price_status` 'available' | 'unavailable', required
        - `rep_volume` number, nullable
        - `rep_whales` integer, nullable
      - ExploreStandalone
        - `type` 'standalone', required
        - `market` ExploreMarket, required
          - `id` string, required
          - `condition_id` string, required
          - `title` string, required — Non-empty market title.
          - `slug` string, nullable — Provider-native market slug.
          - `url_slug` string, nullable — First-party market page slug used for internal links.
          - `image` string, nullable
          - `icon` string, nullable
          - `category` string, nullable
          - `platform` string, nullable
          - `status` 'active' | 'closed', required
          - `volume` number, nullable
          - `liquidity` number, nullable
          - `whale_trade_count` integer, nullable
          - `whale_distinct_wallets` integer, nullable
          - `whale_total_usd` number, nullable
          - `whale_last_trade_at` string, date-time, nullable
          - `end_date` string, date-time, nullable
          - `created_at` string, date-time, nullable
          - `outcome_yes` string, nullable
          - `token_id_yes` string, nullable — The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the YES outcome; null when unavailable (e.g. Kalshi markets, unsynced markets).
          - `outcome_no` string, nullable
          - `token_id_no` string, nullable — The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the NO outcome; null when unavailable (e.g. Kalshi markets, unsynced markets).
          - `event_slug` string, nullable
          - `kalshi_series_slug` string, nullable
          - `smart_score` number, nullable
          - `smart_count` integer, nullable
          - `smart_label` string, nullable
          - `outcome_yes_label` string, nullable — Display label for the YES/outcome_index=0 side, enriched from provider outcome metadata when available.
          - `outcome_no_label` string, nullable — Display label for the NO/outcome_index=1 side, enriched from provider outcome metadata when available.
          - `outcome_yes_provider_id` integer, nullable — Provider-owned YES/outcome_index=0 identifier when available for trade-ticket wiring.
          - `outcome_no_provider_id` integer, nullable — Provider-owned NO/outcome_index=1 identifier when available for trade-ticket wiring.
          - `open_interest` number, nullable
          - `oi_change_pct` number, nullable
          - `price_points` array[], nullable
            - number[]
          - `no_price_points` array[], nullable
            - number[]
          - `last_price` number, nullable
          - `no_last_price` number, nullable
          - `change_pct_24h` number, nullable
          - `no_change_pct_24h` number, nullable
          - `discover_score` number, nullable — Backend-owned deterministic market discovery score used by the hot sort.
          - `score_components` object
            - `volume_signal` number, nullable
            - `whale_signal` number, nullable
            - `liquidity_signal` number, nullable
            - `recency_signal` number, nullable
            - `smart_money_signal` number, nullable
            - `price_move_signal` number, nullable
            - `missing_price_penalty` number, required
          - `freshness` object
            - `enrichment_status` 'available' | 'unavailable', required
            - `price_status` 'available' | 'unavailable', required
  - `has_more` boolean, required
  - `next_cursor` string, nullable
  - `total` integer, nullable — Total matching visible entries after grouping. Present on the first page and omitted on cursor pages.
  - `facets` ExploreFacets, required
    - `categories` ExploreFacetValue[], required
      - `value` string, required
      - `label` string, required
      - `count` integer, required
    - `platforms` ExploreFacetValue[], required
      - `value` string, required
      - `label` string, required
      - `count` integer, required
  - `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.
  - `computed_at` string, date-time — When this response body was computed. Present whenever the body came from, or was just written to, the 60s explore cache. Pair it with fresh_for_seconds to derive how much longer the body may be reused: fresh_for_seconds - age(computed_at). Excluded from the ETag validator, so a body recomputed with identical data keeps its validator.
  - `fresh_for_seconds` integer — How long the body computed at computed_at is good for, in seconds. Deliberately not pre-subtracted: a cached body cannot carry a number that changes while it sits in the cache. Excluded from the ETag validator.

## Other responses

- `304` — Not Modified. Returned when If-None-Match matches the current explore payload.
- `400` — Invalid request parameter
- `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)
