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

# List live cross-venue opportunities

> Live cross-venue opportunities with the feed's age; 503 when the feed is unreadable, never published or stale.



## OpenAPI

````yaml /openapi.json get /public/v1/opportunities
openapi: 3.1.0
info:
  description: >-
    Read-only prediction-market data across Polymarket, Kalshi, Limitless and
    predict.fun over REST. Every request sends your key as `Authorization:
    Bearer tl_live_…`; try any endpoint from its page.
  title: TrendlineLabs Public API
  version: '1'
servers:
  - description: Production
    url: https://systems.trendlinelabs.ai
security: []
tags:
  - description: 'Read back the key you are calling with: its scopes, tier and rate limit.'
    name: Your key
  - description: Events group markets across venues; each carries a `universal_id`.
    name: Events
  - description: One market's order book, price history and trades on one venue.
    name: Markets
  - description: Current prices for a set of markets in one call.
    name: Prices
  - description: Live cross-venue price gaps on the same market.
    name: Opportunities
paths:
  /public/v1/opportunities:
    get:
      tags:
        - Opportunities
      summary: List live cross-venue opportunities
      description: >-
        Live cross-venue opportunities with the feed's age; 503 when the feed is
        unreadable, never published or stale.
      operationId: listOpportunities
      parameters:
        - description: >-
            only opportunities in this sport or category (for example politics);
            omit for all
          in: query
          name: sport
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              only opportunities in this sport or category (for example
              politics); omit for all
            title: Sport
      responses:
        '200':
          content:
            application/json:
              example:
                metadata:
                  active_markets: 37
                  age_s: 8
                  last_update_secs: 1791266392
                  total_markets: 2140
                  total_publishes: 88412
                opportunities:
                  - arb_type: kalshi_yes_poly_no
                    category_group: crypto
                    detected_at_ms: 1791266396000
                    estimated_fee_cents: 2
                    event_universal_id: ev-btc-100k-2026
                    gas_fee_cents: 0
                    image_url: null
                    kalshi_event_title: Bitcoin price at year end
                    kalshi_no_ask: 60
                    kalshi_no_size: 205
                    kalshi_ticker: KXBTC-26DEC31-B100000
                    kalshi_title: Bitcoin above $100k?
                    kalshi_url: https://kalshi.com/markets/kxbtc
                    kalshi_yes_ask: 41
                    kalshi_yes_size: 310
                    limitless_no_ask: null
                    limitless_no_size: null
                    limitless_slug: null
                    limitless_title: null
                    limitless_url: null
                    limitless_yes_ask: null
                    limitless_yes_size: null
                    market_universal_id: mk-btc-100k-2026
                    max_contracts: 120
                    no_price: 55
                    poly_market_title: Bitcoin above $100k on December 31?
                    poly_no_ask: 55
                    poly_no_size: 120
                    poly_slug: bitcoin-above-100k-on-december-31
                    poly_title: Bitcoin above $100k on December 31?
                    poly_url: >-
                      https://polymarket.com/event/bitcoin-above-100k-on-december-31
                    poly_yes_ask: 46
                    poly_yes_size: 1500
                    profit_cents: 2
                    sport: crypto
                    total_cost_cents: 98
                    yes_price: 41
              schema:
                $ref: '#/components/schemas/PublicOpportunitiesResponse'
          description: Successful Response
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            X-RateLimit-Limit:
              description: >-
                requests allowed per window for this key; present once the key
                passed the limiter
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: >-
                requests left in this window; present once the key passed the
                limiter
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                seconds until the window resets; present once the key passed the
                limiter
              schema:
                type: integer
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicError'
          description: 'unauthorized: no key, or a key that is unknown, revoked or expired.'
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            WWW-Authenticate:
              description: Bearer realm="public-api"
              schema:
                type: string
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicError'
          description: >-
            insufficient_scope: the key lacks the scope this route needs;
            ip_not_allowed: the request came from an address outside the key's
            allowlist; tier_required: the key owner's plan does not include API
            access; access_not_granted: API access is free but granted on
            request; request it on https://trendlinelabs.ai/developer.
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
        '422':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicError'
          description: >-
            invalid_request: a parameter is missing, unknown, malformed or out
            of bounds, or a cursor does not match.
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            X-RateLimit-Limit:
              description: >-
                requests allowed per window for this key; present once the key
                passed the limiter
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: >-
                requests left in this window; present once the key passed the
                limiter
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                seconds until the window resets; present once the key passed the
                limiter
              schema:
                type: integer
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicError'
          description: >-
            rate_limited: too many requests, or too many failed authentications;
            wait Retry-After seconds.
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            Retry-After:
              description: seconds to wait before retrying
              schema:
                type: integer
            X-RateLimit-Limit:
              description: >-
                requests allowed per window for this key; present once the key
                passed the limiter
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: >-
                requests left in this window; present once the key passed the
                limiter
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                seconds until the window resets; present once the key passed the
                limiter
              schema:
                type: integer
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicError'
          description: >-
            internal_error: an unexpected failure on our side; quote the request
            id. The body may instead be text/plain with no envelope and no
            X-RateLimit-* headers when a failure escapes the route's own
            handling.
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            X-RateLimit-Limit:
              description: >-
                requests allowed per window for this key; present once the key
                passed the limiter
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: >-
                requests left in this window; present once the key passed the
                limiter
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                seconds until the window resets; present once the key passed the
                limiter
              schema:
                type: integer
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicError'
          description: >-
            dependency_unavailable: one of our own stores could not be read;
            retry later; upstream_unavailable: the data source is unreadable,
            absent or older than its bound; never an empty 200.
          headers:
            Cache-Control:
              description: no-store on every answer
              schema:
                type: string
            X-RateLimit-Limit:
              description: >-
                requests allowed per window for this key; present once the key
                passed the limiter
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: >-
                requests left in this window; present once the key passed the
                limiter
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                seconds until the window resets; present once the key passed the
                limiter
              schema:
                type: integer
            X-Request-Id:
              description: >-
                this request's id (yours, when you sent X-Request-Id); equals
                the envelope's request_id
              schema:
                type: string
      security:
        - HTTPBearer: []
      x-codeSamples:
        - label: cURL
          lang: bash
          source: >
            #!/bin/sh

            # listOpportunities: List live cross-venue opportunities. Needs
            TRENDLINE_API_KEY; TRENDLINE_API_BASE overrides the base.

            curl -sS
            "${TRENDLINE_API_BASE:-https://systems.trendlinelabs.ai}/public/v1/opportunities"
            \
              -H "Authorization: Bearer $TRENDLINE_API_KEY"
        - label: Python
          lang: python
          source: >
            """listOpportunities: List live cross-venue opportunities. Needs
            TRENDLINE_API_KEY; TRENDLINE_API_BASE overrides the base."""

            import os


            import requests


            BASE = os.environ.get("TRENDLINE_API_BASE",
            "https://systems.trendlinelabs.ai")

            KEY = os.environ["TRENDLINE_API_KEY"]


            r = requests.get(f"{BASE}/public/v1/opportunities", params={},
                             headers={"Authorization": f"Bearer {KEY}"}, timeout=10)
            r.raise_for_status()

            print(r.json())
        - label: JavaScript
          lang: javascript
          source: >
            // listOpportunities: List live cross-venue opportunities. Needs
            TRENDLINE_API_KEY; TRENDLINE_API_BASE overrides the base.

            const BASE = process.env.TRENDLINE_API_BASE ??
            "https://systems.trendlinelabs.ai";

            const KEY = process.env.TRENDLINE_API_KEY;

            const r = await fetch(`${BASE}/public/v1/opportunities`, { headers:
            { Authorization: `Bearer ${KEY}` } });

            console.log(r.status, await r.json());
