> ## Documentation Index
> Fetch the complete documentation index at: https://pmxt.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Fetch Events

> Fetch events with optional keyword search. Events group related markets together (e.g., "Who will be Fed Chair?" contains multiple candidate markets).

<Info>
  **Faster with an API key** — With a PMXT API key this endpoint is served from an indexed catalog (\~10 ms) instead of proxying to the venue (\~500 ms). No code changes required — same request, same response, just faster. [Learn more](/concepts/unified-schema).
</Info>

## Use cases

### Search by keyword

Find all events mentioning "Trump" on Polymarket:

<CodeGroup>
  ```python Python theme={null}
  import pmxt

  # Optional: pass pmxt_api_key for ~100x faster catalog-backed lookups
  api = pmxt.Polymarket()
  events = api.fetch_events(query="trump", limit=5)
  for event in events:
      print(event.title, event.url)
  ```

  ```javascript JavaScript theme={null}
  import { Polymarket } from "pmxtjs";

  // Optional: pass pmxtApiKey for ~100x faster catalog-backed lookups
  const api = new Polymarket();
  const events = await api.fetchEvents({ query: "trump", limit: 5 });
  events.forEach((e) => console.log(e.title, e.url));
  ```

  ```bash curl theme={null}
  curl "https://api.pmxt.dev/api/polymarket/fetchEvents?query=trump&limit=5" \
    -H "Authorization: Bearer $PMXT_API_KEY"
  ```
</CodeGroup>

### Filter by category

Get all crypto-related events on Kalshi:

<CodeGroup>
  ```python Python theme={null}
  import pmxt

  api = pmxt.Limitless()
  events = api.fetch_events(category="Crypto", limit=5)
  ```

  ```javascript JavaScript theme={null}
  import { Limitless } from "pmxtjs";

  const api = new Limitless();
  const events = await api.fetchEvents({ category: "Crypto", limit: 5 });
  ```

  ```bash curl theme={null}
  curl "https://api.pmxt.dev/api/limitless/fetchEvents?category=Crypto&limit=5" \
    -H "Authorization: Bearer $PMXT_API_KEY"
  ```
</CodeGroup>

### Filter by tags

Get events tagged with geopolitics:

<CodeGroup>
  ```python Python theme={null}
  import pmxt

  api = pmxt.Polymarket()
  events = api.fetch_events(tags=["Geopolitics", "Middle East"], limit=5)
  ```

  ```javascript JavaScript theme={null}
  import { Polymarket } from "pmxtjs";

  const api = new Polymarket();
  const events = await api.fetchEvents({
    tags: ["Geopolitics", "Middle East"],
    limit: 5,
  });
  ```

  ```bash curl theme={null}
  curl "https://api.pmxt.dev/api/polymarket/fetchEvents?tags=Geopolitics&tags=Middle+East&limit=5" \
    -H "Authorization: Bearer $PMXT_API_KEY"
  ```
</CodeGroup>

### Filter active by category

Find active sports events:

<CodeGroup>
  ```python Python theme={null}
  import pmxt

  api = pmxt.Polymarket()
  events = api.fetch_events(
      category="Sports",
      status="active",
      limit=5,
  )
  ```

  ```javascript JavaScript theme={null}
  import { Polymarket } from "pmxtjs";

  const api = new Polymarket();
  const events = await api.fetchEvents({
    category: "Sports",
    status: "active",
    limit: 5,
  });
  ```

  ```bash curl theme={null}
  curl "https://api.pmxt.dev/api/polymarket/fetchEvents?category=Sports&status=active&limit=5" \
    -H "Authorization: Bearer $PMXT_API_KEY"
  ```
</CodeGroup>


## OpenAPI

````yaml GET /api/{exchange}/fetchEvents
openapi: 3.0.0
info:
  title: PMXT Hosted API
  description: >-
    One API for supported prediction markets. Hosted catalog search in under
    10ms, a unified schema for supported venues, and venue-native trading where
    venues expose writes.
  version: 2.54.0
servers:
  - url: https://api.pmxt.dev
    description: Hosted PMXT (production)
