openapi: 3.1.0
info:
  title: SiftingIO Market Data API
  version: "1.0.0"
  description: |
    REST data-plane for the SiftingIO market data API: live prices,
    SEC fundamentals (filings, XBRL financials, ratios, insiders, ownership),
    historical OHLCV bars, market calendars, DEX wallet portfolios, and the
    US economic calendar.

    Authentication is by API key, sent as the `X-API-Key` header (preferred)
    or the `api_key` query parameter.

    Six endpoints return large payloads and **require** `Accept-Encoding: gzip`
    (they respond `406` otherwise): the screener, both financials endpoints,
    and all three historical-bars endpoints.

    This document describes the data plane only. Account, billing, and auth
    (the `/ops/v1` control plane) are out of scope.
  license:
    name: MIT
    identifier: MIT
  contact:
    name: SiftingIO
    url: https://sifting.io/docs

servers:
  - url: https://api.sifting.io
    description: Production

security:
  - ApiKeyHeader: []
  - ApiKeyQuery: []

tags:
  - name: Live
    description: Live market data read from the engine's latest snapshot.
  - name: Stocks
    description: US-equity fundamentals (SEC) and historical bars.
  - name: Filers
    description: 13F institutional holdings.
  - name: Markets
    description: Market catalog, status, hours, and holiday calendars.
  - name: Forex
    description: Historical OHLC bars for FX pairs.
  - name: Crypto
    description: Historical OHLCV bars for crypto symbols.
  - name: DEX
    description: On-chain wallet portfolios.
  - name: EconomicCalendar
    description: US macro economic events.

