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

# Mispricing leads

> Two-leg purchases that pay at least $1 in every outcome a proven relation
allows, priced from recorded top-of-book asks with estimated fees. The best
leads each run are sized against live order books (`depth`); only leads
with `depth.executable` could be filled at a profit when the books were
read. Leads are observations, not quotes.




## OpenAPI

````yaml /openapi.yaml get /v1/opportunities
openapi: 3.1.0
info:
  title: Routeur Graph API
  version: '2026-09-15'
  summary: >-
    Proven relations between Kalshi and Polymarket contracts, with the history
    and signals built on them.
  description: >
    Routeur reads every open market on Kalshi and global Polymarket, and proves

    which ones imply, exclude or equal each other from each venue's structured

    contract terms and exact arithmetic. No relation is inferred from prose, and

    ambiguous or conflicting evidence is withheld.


    ## Authentication

    Send your key in the `X-API-Key` header, or as `Authorization: Bearer
    <key>`.

    Keys look like `rk_live_<12-character id>_<43-character secret>` and are

    shown once when issued. Keep them server-side.


    ## Rate limits

    Every key has a per-minute limit and a per-day quota set by its tier:


    | Tier | Requests per minute | Requests per day |

    |---|---|---|

    | free | 30 | 2,000 |

    | trader | 120 | 50,000 |

    | pro | 600 | 500,000 |

    | institutional | 3,000 | 10,000,000 |


    Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and

    `X-RateLimit-Reset` (Unix seconds) for the minute window, and

    `X-Daily-Quota-Limit` and `X-Daily-Quota-Remaining` for the day (UTC).

    A request over either limit returns `429` with `Retry-After` in seconds.


    ## Conventions

    - Money and prices are decimal strings in USD (for example `"0.4100"`),
    never
      floats. Prices are per contract that pays $1.
    - Times are RFC 3339 in UTC.

    - Successful responses wrap results in `data`. Graph-derived responses also
      name the `graph_run` they were read from; runs finish hourly.
    - Paginated lists return `next_cursor` when more results exist. Pass it back
      as `cursor` unchanged; cursors are opaque.
    - Unknown or repeated query parameters are rejected with `400`, so typos
    never
      silently widen a query.

    ## Errors

    Errors return a JSON body `{"error": {"code": "...", "message": "..."}}`.

    Branch on `code`; messages may change.


    | Status | Code | Meaning |

    |---|---|---|

    | 400 | `invalid_parameter` | A query parameter is unknown, repeated or out
    of range. |

    | 400 | `invalid_market` | The venue is not `kalshi` or `polymarket`, or the
    market id is malformed. |

    | 401 | `api_key_required` | No key was sent. |

    | 401 | `invalid_api_key` | The key is malformed or unknown. |

    | 401 | `api_key_revoked` | The key was revoked. |

    | 404 | `market_not_found` | The market is not in the latest graph. |

    | 404 | `not_found` | No such endpoint. |

    | 429 | `rate_limited` | The per-minute limit was exceeded. |

    | 429 | `daily_quota_exceeded` | The daily quota is used up. |

    | 503 | `graph_unavailable` | No graph run has finished yet. |

    | 504 | `timeout` | The request took longer than 15 seconds. |

    | 500 | `internal` | Something failed on our side. |


    ## Webhooks

    Mispricing alerts are delivered as signed `POST` requests; see the

    `webhooks` section. Each request carries

    `Routeur-Signature: t=<unix seconds>,v1=<hex>`, where `v1` is

    HMAC-SHA256 of `<t>.<raw body>` keyed by your subscription's signing

    secret. Reject requests whose signature does not match or whose `t` is more

    than five minutes old, and respond `2xx` within 10 seconds.


    `Routeur-Delivery` identifies the delivery and stays the same across
    retries,

    so use it to ignore duplicates. Any response other than `2xx` or `410`, or
    no

    response, is retried with exponential backoff from 30 seconds for up to 8

    attempts; responding `410 Gone` disables the subscription. Endpoints must be

    HTTPS on a public address, and redirects are not followed.
  contact:
    name: Routeur
    url: https://routeur.app
servers:
  - url: https://api.routeur.app
security:
  - apiKey: []
  - bearer: []