security: []
tags:
  - name: Hosted
    description: >-
      Requires a PMXT API key. These endpoints use the cross-venue catalog and
      have no local equivalent.
  - name: Local Only
    description: >-
      Executed locally by the SDK against the venue. Never proxied through PMXT
      servers.
  - name: Data Feeds
    description: >-
      Auxiliary price and oracle data feeds (Binance, Chainlink).
      CCXT-compatible method names and response shapes.
paths:
  /api/{exchange}/fetchEvents:
    get:
      summary: Fetch Events
      description: >-
        Fetch events with optional keyword search. Events group related markets
        together (e.g., "Who will be Fed Chair?" contains multiple candidate
        markets).
      operationId: fetchEvents
      parameters:
        - $ref: '#/components/parameters/ExchangeParam'
        - in: query
          name: query
          required: false
          schema:
            type: string
          description: For keyword search
        - in: query
          name: limit
          required: false
          schema:
            type: number
          description: Maximum number of results to return
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque venue pagination cursor, where supported.
        - in: query
          name: offset
          required: false
          schema:
            type: number
          description: Pagination offset — number of results to skip
        - in: query
          name: sort
          required: false
          schema:
            type: string
            enum:
              - volume
              - liquidity
              - newest
          description: Sort order for results
        - in: query
          name: status
          required: false
          schema:
            type: string
            enum:
              - active
              - inactive
              - closed
              - all
          description: >-
            Filter by event status (default: 'active', 'inactive' and 'closed'
            are interchangeable)
        - in: query
          name: searchIn
          required: false
          schema:
            type: string
            enum:
              - title
              - description
              - both
          description: 'Where to search (default: ''title'')'
        - in: query
          name: eventId
          required: false
          schema:
            type: string
          description: Direct lookup by event ID
        - in: query
          name: slug
          required: false
          schema:
            type: string
          description: Lookup by event slug
        - in: query
          name: series
          required: false
          schema:
            type: string
          description: >-
            Filter events by their parent series. Accepts the venue-native
            series id / ticker / slug (e.g. Kalshi `"KXATPMATCH"`, Polymarket
            `"wta"`). Passed through to the vendor where supported, otherwise
            applied to `sourceMetadata` after fetch.
        - in: query
          name: filter
          required: false
          schema:
            allOf:
              - $ref: '#/components/schemas/EventFilterCriteria'
          description: Optional client-side filter applied after fetching
        - in: query
          name: category
          required: false
          schema:
            type: string
          description: >-
            Filter by category. Each event belongs to a venue-assigned category
            such as "Sports", "Politics", "Crypto", "Bitcoin", "Soccer",
            "Economic Policy" (Polymarket) or "Sports", "Mentions" (Kalshi).
        - in: query
          name: tags
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Filter by tags. Returns events matching ANY of the provided tags.
            Tags are more specific than categories -- for example a "Politics"
            event might carry tags ["Politics", "Geopolitics", "Middle East",
            "Iran"]. Common tags include "Crypto", "Elections", "Fed Rates",
            "FIFA World Cup", "Trump".
        - in: query
          name: sourceExchange
          required: false
          schema:
            type: string
          description: >-
            Filter by source venue (e.g. 'polymarket', 'kalshi', 'myriad').
            `exchange` is an alias.
        - in: query
          name: exchange
          required: false
          schema:
            type: string
          description: Alias for `sourceExchange`.
      responses:
        '200':
          description: Fetch Events response
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/BaseResponse'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/UnifiedEvent'
      security: []
      x-codeSamples:
        - lang: python
          label: Polymarket
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Polymarket(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Limitless
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Limitless(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Kalshi
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Kalshi(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: KalshiDemo
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.KalshiDemo(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Probable
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Probable(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Baozi
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Baozi(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Myriad
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Myriad(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Opinion
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Opinion(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Metaculus
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Metaculus(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Smarkets
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Smarkets(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: PolymarketUS
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.PolymarketUS(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Hyperliquid
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Hyperliquid(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: GeminiTitan
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.GeminiTitan(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: SuiBets
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.SuiBets(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Rain
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Rain(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Hunch
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Hunch(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: python
          label: Router
          source: |-
            import pmxt

            # API key optional — enables faster catalog-backed lookups
            exchange = pmxt.Router(
                pmxt_api_key="YOUR_PMXT_API_KEY",
            )
            result = exchange.fetch_events(
                query="election",
                limit=10,
                status="active",
            )
        - lang: javascript
          label: Polymarket
          source: |-
            import { Polymarket } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Polymarket({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Limitless
          source: |-
            import { Limitless } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Limitless({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Kalshi
          source: |-
            import { Kalshi } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Kalshi({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: KalshiDemo
          source: |-
            import { KalshiDemo } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new KalshiDemo({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Probable
          source: |-
            import { Probable } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Probable({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Baozi
          source: |-
            import { Baozi } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Baozi({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Myriad
          source: |-
            import { Myriad } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Myriad({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Opinion
          source: |-
            import { Opinion } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Opinion({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Metaculus
          source: |-
            import { Metaculus } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Metaculus({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Smarkets
          source: |-
            import { Smarkets } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Smarkets({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: PolymarketUS
          source: |-
            import { PolymarketUS } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new PolymarketUS({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Hyperliquid
          source: |-
            import { Hyperliquid } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Hyperliquid({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: GeminiTitan
          source: |-
            import { GeminiTitan } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new GeminiTitan({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: SuiBets
          source: |-
            import { SuiBets } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new SuiBets({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Rain
          source: |-
            import { Rain } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Rain({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Hunch
          source: |-
            import { Hunch } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Hunch({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
        - lang: javascript
          label: Router
          source: |-
            import { Router } from "pmxtjs";

            // API key optional — enables faster catalog-backed lookups
            const exchange = new Router({
              pmxtApiKey: "YOUR_PMXT_API_KEY",
            });
            const result = await exchange.fetchEvents({
              query: "election",
              limit: 10,
              status: "active",
            });
components:
  parameters:
    ExchangeParam:
      in: path
      name: exchange
      schema:
        type: string
        enum:
          - polymarket
          - kalshi
          - kalshi-demo
          - limitless
          - probable
          - baozi
          - myriad
          - opinion
          - metaculus
          - smarkets
          - polymarket_us
          - gemini-titan
          - hyperliquid
          - suibets
          - rain
          - hunch
          - router
      required: true
      description: The prediction market exchange to target.
  schemas:
    EventFilterCriteria:
      type: object
      properties:
        text:
          type: string
        searchIn:
          type: array
          items:
            type: string
            enum:
              - title
              - description
              - category
              - tags
          description: 'Default: [''title'']'
        category:
          type: string
          description: >-
            Filter by category. Common values: "Sports", "Politics", "Crypto",
            "Bitcoin", "Soccer", "Economic Policy" (Polymarket) or "Sports",
            "Mentions" (Kalshi).
        tags:
          type: array
          items:
            type: string
          description: >-
            Match events that have ANY of these tags. Examples: ["Crypto"],
            ["Politics", "Geopolitics", "Middle East"], ["Sports", "FIFA World
            Cup"].
        marketCount:
          type: object
          properties:
            min:
              type: number
            max:
              type: number
        totalVolume:
          type: object
          properties:
            min:
              type: number
            max:
              type: number
          description: Sum of market volumes
    BaseResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        error:
          $ref: '#/components/schemas/ErrorDetail'
    UnifiedEvent:
      type: object
      description: >-
        A grouped collection of related markets (e.g., "Who will be Fed Chair?"
        contains multiple candidate markets).
      properties:
        id:
          type: string
          description: The unique identifier for this event.
        title:
          type: string
          description: The event title (e.g., "Who will be Fed Chair?").
        description:
          type: string
          description: Long-form event description.
        slug:
          type: string
          description: URL-friendly slug for the event.
        markets:
          type: array
          items:
            $ref: '#/components/schemas/UnifiedMarket'
          description: Markets grouped under this event.
        volume24h:
          type: number
          description: Trading volume over the past 24 hours (USD).
        volume:
          type: number
          description: >-
            Total / Lifetime volume (sum across markets; undefined if no market
            provides it)
        url:
          type: string
          description: Canonical URL to view the event on the venue.
        image:
          type: string
          description: Optional image URL for the event.
        category:
          type: string
          description: >-
            Optional category label. Venue-defined — common values include
            "Sports", "Politics", "Crypto", "Economics", "Science", "Culture".
            Polymarket uses finer-grained categories like "Bitcoin", "Soccer",
            "Economic Policy"; Kalshi uses broader ones like "Sports" or
            "Mentions".
        tags:
          type: array
          items:
            type: string
          description: >-
            Optional list of tags. More granular than category — e.g. ["Sports",
            "FIFA World Cup", "2026 FIFA World Cup"] or ["Politics",
            "Geopolitics", "Middle East"]. Tags vary by venue: Polymarket
            markets carry several, Kalshi typically one.
        sourceMetadata:
          type: object
          additionalProperties: {}
          description: >-
            Raw venue-specific metadata not captured by first-class fields (e.g.
            Kalshi series_ticker / series_title, Polymarket series). Passed
            through verbatim so downstream consumers can recover anything the
            unified shape omits. Each venue populates what it has.
        sourceExchange:
          type: string
          description: >-
            The exchange/venue this event originates from (e.g. 'polymarket',
            'kalshi'). Populated by the Router.
      required:
        - id
        - title
        - description
        - slug
        - markets
        - volume24h
        - url
    ErrorDetail:
      type: object
      description: >-
        Structured error envelope returned inside `BaseResponse.error` and
        `ErrorResponse.error`. Hosted-mode endpoints populate `code`,
        `retryable`, and optionally `exchange` / `detail`; legacy local-mode
        endpoints may still return only `message`.
      properties:
        message:
          type: string
          description: Human-readable error message.
        code:
          type: string
          description: >-
            Stable machine-readable error code. Hosted-mode errors use the
            `HostedTradingError` family (e.g. `INSUFFICIENT_ESCROW_BALANCE`,
            `BUILT_ORDER_EXPIRED`); pre-hosted local errors use the legacy
            family (e.g. `BAD_REQUEST`, `NOT_FOUND`).
          enum:
            - HOSTED_TRADING_ERROR
            - INSUFFICIENT_ESCROW_BALANCE
            - ORDER_SIZE_TOO_SMALL
            - INVALID_API_KEY
            - OUTCOME_NOT_FOUND
            - CATALOG_UNAVAILABLE
            - BUILT_ORDER_EXPIRED
            - INVALID_SIGNATURE
            - NO_LIQUIDITY
            - MISSING_WALLET_ADDRESS
            - BAD_REQUEST
            - AUTHENTICATION_ERROR
            - PERMISSION_DENIED
            - NOT_FOUND
            - ORDER_NOT_FOUND
            - MARKET_NOT_FOUND
            - EVENT_NOT_FOUND
            - RATE_LIMIT_EXCEEDED
            - INVALID_ORDER
            - INSUFFICIENT_FUNDS
            - VALIDATION_ERROR
            - NETWORK_ERROR
            - EXCHANGE_NOT_AVAILABLE
            - NOT_SUPPORTED
        retryable:
          type: boolean
          description: >-
            Hint for clients: when `true`, the same request may succeed on retry
            (e.g. transient network or rate-limit conditions); when `false`, the
            caller should not retry without modifying the request.
        exchange:
          type: string
          nullable: true
          description: >-
            Venue the error originated from, when known (e.g. 'polymarket',
            'kalshi').
        detail:
          type: object
          additionalProperties: {}
          nullable: true
          description: >-
            Free-form hosted-mode detail blob. Shape depends on `code` — e.g.
            for `INSUFFICIENT_ESCROW_BALANCE` it may include `{ requested,
            available }`; for `ORDER_SIZE_TOO_SMALL` it may include `{ min }`;
            for `BUILT_ORDER_EXPIRED` it may include `{ expiry }`.
    UnifiedMarket:
      type: object
      properties:
        marketId:
          type: string
          description: The unique identifier for this market
        eventId:
          type: string
          description: Link to parent event
        title:
          type: string
          description: The market title (e.g., "Will BTC close above $100k on Dec 31?").
        description:
          type: string
          description: Long-form market description or resolution criteria.
        slug:
          type: string
          description: URL-friendly slug for the market.
        outcomes:
          type: array
          items:
            $ref: '#/components/schemas/MarketOutcome'
          description: The possible outcomes for this market.
        resolutionDate:
          type: string
          format: date-time
          description: >-
            When the market is scheduled to resolve. Optional because some
            venues do not publish a cutoff for every market (e.g. Opinion
            categorical children) — emit `undefined` rather than coercing to
            epoch.
        volume24h:
          type: number
          description: Trading volume over the past 24 hours (USD).
        volume:
          type: number
          description: Total / Lifetime volume
        liquidity:
          type: number
          description: Current market liquidity (USD).
        openInterest:
          type: number
          description: Total value of outstanding contracts (USD).
        url:
          type: string
          description: Canonical URL to view the market on the venue.
        image:
          type: string
          description: Optional image URL for the market.
        category:
          type: string
          description: >-
            Optional category label. Venue-defined — common values include
            "Sports", "Politics", "Crypto", "Economics", "Science", "Culture".
            Polymarket uses finer-grained categories like "Bitcoin", "Soccer",
            "Economic Policy"; Kalshi uses broader ones like "Sports" or
            "Mentions".
        tags:
          type: array
          items:
            type: string
          description: >-
            Optional list of tags. More granular than category — e.g. ["Crypto",
            "Crypto Prices", "Bitcoin"] or ["Politics", "Elections", "Trump"].
            Tags vary by venue: Polymarket markets carry several, Kalshi
            typically one.
        tickSize:
          type: number
          description: Minimum price increment (e.g., 0.01, 0.001)
        status:
          type: string
          description: Venue-native lifecycle status (e.g. 'active', 'closed', 'archived').
        contractAddress:
          type: string
          description: >-
            On-chain contract / condition identifier where applicable
            (Polymarket conditionId, etc.).
        sourceMetadata:
          type: object
          additionalProperties: {}
          description: >-
            Raw venue-specific metadata not captured by first-class fields (e.g.
            Kalshi series_ticker / series_title from the parent event,
            Polymarket series). Passed through verbatim so downstream consumers
            can recover anything the unified shape omits. Each venue populates
            what it has.
        sourceExchange:
          type: string
          description: >-
            The exchange/venue this market originates from (e.g. 'polymarket',
            'kalshi'). Populated by the Router.
        'yes':
          allOf:
            - $ref: '#/components/schemas/MarketOutcome'
          description: Convenience accessor for the YES outcome on a binary market.
        'no':
          allOf:
            - $ref: '#/components/schemas/MarketOutcome'
          description: Convenience accessor for the NO outcome on a binary market.
        up:
          allOf:
            - $ref: '#/components/schemas/MarketOutcome'
          description: Convenience accessor for the UP outcome on a binary market.
        down:
          allOf:
            - $ref: '#/components/schemas/MarketOutcome'
          description: Convenience accessor for the DOWN outcome on a binary market.
      required:
        - marketId
        - title
        - description
        - outcomes
        - volume24h
        - liquidity
        - url
    MarketOutcome:
      type: object
      properties:
        outcomeId:
          type: string
          description: >-
            Outcome ID for trading operations (CLOB Token ID for Polymarket,
            Market Ticker for Kalshi)
        marketId:
          type: string
          description: >-
            The market this outcome belongs to (set automatically when outcomes
            are built)
        label:
          type: string
          description: Human-readable outcome label (e.g., "Yes", "No", candidate name).
        price:
          type: number
          description: Probability between 0.0 and 1.0.
        priceChange24h:
          type: number
          description: >-
            Change in price over the past 24 hours, as an absolute probability
            delta.
        metadata:
          type: object
          additionalProperties: {}
          description: Exchange-specific metadata (e.g., clobTokenId for Polymarket)
      required:
        - outcomeId
        - label
        - price

````