paths:
  /v1/last/trade/{venue}/{symbol}:
    get:
      tags: [Live]
      operationId: getLastTrade
      summary: Latest trade
      parameters:
        - $ref: '#/components/parameters/Venue'
        - $ref: '#/components/parameters/Symbol'
      responses:
        '200':
          description: Latest trade snapshot.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LastTrade' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }

  /v1/last/quote/{venue}/{symbol}:
    get:
      tags: [Live]
      operationId: getLastQuote
      summary: Top-of-book quote
      parameters:
        - $ref: '#/components/parameters/Venue'
        - $ref: '#/components/parameters/Symbol'
      responses:
        '200':
          description: Top-of-book quote.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LastQuote' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }

  /v1/last/tvl/{chain}/{pair}:
    get:
      tags: [Live]
      operationId: getLastTVL
      summary: Aggregated DEX pool TVL
      parameters:
        - $ref: '#/components/parameters/Chain'
        - $ref: '#/components/parameters/Pair'
      responses:
        '200':
          description: Aggregated TVL for the chain/pair.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/LastTVL' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }

  /v1/fnd/stocks/search:
    get:
      tags: [Stocks]
      operationId: searchStocks
      summary: Ticker / company search
      parameters:
        - name: q
          in: query
          required: true
          description: Search string (ticker, company name, or CIK substring).
          schema: { type: string }
        - name: limit
          in: query
          description: Max results.
          schema: { type: integer, default: 25, maximum: 100 }
      responses:
        '200':
          description: Search results.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StockSearchResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/screener/{concept}/{period}:
    get:
      tags: [Stocks]
      operationId: getScreener
      summary: Cross-sectional screener (gzip required)
      description: "One XBRL concept/period across many companies. Requires `Accept-Encoding: gzip`."
      parameters:
        - $ref: '#/components/parameters/AcceptEncodingGzip'
        - name: concept
          in: path
          required: true
          description: XBRL concept tag, e.g. `Assets`, `NetIncomeLoss`.
          schema: { type: string }
        - name: period
          in: path
          required: true
          description: Reporting period, e.g. `CY2024Q1`, `CY2024FY`.
          schema: { type: string }
        - name: taxonomy
          in: query
          schema: { type: string, default: us-gaap }
        - name: unit
          in: query
          schema: { type: string, default: USD }
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Screener rows.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ScreenerResult' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '406': { $ref: '#/components/responses/GzipRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/profile:
    get:
      tags: [Stocks]
      operationId: getCompanyProfile
      summary: Company profile
      parameters:
        - $ref: '#/components/parameters/Ticker'
      responses:
        '200':
          description: Company profile.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CompanyProfile' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/filings:
    get:
      tags: [Stocks]
      operationId: listFilings
      summary: SEC filings
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - name: form
          in: query
          description: Comma-separated exact form types, e.g. `10-K,10-Q`.
          schema: { type: string }
        - name: from
          in: query
          description: Lower bound on filed date (YYYY-MM-DD).
          schema: { type: string, format: date }
        - name: to
          in: query
          description: Upper bound on filed date (YYYY-MM-DD).
          schema: { type: string, format: date }
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated filings.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FilingsResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/filings/{accession}:
    get:
      tags: [Stocks]
      operationId: getFiling
      summary: Filing detail
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Accession'
      responses:
        '200':
          description: Filing detail.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FilingDetail' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/filings/{accession}/sections:
    get:
      tags: [Stocks]
      operationId: getFilingSections
      summary: All filing sections
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Accession'
      responses:
        '200':
          description: Extracted sections.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FilingSections' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/filings/{accession}/sections/{section}:
    get:
      tags: [Stocks]
      operationId: getFilingSection
      summary: One filing section
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Accession'
        - name: section
          in: path
          required: true
          description: Section code, e.g. `business`, `risk-factors`, `mda`, `legal-proceedings`, `market-risk`.
          schema: { type: string }
      responses:
        '200':
          description: Section text.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FilingSectionDetail' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/risk-factors-diff:
    get:
      tags: [Stocks]
      operationId: getRiskFactorsDiff
      summary: Year-over-year risk-factor diff
      parameters:
        - $ref: '#/components/parameters/Ticker'
      responses:
        '200':
          description: Item 1A diff between the two most recent 10-Ks.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RiskFactorsDiff' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/ratios:
    get:
      tags: [Stocks]
      operationId: getRatios
      summary: Fundamental ratios
      parameters:
        - $ref: '#/components/parameters/Ticker'
      responses:
        '200':
          description: Latest ratios plus history.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Ratios' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/earnings:
    get:
      tags: [Stocks]
      operationId: listEarnings
      summary: Earnings releases
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated earnings-release filings.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/EventFilingsResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/financials:
    get:
      tags: [Stocks]
      operationId: getFinancials
      summary: Full XBRL financials (gzip required)
      description: "Full XBRL concept bundle for a company. Requires `Accept-Encoding: gzip`."
      parameters:
        - $ref: '#/components/parameters/AcceptEncodingGzip'
        - $ref: '#/components/parameters/Ticker'
      responses:
        '200':
          description: XBRL financials.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Financials' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '406': { $ref: '#/components/responses/GzipRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/financials/{concept}:
    get:
      tags: [Stocks]
      operationId: getFinancialConcept
      summary: One XBRL concept (gzip required)
      description: "One XBRL concept's full time series. Requires `Accept-Encoding: gzip`."
      parameters:
        - $ref: '#/components/parameters/AcceptEncodingGzip'
        - $ref: '#/components/parameters/Ticker'
        - name: concept
          in: path
          required: true
          description: XBRL concept tag, e.g. `Assets`.
          schema: { type: string }
        - name: taxonomy
          in: query
          schema: { type: string, default: us-gaap }
      responses:
        '200':
          description: Concept time series.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FinancialConcept' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '406': { $ref: '#/components/responses/GzipRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/insiders:
    get:
      tags: [Stocks]
      operationId: listInsiders
      summary: Insider transactions (Form 3/4/5)
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Cursor'
        - name: limit
          in: query
          description: Page size (lower cap due to XML parsing).
          schema: { type: integer, default: 10, maximum: 25 }
      responses:
        '200':
          description: Paginated insider transactions.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InsidersResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/ownership:
    get:
      tags: [Stocks]
      operationId: listOwnership
      summary: Schedule 13D/13G ownership filings
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated ownership filings.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OwnershipResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/events:
    get:
      tags: [Stocks]
      operationId: listEvents
      summary: 8-K material events
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - name: item
          in: query
          description: Filter by 8-K item code, e.g. `2.02`.
          schema: { type: string }
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated 8-K events.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/EventFilingsResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/stocks/{ticker}/compensation:
    get:
      tags: [Stocks]
      operationId: listCompensation
      summary: DEF 14A proxy / compensation filings
      parameters:
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated proxy filings.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CompensationResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/filers/{filer}/holdings:
    get:
      tags: [Filers]
      operationId: getFilerHoldings
      summary: 13F institutional holdings
      parameters:
        - name: filer
          in: path
          required: true
          description: Institutional filer CIK (numeric) or ticker.
          schema: { type: string }
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Latest 13F-HR positions.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Holdings' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/economic-calendar:
    get:
      tags: [EconomicCalendar]
      operationId: getEconomicCalendar
      summary: US economic calendar
      parameters:
        - name: from
          in: query
          description: Lower bound (YYYY-MM-DD or RFC3339). Default now.
          schema: { type: string }
        - name: to
          in: query
          description: Upper bound. Default from + 30 days.
          schema: { type: string }
        - name: country
          in: query
          schema: { type: string, default: US }
        - name: impact
          in: query
          schema: { type: string, enum: [low, medium, high] }
        - name: agency
          in: query
          schema: { type: string, enum: [BLS, BEA, Census, Fed, DOL, EIA] }
        - name: event_id
          in: query
          description: Filter to a recurring event, e.g. `us_cpi`.
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, default: 100, minimum: 1, maximum: 500 }
      responses:
        '200':
          description: Economic events.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/EconomicCalendarResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/markets:
    get:
      tags: [Markets]
      operationId: listMarkets
      summary: List markets
      parameters:
        - $ref: '#/components/parameters/Region'
      responses:
        '200':
          description: Market catalog.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MarketsListResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/markets/status:
    get:
      tags: [Markets]
      operationId: getAllMarketStatus
      summary: All market statuses
      parameters:
        - $ref: '#/components/parameters/Region'
      responses:
        '200':
          description: Open/closed snapshot for every market.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MarketsStatusAllResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/markets/{market}/status:
    get:
      tags: [Markets]
      operationId: getMarketStatus
      summary: One market status
      parameters:
        - $ref: '#/components/parameters/Market'
      responses:
        '200':
          description: Open/closed snapshot.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MarketStatusResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/markets/{market}/hours:
    get:
      tags: [Markets]
      operationId: getMarketHours
      summary: Market trading hours
      parameters:
        - $ref: '#/components/parameters/Market'
      responses:
        '200':
          description: Weekly schedule.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MarketHoursResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/markets/{market}/calendar:
    get:
      tags: [Markets]
      operationId: getMarketCalendar
      summary: Market holiday calendar
      parameters:
        - $ref: '#/components/parameters/Market'
        - name: from
          in: query
          schema: { type: string, format: date }
        - name: to
          in: query
          schema: { type: string, format: date }
      responses:
        '200':
          description: Holidays / half-days in range.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MarketCalendarResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/hist/stocks/{ticker}/bars:
    get:
      tags: [Stocks]
      operationId: getStockBars
      summary: Historical stock OHLCV bars (gzip required)
      description: "Requires `Accept-Encoding: gzip`."
      parameters:
        - $ref: '#/components/parameters/AcceptEncodingGzip'
        - $ref: '#/components/parameters/Ticker'
        - $ref: '#/components/parameters/BarStart'
        - $ref: '#/components/parameters/BarEnd'
        - $ref: '#/components/parameters/BarInterval'
        - $ref: '#/components/parameters/Cursor'
        - name: limit
          in: query
          schema: { type: integer, default: 1000, maximum: 2000 }
      responses:
        '200':
          description: OHLCV bars.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BarsResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '406': { $ref: '#/components/responses/GzipRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/hist/forex/{pair}/bars:
    get:
      tags: [Forex]
      operationId: getForexBars
      summary: Historical forex OHLC bars (gzip required)
      description: "Requires `Accept-Encoding: gzip`. Volume is always 0 for OTC spot forex."
      parameters:
        - $ref: '#/components/parameters/AcceptEncodingGzip'
        - $ref: '#/components/parameters/Pair'
        - $ref: '#/components/parameters/BarStartRequired'
        - $ref: '#/components/parameters/BarEnd'
        - $ref: '#/components/parameters/BarInterval'
        - $ref: '#/components/parameters/Cursor'
        - name: limit
          in: query
          schema: { type: integer, default: 1000, maximum: 2000 }
      responses:
        '200':
          description: OHLC bars.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BarsResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '406': { $ref: '#/components/responses/GzipRequired' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/hist/crypto/{symbol}/bars:
    get:
      tags: [Crypto]
      operationId: getCryptoBars
      summary: Historical crypto OHLCV bars (gzip required)
      description: |
        Requires `Accept-Encoding: gzip`. When `start` predates all upstreams'
        coverage the API returns `422` with code `data_unavailable` and an
        `earliest` field.
      parameters:
        - $ref: '#/components/parameters/AcceptEncodingGzip'
        - name: symbol
          in: path
          required: true
          description: USD-quoted crypto symbol, e.g. `BTCUSD`.
          schema: { type: string }
        - $ref: '#/components/parameters/BarStartRequired'
        - $ref: '#/components/parameters/BarEnd'
        - $ref: '#/components/parameters/BarInterval'
        - $ref: '#/components/parameters/Cursor'
        - name: limit
          in: query
          schema: { type: integer, default: 1000, maximum: 5000 }
      responses:
        '200':
          description: OHLCV bars.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BarsResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '406': { $ref: '#/components/responses/GzipRequired' }
        '422': { $ref: '#/components/responses/DataUnavailable' }
        '429': { $ref: '#/components/responses/TooManyRequests' }

  /v1/fnd/dex/wallet/{chain}/{address}:
    get:
      tags: [DEX]
      operationId: getWalletPortfolio
      summary: Wallet token portfolio
      parameters:
        - $ref: '#/components/parameters/Chain'
        - name: address
          in: path
          required: true
          description: 0x-prefixed 40-hex-char wallet address.
          schema: { type: string }
      responses:
        '200':
          description: Token holdings.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WalletPortfolio' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }

components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
    ApiKeyQuery:
      type: apiKey
      in: query
      name: api_key

  parameters:
    Venue:
      name: venue
      in: path
      required: true
      description: Market venue.
      schema: { type: string, enum: [stocks, crypto, forex, commodities, dex] }
    Symbol:
      name: symbol
      in: path
      required: true
      description: Symbol, normalized to the venue's canonical form.
      schema: { type: string }
    Chain:
      name: chain
      in: path
      required: true
      description: EVM chain.
      schema: { type: string, enum: [eth, base, arbitrum, bsc, polygon] }
    Pair:
      name: pair
      in: path
      required: true
      description: DEX pair (TOKEN0-TOKEN1) or FX pair (e.g. EURUSD).
      schema: { type: string }
    Ticker:
      name: ticker
      in: path
      required: true
      description: US equity ticker (case-insensitive).
      schema: { type: string }
    Accession:
      name: accession
      in: path
      required: true
      description: SEC accession number.
      schema: { type: string }
    Market:
      name: market
      in: path
      required: true
      description: Market slug, e.g. `us_equities`, `uk_equities`, `forex`, `crypto`.
      schema: { type: string }
    Region:
      name: region
      in: query
      description: Region filter.
      schema:
        type: string
        enum: [north_america, europe, asia_pacific, latam, global]
    Cursor:
      name: cursor
      in: query
      description: Opaque pagination cursor from a previous response's `meta.next_cursor`.
      schema: { type: string }
    Limit:
      name: limit
      in: query
      description: Page size. Default 50, max 200.
      schema: { type: integer, default: 50, maximum: 200 }
    BarStart:
      name: start
      in: query
      description: Inclusive lower bound (YYYY-MM-DD or RFC3339).
      schema: { type: string }
    BarStartRequired:
      name: start
      in: query
      required: true
      description: Inclusive lower bound (YYYY-MM-DD or RFC3339). Required.
      schema: { type: string }
    BarEnd:
      name: end
      in: query
      description: Inclusive upper bound. Default now.
      schema: { type: string }
    BarInterval:
      name: interval
      in: query
      description: Bar interval. Default 1m.
      schema: { type: string, enum: ["1m", "5m", "15m", "30m", "1h"], default: "1m" }
    AcceptEncodingGzip:
      name: Accept-Encoding
      in: header
      required: true
      description: Must include `gzip`; this endpoint returns 406 otherwise.
      schema: { type: string, enum: [gzip] }

  responses:
    BadRequest:
      description: Invalid request parameters.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    GzipRequired:
      description: "This endpoint requires `Accept-Encoding: gzip`."
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    DataUnavailable:
      description: No upstream has data for the requested window.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    TooManyRequests:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    ServiceUnavailable:
      description: Upstream or snapshot temporarily unavailable.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }

  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: string
          description: Machine-readable error code, e.g. `unknown_ticker`.
        message:
          type: string
        retry_after:
          type: number
          description: Present on rate-limit errors.
      additionalProperties: true

    ListMeta:
      type: object
      properties:
        next_cursor:
          type: string
          description: Cursor for the next page; absent on the last page.
        as_of:
          type: string
          format: date-time
        total:
          type: integer

    LastTrade:
      type: object
      properties:
        s: { type: string, description: Symbol. }
        p: { type: string, description: Last trade price. }
        P: { type: string, description: Last trade size. }
        t: { type: integer, format: int64, description: Unix epoch milliseconds. }

    LastQuote:
      type: object
      properties:
        s: { type: string, description: Symbol. }
        b: { type: string, description: Bid price. }
        B: { type: string, description: Bid size. }
        a: { type: string, description: Ask price. }
        A: { type: string, description: Ask size. }
        t: { type: integer, format: int64 }

    LastTVL:
      type: object
      properties:
        chain: { type: string }
        pair: { type: string }
        usd: { type: string }
        r0: { type: string, description: Reserve of token0. }
        r1: { type: string, description: Reserve of token1. }
        n: { type: integer, description: Pools aggregated. }
        v: { type: integer }
        t: { type: integer, format: int64 }

    StockSearchResult:
      type: object
      properties:
        ticker: { type: string }
        name: { type: string }
        cik: { type: string }
        exchange: { type: string }

    StockSearchResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/StockSearchResult' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    CompanyProfile:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        name: { type: string }
        exchanges: { type: array, items: { type: string } }
        other_tickers: { type: array, items: { type: string } }
        sic_code: { type: string }
        sic_description: { type: string }
        entity_type: { type: string }
        fiscal_year_end: { type: string, description: MMDD. }

    Filing:
      type: object
      properties:
        accession: { type: string }
        form: { type: string }
        filed_at: { type: string, format: date }
        period_end: { type: string, format: date }
        accepted_at: { type: string, format: date-time }
        items: { type: string, description: Comma-separated 8-K item codes. }
        primary_document_url: { type: string }
        description: { type: string }
        has_xbrl: { type: boolean }

    FilingDetail:
      allOf:
        - $ref: '#/components/schemas/Filing'
        - type: object
          properties:
            ticker: { type: string }
            cik: { type: string }
            archive_url: { type: string }
            files: { type: array, items: { type: string } }

    FilingsResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Filing' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    FilingSection:
      type: object
      properties:
        section: { type: string }
        content: { type: string }

    FilingSections:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        accession: { type: string }
        form: { type: string }
        filed_at: { type: string, format: date }
        sections:
          type: array
          items: { $ref: '#/components/schemas/FilingSection' }

    FilingSectionDetail:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        accession: { type: string }
        form: { type: string }
        filed_at: { type: string, format: date }
        section: { type: string }
        content: { type: string }

    FilingRef:
      type: object
      properties:
        accession: { type: string }
        form: { type: string }
        filed_at: { type: string, format: date }
        period_end: { type: string, format: date }

    DiffPair:
      type: object
      properties:
        before: { type: string }
        after: { type: string }

    SectionDiffStats:
      type: object
      properties:
        before_paragraphs: { type: integer }
        after_paragraphs: { type: integer }
        unchanged_count: { type: integer }
        added_count: { type: integer }
        removed_count: { type: integer }
        modified_count: { type: integer }

    SectionDiff:
      type: object
      properties:
        added: { type: array, items: { type: string } }
        removed: { type: array, items: { type: string } }
        modified:
          type: array
          items: { $ref: '#/components/schemas/DiffPair' }
        stats: { $ref: '#/components/schemas/SectionDiffStats' }

    RiskFactorsDiff:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        current: { $ref: '#/components/schemas/FilingRef' }
        previous: { $ref: '#/components/schemas/FilingRef' }
        diff: { $ref: '#/components/schemas/SectionDiff' }

    MetricValue:
      type: object
      properties:
        value: { type: number }
        unit: { type: string }
        period_start: { type: string, format: date }
        period_end: { type: string, format: date }
        fiscal_year: { type: integer }
        fiscal_period: { type: string }
        form: { type: string }
        accession: { type: string }
        filed_at: { type: string, format: date }

    ConceptBlock:
      type: object
      properties:
        taxonomy: { type: string }
        concept: { type: string }
        label: { type: string }
        description: { type: string }
        series:
          type: array
          items: { $ref: '#/components/schemas/MetricValue' }

    Financials:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        name: { type: string }
        concepts:
          type: array
          items: { $ref: '#/components/schemas/ConceptBlock' }

    FinancialConcept:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        taxonomy: { type: string }
        concept: { type: string }
        label: { type: string }
        description: { type: string }
        series:
          type: array
          items: { $ref: '#/components/schemas/MetricValue' }

    ScreenerRow:
      type: object
      properties:
        cik: { type: string }
        name: { type: string }
        value: { type: number }
        unit: { type: string }
        period_end: { type: string, format: date }
        accession: { type: string }

    ScreenerResult:
      type: object
      properties:
        taxonomy: { type: string }
        concept: { type: string }
        period: { type: string }
        unit: { type: string }
        label: { type: string }
        rows:
          type: array
          items: { $ref: '#/components/schemas/ScreenerRow' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    FinancialRatio:
      type: object
      properties:
        fiscal_year: { type: integer }
        fiscal_period: { type: string }
        period_end: { type: string, format: date }
        form: { type: string }
        accession: { type: string }
        gross_margin: { type: number }
        operating_margin: { type: number }
        net_margin: { type: number }
        return_on_equity: { type: number }
        return_on_assets: { type: number }
        debt_to_equity: { type: number }
        current_ratio: { type: number }
        quick_ratio: { type: number }
        asset_turnover: { type: number }
        free_cash_flow: { type: number }
        fcf_margin: { type: number }

    Ratios:
      type: object
      properties:
        ticker: { type: string }
        cik: { type: string }
        latest: { $ref: '#/components/schemas/FinancialRatio' }
        history:
          type: array
          items: { $ref: '#/components/schemas/FinancialRatio' }

    EventFiling:
      type: object
      properties:
        accession: { type: string }
        filed_at: { type: string, format: date }
        accepted_at: { type: string, format: date-time }
        items: { type: array, items: { type: string } }
        primary_document_url: { type: string }
        description: { type: string }

    EventFilingsResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/EventFiling' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    OwnershipFiling:
      type: object
      properties:
        form: { type: string }
        accession: { type: string }
        filed_at: { type: string, format: date }
        primary_document_url: { type: string }
        description: { type: string }

    OwnershipResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/OwnershipFiling' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    CompensationFiling:
      type: object
      properties:
        form: { type: string }
        accession: { type: string }
        filed_at: { type: string, format: date }
        period_end: { type: string, format: date }
        primary_document_url: { type: string }

    CompensationResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/CompensationFiling' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    InsiderTransaction:
      type: object
      properties:
        accession: { type: string }
        filed_at: { type: string, format: date }
        reporter: { type: string }
        reporter_cik: { type: string }
        roles: { type: array, items: { type: string } }
        officer_title: { type: string }
        security: { type: string }
        transaction_date: { type: string, format: date }
        transaction_code: { type: string }
        transaction_description: { type: string }
        direction: { type: string, description: "buy or sell" }
        shares: { type: number }
        price_per_share: { type: number }
        notional_usd: { type: number }
        shares_owned_after: { type: number }
        ownership: { type: string, description: "direct or indirect" }
        derivative: { type: boolean }

    InsidersResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/InsiderTransaction' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    HoldingPosition:
      type: object
      properties:
        issuer: { type: string }
        security_type: { type: string }
        cusip: { type: string }
        value_usd: { type: number }
        shares: { type: number }
        shares_type: { type: string }
        discretion: { type: string }

    Holdings:
      type: object
      properties:
        filer_cik: { type: string }
        filer_name: { type: string }
        accession: { type: string }
        filed_at: { type: string, format: date }
        period_end: { type: string, format: date }
        total_value_usd: { type: number }
        positions:
          type: array
          items: { $ref: '#/components/schemas/HoldingPosition' }
        meta: { $ref: '#/components/schemas/ListMeta' }

    EconomicEvent:
      type: object
      properties:
        event_id: { type: string }
        name: { type: string }
        country: { type: string }
        currency: { type: string }
        agency: { type: string }
        impact: { type: string }
        scheduled_at: { type: string, format: date-time }
        actual: { type: [number, "null"] }
        previous: { type: [number, "null"] }
        consensus: { type: [number, "null"] }
        released_at: { type: [string, "null"], format: date-time }

    EconomicCalendarResponse:
      type: object
      properties:
        events:
          type: array
          items: { $ref: '#/components/schemas/EconomicEvent' }
        count: { type: integer }
        filter:
          type: object
          additionalProperties: true

    MarketStats:
      type: object
      properties:
        market_cap_usd: { type: integer, format: int64 }
        listed_companies: { type: integer }
        currency: { type: string }
        as_of: { type: string, format: date }

    Market:
      type: object
      properties:
        market: { type: string }
        name: { type: string }
        type: { type: string, description: "exchange | forex | crypto" }
        timezone: { type: string }
        region: { type: string }
        exchanges: { type: array, items: { type: string } }
        stats: { $ref: '#/components/schemas/MarketStats' }

    MarketStatus:
      type: object
      properties:
        market: { type: string }
        type: { type: string }
        is_open: { type: boolean }
        state: { type: string, description: "closed | regular | open" }
        session: { type: string }
        next_open: { type: string, format: date-time }
        next_close: { type: string, format: date-time }
        timezone: { type: string }
        stats: { $ref: '#/components/schemas/MarketStats' }

    HoursBreak:
      type: object
      properties:
        open: { type: string, description: "HH:MM" }
        close: { type: string, description: "HH:MM" }

    HoursSpec:
      type: object
      properties:
        open: { type: string }
        close: { type: string }
        breaks:
          type: array
          items: { $ref: '#/components/schemas/HoursBreak' }

    ForexSession:
      type: object
      properties:
        name: { type: string }
        start: { type: string, description: "HH:MM UTC" }
        end: { type: string }

    MarketHoursBlock:
      type: object
      properties:
        regular: { $ref: '#/components/schemas/HoursSpec' }
        pre_market: { $ref: '#/components/schemas/HoursSpec' }
        post_market: { $ref: '#/components/schemas/HoursSpec' }

    MarketHours:
      type: object
      properties:
        market: { type: string }
        type: { type: string }
        timezone: { type: string }
        hours: { $ref: '#/components/schemas/MarketHoursBlock' }
        opens_at: { type: string }
        closes_at: { type: string }
        sessions:
          type: array
          items: { $ref: '#/components/schemas/ForexSession' }
        exchanges: { type: array, items: { type: string } }

    CalendarDay:
      type: object
      properties:
        date: { type: string, format: date }
        name: { type: string }
        kind: { type: string, description: "full_day_holiday | half_day_holiday" }
        state: { type: string }
        early_close: { type: string, format: date-time }

    MarketsListResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Market' }
        meta:
          type: object
          additionalProperties: true

    MarketsStatusAllResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/MarketStatus' }
        meta:
          type: object
          additionalProperties: true

    MarketStatusResponse:
      type: object
      properties:
        data: { $ref: '#/components/schemas/MarketStatus' }
        meta:
          type: object
          additionalProperties: true

    MarketHoursResponse:
      type: object
      properties:
        data: { $ref: '#/components/schemas/MarketHours' }
        meta:
          type: object
          additionalProperties: true

    MarketCalendarResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/CalendarDay' }
        meta:
          type: object
          additionalProperties: true

    Bar:
      type: object
      properties:
        t: { type: integer, format: int64, description: "Bar open time, Unix epoch ms." }
        o: { type: number }
        h: { type: number }
        l: { type: number }
        c: { type: number }
        v: { type: number }

    BarsMeta:
      type: object
      properties:
        as_of: { type: string, format: date-time }
        next_cursor: { type: string }
        symbol: { type: string }
        interval: { type: string }

    BarsResponse:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Bar' }
        meta: { $ref: '#/components/schemas/BarsMeta' }

    WalletToken:
      type: object
      properties:
        contract_address: { type: string }
        symbol: { type: string }
        name: { type: string }
        decimals: { type: integer }
        logo: { type: string }
        raw_balance: { type: string, description: Integer balance in base units. }
        balance: { type: string, description: Human-readable balance. }
        native: { type: boolean }

    WalletPortfolio:
      type: object
      properties:
        chain: { type: string }
        address: { type: string }
        tokens:
          type: array
          items: { $ref: '#/components/schemas/WalletToken' }
        count: { type: integer }
        updated_at: { type: integer, format: int64, description: Unix epoch seconds. }
