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

# Match a contract

> Bidirectional cross-exchange contract lookup. Given any contract ID — a Kalshi ticker or a Polymarket contract ID — returns the full cluster: every contract in the same cross-exchange group and the relationships between them.

The response is identical regardless of which contract ID in the cluster you query. A match lookup is only counted against your daily quota if it is a **new** cluster — repeated lookups of any ID within the same cluster are always free.

<ParamField path="contractID" type="string" required>
  Any Kalshi contract ticker or Polymarket contract slug. See [Understanding contract ID](/quickstart#understanding-contract-id) for the difference between tickers and slugs.
</ParamField>


## OpenAPI

````yaml GET /match/{contractID}
openapi: 3.1.0
info:
  title: Surface API
  description: >-
    Cross-exchange prediction market data. Bidirectional contract matching
    between Kalshi and Polymarket.
  version: 1.0.0
servers:
  - url: https://surfaceapi.com/api/v1
    description: Production
security:
  - apiKey: []
paths:
  /match/{contractID}:
    get:
      tags:
        - Contracts
      summary: Match a contract
      description: >-
        Bidirectional cross-exchange contract lookup. Given any contract ID — a
        Kalshi ticker or a Polymarket contract ID — returns the full cluster:
        every contract in the same cross-exchange group and the relationships
        between them.


        The response is identical regardless of which contract ID in the cluster
        you query. A match lookup is only counted against your daily quota if it
        is a **new** cluster — repeated lookups of any ID within the same
        cluster are always free.
      operationId: matchContract
      parameters:
        - name: contractID
          in: path
          required: true
          description: Any Kalshi contract ticker or Polymarket contract ID.
          schema:
            type: string
          examples:
            kalshi:
              summary: Kalshi ticker
              value: KXNBAGAME-26MAR07LACMEM-LAC
            polymarket:
              summary: Polymarket contract ID
              value: nba-lac-mem-2026-03-07
      responses:
        '200':
          description: Cluster found.
          headers:
            X-Match-Limit-Remaining:
              description: Number of new unique cluster lookups remaining today.
              schema:
                type: integer
            X-Match-Limit-Reset:
              description: RFC3339 UTC timestamp of the next quota reset (midnight UTC).
              schema:
                type: string
                format: date-time
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClusterResponse'
              example:
                contracts:
                  - id: KXNBAGAME-26MAR07LACMEM-LAC
                    parent_market_id: KXNBAGAME-26MAR07LACMEM
                    exchange: kalshi
                    question: Los Angeles C at Memphis Winner?
                    yes_name: Los Angeles C
                    no_name: Memphis
                    rules: >-
                      If Los Angeles C wins the game originally scheduled for
                      Mar 7, 2026, this market resolves Yes.
                  - id: KXNBAGAME-26MAR07LACMEM-MEM
                    parent_market_id: KXNBAGAME-26MAR07LACMEM
                    exchange: kalshi
                    question: Los Angeles C at Memphis Winner?
                    yes_name: Memphis
                    no_name: Los Angeles C
                    rules: >-
                      If Memphis wins the game originally scheduled for Mar 7,
                      2026, this market resolves Yes.
                  - id: nba-lac-mem-2026-03-07
                    parent_market_id: nba-lac-mem-2026-03-07
                    exchange: polymarket
                    question: Clippers vs. Grizzlies
                    yes_name: Clippers
                    no_name: Grizzlies
                    rules: >-
                      If the Clippers win, resolves Clippers. If the Grizzlies
                      win, resolves Grizzlies.
                relationships:
                  - from: KXNBAGAME-26MAR07LACMEM-LAC
                    to: nba-lac-mem-2026-03-07
                    is_inverse: false
                  - from: KXNBAGAME-26MAR07LACMEM-MEM
                    to: nba-lac-mem-2026-03-07
                    is_inverse: true
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missing:
                  summary: No key provided
                  value:
                    error: unauthorized
                    message: Missing API key. Include your key in the X-API-Key header.
                invalid:
                  summary: Invalid or revoked key
                  value:
                    error: unauthorized
                    message: Invalid or revoked API key.
        '404':
          description: >-
            No cross-exchange match found for this contract. Does not count
            against your quota.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: no_match
                message: No cross-exchange match found for this contract.
        '429':
          description: Daily quota exhausted.
          headers:
            X-Match-Limit-Remaining:
              description: Will be 0.
              schema:
                type: integer
            X-Match-Limit-Reset:
              description: RFC3339 UTC timestamp of the next quota reset.
              schema:
                type: string
                format: date-time
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuotaErrorResponse'
              example:
                error: daily_limit_reached
                message: >-
                  Free tier limit of 25 new matches per day reached. Resets at
                  midnight UTC.
                limit: 25
                used: 25
                resets_at: '2026-03-03T00:00:00Z'
        '500':
          description: Internal server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: internal_error
                message: An unexpected error occurred.
components:
  schemas:
    ClusterResponse:
      type: object
      required:
        - contracts
        - relationships
      properties:
        contracts:
          type: array
          items:
            $ref: '#/components/schemas/Contract'
          description: >-
            All contracts in this cross-exchange group. Includes all Kalshi
            contracts and the Polymarket contract they map to.
        relationships:
          type: array
          items:
            $ref: '#/components/schemas/Relationship'
          description: >-
            Directed edges from each Kalshi contract to the Polymarket contract,
            with inversion flag and confidence.
    ErrorResponse:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
          description: Machine-readable error code.
        message:
          type: string
          description: Human-readable description of the error.
    QuotaErrorResponse:
      allOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          required:
            - limit
            - used
            - resets_at
          properties:
            limit:
              type: integer
              description: Your daily quota for new unique cluster lookups.
            used:
              type: integer
              description: Number of new unique clusters accessed today.
            resets_at:
              type: string
              format: date-time
              description: RFC3339 UTC timestamp of the next quota reset (midnight UTC).
    Contract:
      type: object
      required:
        - id
        - exchange
        - question
      properties:
        id:
          type: string
          description: The contract identifier on its exchange.
        parent_market_id:
          type: string
          description: >-
            The parent market or event ID. For Kalshi this is the series+date
            portion; for Polymarket it is the event slug.
        exchange:
          type: string
          enum:
            - kalshi
            - polymarket
          description: The exchange this contract belongs to.
        question:
          type: string
          description: The market question as displayed on the exchange.
        yes_name:
          type: string
          description: >-
            Label for the YES outcome (e.g. the team name on a Kalshi
            moneyline).
        no_name:
          type: string
          description: Label for the NO outcome.
        rules:
          type: string
          description: Resolution rules for the contract. Omitted if not available.
    Relationship:
      type: object
      required:
        - from
        - to
        - is_inverse
      properties:
        from:
          type: string
          description: Kalshi contract ID.
        to:
          type: string
          description: Polymarket contract ID.
        is_inverse:
          type: boolean
          description: >-
            When true, YES on the Kalshi contract is equivalent to NO on the
            Polymarket contract. Invert prices and sides accordingly.
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key

````