tags:
  - name: Events
    description: Real-world events across both venues, the way people browse them.
  - name: Graph
    description: Markets and the relations proven between them.
  - name: History
    description: Recorded quotes and trades.
  - name: Signals
    description: Mispricing leads and unusual trading derived from the graph and history.
  - name: Service
    description: Service status and this document.
paths:
  /v1/opportunities:
    get:
      tags:
        - Signals
      summary: Mispricing leads
      description: >
        Two-leg purchases that pay at least $1 in every outcome a proven
        relation

        allows, priced from recorded top-of-book asks with estimated fees. The
        best

        leads each run are sized against live order books (`depth`); only leads

        with `depth.executable` could be filled at a profit when the books were

        read. Leads are observations, not quotes.
      operationId: listOpportunities
      parameters:
        - name: cross_venue
          in: query
          description: Only leads whose legs are on different venues.
          schema:
            type: boolean
            default: false
        - name: executable
          in: query
          description: Only leads whose live order books could be filled at a profit.
          schema:
            type: boolean
            default: false
        - name: limit
          in: query
          description: Leads to return, best net edge first.
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: Leads from the latest graph run, best estimated net edge first.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
            X-Daily-Quota-Limit:
              $ref: '#/components/headers/X-Daily-Quota-Limit'
            X-Daily-Quota-Remaining:
              $ref: '#/components/headers/X-Daily-Quota-Remaining'
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - data
                  - graph_run
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Opportunity'
                  graph_run:
                    $ref: '#/components/schemas/GraphRunID'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/Unavailable'
        default:
          $ref: '#/components/responses/Error'