components:
  schemas:
    PublicOpportunitiesResponse:
      properties:
        metadata:
          $ref: '#/components/schemas/PublicFeedMeta'
          description: the feed's liveness
        opportunities:
          description: live opportunities, most profitable first
          items:
            $ref: '#/components/schemas/PublicOpportunity'
          title: Opportunities
          type: array
      required:
        - opportunities
        - metadata
      title: PublicOpportunitiesResponse
      type: object
    PublicError:
      properties:
        error:
          $ref: '#/components/schemas/PublicErrorBody'
          description: the refusal
      required:
        - error
      title: PublicError
      type: object
    PublicFeedMeta:
      properties:
        active_markets:
          description: markets the feed found active in its last pass
          title: Active Markets
          type: integer
        age_s:
          description: >-
            seconds since `last_update_secs`; the route answers 503 instead once
            this passes its bound
          title: Age S
          type: integer
        last_update_secs:
          description: when the opportunities feed last published, unix seconds
          title: Last Update Secs
          type: integer
        total_markets:
          description: markets the feed scanned in its last pass
          title: Total Markets
          type: integer
        total_publishes:
          description: how many times the feed has published
          title: Total Publishes
          type: integer
      required:
        - last_update_secs
        - age_s
        - total_markets
        - active_markets
        - total_publishes
      title: PublicFeedMeta
      type: object
    PublicOpportunity:
      properties:
        arb_type:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            which venue buys YES and which buys NO, as {yes venue}_yes_{no
            venue}_no, e.g. kalshi_yes_poly_no; venue tokens kalshi and poly
            (limitless not yet confirmed live)
          title: Arb Type
        category_group:
          anyOf:
            - type: string
            - type: 'null'
          description: the broad category group of the event
          title: Category Group
        detected_at_ms:
          anyOf:
            - type: integer
            - type: 'null'
          description: when the opportunity was detected, unix milliseconds
          title: Detected At Ms
        estimated_fee_cents:
          anyOf:
            - type: number
            - type: 'null'
          description: the venues' estimated trading fees, in whole cents per contract pair
          title: Estimated Fee Cents
        event_universal_id:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            the event this opportunity belongs to; pass it to
            /public/v1/events/{universal_id}
          title: Event Universal Id
        gas_fee_cents:
          anyOf:
            - type: number
            - type: 'null'
          description: the estimated on-chain gas cost, in whole cents per contract pair
          title: Gas Fee Cents
        image_url:
          anyOf:
            - type: string
            - type: 'null'
          description: an image for the event, when a venue publishes one
          title: Image Url
        kalshi_event_title:
          anyOf:
            - type: string
            - type: 'null'
          description: the Kalshi event title
          title: Kalshi Event Title
        kalshi_no_ask:
          anyOf:
            - type: number
            - type: 'null'
          description: Kalshi's best NO ask in cents
          title: Kalshi No Ask
        kalshi_no_size:
          anyOf:
            - type: number
            - type: 'null'
          description: contracts available at Kalshi's best NO ask
          title: Kalshi No Size
        kalshi_ticker:
          anyOf:
            - type: string
            - type: 'null'
          description: the Kalshi market ticker, when Kalshi is a leg
          title: Kalshi Ticker
        kalshi_title:
          anyOf:
            - type: string
            - type: 'null'
          description: the Kalshi market title
          title: Kalshi Title
        kalshi_url:
          anyOf:
            - type: string
            - type: 'null'
          description: the market's page on kalshi.com
          title: Kalshi Url
        kalshi_yes_ask:
          anyOf:
            - type: number
            - type: 'null'
          description: Kalshi's best YES ask in cents
          title: Kalshi Yes Ask
        kalshi_yes_size:
          anyOf:
            - type: number
            - type: 'null'
          description: contracts available at Kalshi's best YES ask
          title: Kalshi Yes Size
        limitless_no_ask:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            Limitless's best NO ask as the feed publishes it (unit not yet
            confirmed)
          title: Limitless No Ask
        limitless_no_size:
          anyOf:
            - type: number
            - type: 'null'
          description: size available at Limitless's best NO ask
          title: Limitless No Size
        limitless_slug:
          anyOf:
            - type: string
            - type: 'null'
          description: the Limitless market slug, when Limitless is a leg
          title: Limitless Slug
        limitless_title:
          anyOf:
            - type: string
            - type: 'null'
          description: the Limitless market title
          title: Limitless Title
        limitless_url:
          anyOf:
            - type: string
            - type: 'null'
          description: the market's page on limitless.exchange
          title: Limitless Url
        limitless_yes_ask:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            Limitless's best YES ask as the feed publishes it (unit not yet
            confirmed)
          title: Limitless Yes Ask
        limitless_yes_size:
          anyOf:
            - type: number
            - type: 'null'
          description: size available at Limitless's best YES ask
          title: Limitless Yes Size
        market_universal_id:
          anyOf:
            - type: string
            - type: 'null'
          description: the market this opportunity prices; pass it to the per-market routes
          title: Market Universal Id
        max_contracts:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            the contracts both legs can fill at these prices, shrunk to fresh
            book depth when a book is readable
          title: Max Contracts
        no_price:
          anyOf:
            - type: number
            - type: 'null'
          description: the NO leg's price in whole cents
          title: No Price
        poly_market_title:
          anyOf:
            - type: string
            - type: 'null'
          description: the Polymarket market title
          title: Poly Market Title
        poly_no_ask:
          anyOf:
            - type: number
            - type: 'null'
          description: Polymarket's best NO ask in cents, to one decimal place
          title: Poly No Ask
        poly_no_size:
          anyOf:
            - type: number
            - type: 'null'
          description: contracts available at Polymarket's best NO ask
          title: Poly No Size
        poly_slug:
          anyOf:
            - type: string
            - type: 'null'
          description: the Polymarket market slug, when Polymarket is a leg
          title: Poly Slug
        poly_title:
          anyOf:
            - type: string
            - type: 'null'
          description: the Polymarket event title
          title: Poly Title
        poly_url:
          anyOf:
            - type: string
            - type: 'null'
          description: the market's page on polymarket.com
          title: Poly Url
        poly_yes_ask:
          anyOf:
            - type: number
            - type: 'null'
          description: Polymarket's best YES ask in cents, to one decimal place
          title: Poly Yes Ask
        poly_yes_size:
          anyOf:
            - type: number
            - type: 'null'
          description: contracts available at Polymarket's best YES ask
          title: Poly Yes Size
        profit_cents:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            the expected profit per contract pair in whole cents: 100 minus
            total_cost_cents
          title: Profit Cents
        sport:
          anyOf:
            - type: string
            - type: 'null'
          description: the sport or category the opportunity is filed under
          title: Sport
        total_cost_cents:
          anyOf:
            - type: number
            - type: 'null'
          description: both legs plus the fees below, in whole cents per contract pair
          title: Total Cost Cents
        yes_price:
          anyOf:
            - type: number
            - type: 'null'
          description: the YES leg's price in whole cents
          title: Yes Price
      title: PublicOpportunity
      type: object
    PublicErrorBody:
      properties:
        code:
          description: >-
            a machine-readable code from x-known-codes; a new code may be added,
            so handle an unknown one by its status
          title: Code
          type: string
          x-known-codes:
            - access_not_granted
            - dependency_unavailable
            - insufficient_scope
            - internal_error
            - invalid_request
            - ip_not_allowed
            - market_closed
            - not_found
            - rate_limited
            - tier_required
            - unauthorized
            - upstream_unavailable
            - venue_not_listed
            - venue_unsupported
        message:
          description: a human-readable explanation; never parse it
          title: Message
          type: string
        request_id:
          description: >-
            this request's id, also sent as the X-Request-Id header; quote it
            when asking for help
          title: Request Id
          type: string
      required:
        - code
        - message
        - request_id
      title: PublicErrorBody
      type: object
  securitySchemes:
    HTTPBearer:
      description: 'Authorization: Bearer tl_live_…'
      scheme: bearer
      type: http

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.