components:
  headers:
    X-RateLimit-Limit:
      description: Requests allowed per minute for this key.
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Requests left in the current minute.
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Unix time when the minute window resets.
      schema:
        type: integer
    X-Daily-Quota-Limit:
      description: Requests allowed per UTC day.
      schema:
        type: integer
    X-Daily-Quota-Remaining:
      description: Requests left today.
      schema:
        type: integer
  schemas:
    Opportunity:
      type: object
      additionalProperties: false
      required:
        - relation
        - cross_venue
        - left
        - right
        - legs
        - cost_usd
        - gross_edge_usd
        - estimated_fee_usd
        - net_edge_usd
        - issues
      properties:
        relation:
          $ref: '#/components/schemas/GraphRelation'
        cross_venue:
          type: boolean
        left:
          $ref: '#/components/schemas/Market'
        right:
          $ref: '#/components/schemas/Market'
        legs:
          type: array
          minItems: 2
          maxItems: 2
          items:
            $ref: '#/components/schemas/Leg'
        cost_usd:
          $ref: '#/components/schemas/Decimal'
        gross_edge_usd:
          $ref: '#/components/schemas/Decimal'
        estimated_fee_usd:
          $ref: '#/components/schemas/Decimal'
        net_edge_usd:
          $ref: '#/components/schemas/Decimal'
        issues:
          type:
            - array
            - 'null'
          description: Caveats that apply to the top-of-book estimate.
          items:
            type: string
        depth:
          $ref: '#/components/schemas/Depth'
    GraphRunID:
      type: integer
      description: The graph run the response was read from.
      minimum: 1
    GraphRelation:
      type: string
      description: A relation stated from the left market's side.
      enum:
        - equivalent
        - subset
        - superset
        - contradicts
        - overlaps
    Market:
      type: object
      additionalProperties: false
      required:
        - venue
        - id
        - event_id
        - title
        - yes_label
        - no_label
        - event_mutually_exclusive
        - relations
      properties:
        venue:
          $ref: '#/components/schemas/Venue'
        id:
          $ref: '#/components/schemas/MarketID'
        event_id:
          type: string
        title:
          type: string
        yes_label:
          type: string
        no_label:
          type: string
        category:
          type: string
        strike:
          $ref: '#/components/schemas/Strike'
        event_mutually_exclusive:
          type: boolean
          description: At most one market in the event can resolve Yes.
        game_starts_at:
          $ref: '#/components/schemas/Timestamp'
        quote:
          $ref: '#/components/schemas/Quote'
        relations:
          $ref: '#/components/schemas/RelationCounts'
    Leg:
      type: object
      additionalProperties: false
      required:
        - venue
        - market_id
        - side
        - ask_usd
      properties:
        venue:
          $ref: '#/components/schemas/Venue'
        market_id:
          $ref: '#/components/schemas/MarketID'
        side:
          type: string
          enum:
            - 'yes'
            - 'no'
        ask_usd:
          $ref: '#/components/schemas/Decimal'
    Decimal:
      type: string
      description: An exact decimal number.
      pattern: ^-?[0-9]+(\.[0-9]+)?$
      example: '0.4100'
    Depth:
      type: object
      description: The lead sized against live order books.
      additionalProperties: false
      required:
        - executable
        - pairs
        - cost_usd
        - fees_usd
        - net_usd
        - limited_by
        - observed_at
        - issues
      properties:
        executable:
          type: boolean
          description: >-
            At least one whole pair could be bought for less than it pays, after
            fees and minimum sizes.
        pairs:
          $ref: '#/components/schemas/Decimal'
        cost_usd:
          $ref: '#/components/schemas/Decimal'
        fees_usd:
          $ref: '#/components/schemas/Decimal'
        net_usd:
          $ref: '#/components/schemas/Decimal'
        limited_by:
          type: string
          description: What stopped the fill from growing.
          enum:
            - price
            - depth
            - minimum
            - fees
            - unavailable
        observed_at:
          $ref: '#/components/schemas/Timestamp'
        issues:
          type:
            - array
            - 'null'
          items:
            type: string
    Error:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: A stable machine-readable code.
            message:
              type: string
              description: A human-readable explanation.
    Venue:
      type: string
      enum:
        - kalshi
        - polymarket
    MarketID:
      type: string
      pattern: ^[A-Za-z0-9][A-Za-z0-9_.-]{0,199}$
    Strike:
      type: object
      description: >-
        The market's payoff condition as a structured range on its underlying
        quantity.
      additionalProperties: false
      required:
        - type
        - rules_template_hash
      properties:
        type:
          type: string
          description: The venue's strike type, such as `greater`, `less` or `between`.
        floor:
          type: string
          description: Lower bound as an exact rational, such as `83/2`.
        cap:
          type: string
          description: Upper bound as an exact rational.
        custom:
          type: object
          additionalProperties:
            type: string
        functional:
          type: boolean
        rules_template_hash:
          type: string
          description: >-
            Fingerprint of the rules with bounds masked; markets compare only
            within one template.
    Timestamp:
      type: string
      format: date-time
    Quote:
      type: object
      additionalProperties: false
      required:
        - observed_at
      properties:
        yes_ask_usd:
          $ref: '#/components/schemas/Decimal'
        no_ask_usd:
          $ref: '#/components/schemas/Decimal'
        observed_at:
          $ref: '#/components/schemas/Timestamp'
        price_usd:
          $ref: '#/components/schemas/Decimal'
          description: >-
            On history from before quotes were recorded: the closing Yes price
            of the hour or day. Such points carry no asks.
        source:
          type: string
          enum:
            - venue
            - archive
          description: >-
            Where a price_usd point comes from: the venue's own archive, or
            imported history.
    RelationCounts:
      type: object
      additionalProperties: false
      required:
        - implies
        - implied_by
        - equivalent
        - contradicts
        - overlaps
        - cross_venue
      properties:
        implies:
          type: integer
        implied_by:
          type: integer
        equivalent:
          type: integer
        contradicts:
          type: integer
        overlaps:
          type: integer
        cross_venue:
          type: integer
          description: Relations of any kind to the other venue.
  responses:
    BadRequest:
      description: Invalid parameter or market.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: invalid_parameter
              message: 'Invalid or unsupported query parameter: limit.'
    Unauthorized:
      description: Missing, invalid or revoked API key.
      headers:
        WWW-Authenticate:
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: api_key_required
              message: Send an API key in the X-API-Key header or as a Bearer token.
    TooManyRequests:
      description: Rate limit or daily quota exceeded.
      headers:
        Retry-After:
          description: Seconds until the limit resets.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unavailable:
      description: No graph run has finished yet.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Error:
      description: Any other error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
    bearer:
      type: http
      scheme: bearer
      description: The same API key sent as a Bearer token.

````