AlphaAI News API

Relevance-scored, ticker-linked news.

Operations 6

GET /api/news/ List news (cursor-paginated)
GET /api/news/trending/ Trending news (last 48h)
GET /api/news/insider/ Insider-transaction news (SEC Form 4)
GET /api/news/macro/ Market-wide macro news (central banks, prints, commodities, geopolitics)
GET /api/news/{uid}/ Get a news article by UID
GET /api/news/{uid}/related/ Articles related to one article

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/alphaai-news-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

alphaai-news-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: alphai REST News API
  version: 1.24.0
  description: 'Public REST API for alphai''s relevance-scored, ticker-linked financial news.

    All endpoints accept `Authorization: Bearer ak_live_…` (issued from your

    `/account/api-keys`). Authenticated traffic is metered per account with two

    layers — a per-minute burst cap and a per-day volume cap (Free 20/min +

    100/day, Basic 60/min + 10,000/day, Pro 150/min + 100,000/day); a request

    passes only if both are under budget. The Free tier is for evaluation /

    personal non-commercial use; Basic is licensed for internal commercial use;

    Pro adds redistribution of the enriched feed. Send all API traffic to

    `api.alphai.io`; a key is required on every request.


    Every keyed response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`

    and `X-RateLimit-Reset`; these report the per-day volume layer (Reset =

    epoch seconds of the next 00:00 UTC reset) so you can watch your daily budget

    without provoking a 429. A per-minute burst surfaces only as a 429 with a

    short `Retry-After`. A 429 body also names your tier, its caps and an

    `upgrade` block. The headers reflect origin processing, so a response

    served from a shared cache may omit them.


    News-archive depth is tiered: Free keys can page the feeds back 30 days,

    Basic 90 days, Pro 180 days. Paging past your horizon returns `403`

    with an `extra.reason` of `archive_horizon` (see the ArchiveGated response).

    Coverage note: our collectors expanded in June 2026, so archive months

    before 2026-06 hold fewer articles per day than the current feed.


    Insider data (SEC EDGAR Form 4) flows through the same news shape:

    `category=insider` rows in the feed, a dedicated `/api/news/insider/`

    route, and 30-day per-ticker rollups at

    `/api/symbols/{ticker}/insider-summary/`. For charts and analysis there

    is `/api/symbols/{ticker}/insider-trades/`: the complete per-ticker

    event history with 3m/12m/all-time rollups, weekly and monthly dollar

    buckets, and every event of the trailing 12 months in one response.


    Market-wide macro coverage (central-bank decisions, CPI/jobs prints,

    commodities, geopolitics) is in the feed too, under the `macro_economy`,

    `commodities` and `geopolitics` categories and a dedicated

    `/api/news/macro/` route. Macro rows are market-wide events, so most

    carry an empty `tickers` list — filter them by category, not by symbol.


    The forward half of the macro loop is `/api/calendar/`: the official

    schedule of upcoming US macro releases (FOMC decisions and minutes, CPI,

    PPI, jobs report, GDP estimates, PCE, retail sales, jobless claims,

    JOLTS), each with a stable occurrence `uid` that survives reschedules.

    Ask the calendar what''s coming, then read `/api/news/macro/` for what a

    release meant once it''s out.


    Query parameters are validated strictly. An unknown or misspelled

    parameter returns 400 naming the field, and the error body carries

    `extra.allowed_params` — every parameter that endpoint accepts — so you

    never have to guess. Common mix-ups also get a did-you-mean (`limit` and

    `per_page` point to `page_size`; `offset`, `page`, `skip` and the

    `after_id`/`before_id` family point to `cursor`; `ticker` points to

    `symbol`). Parameters that belong to the MCP server rather than to this

    API — free-text `q`/`query`/`search`, and `min_actionability` — say so

    instead of failing silently.


    See https://alphai.io/developers for the score and sentiment legend, and

    https://alphai.io/pricing for tier limits.

    '
  contact:
    name: alphai support
    email: support@alphai.io
    url: https://alphai.io/contact
  license:
    name: Proprietary
servers:
- url: https://api.alphai.io
  description: Production (API host — key required)
security:
- apiKey: []
tags:
- name: news
  description: Relevance-scored, ticker-linked news.
paths:
  /api/news/:
    get:
      tags:
      - news
      summary: List news (cursor-paginated)
      description: 'Feed of enriched articles, newest first. Default filter:

        `relevance_score >= 4` AND at least one active ticker. Pagination is

        cursor-based with a default page size of 10 (any size from 1 to 20 via

        `page_size=`; Pro keys go up to 50): omit `cursor` for the newest page,

        then pass the

        `next_cursor` from each response to fetch the next (older) page.

        `next_cursor: null` means the end of the feed. Cursors are opaque — do

        not construct or parse them; an invalid cursor returns 400. Archive

        depth is tiered: paging back past your plan''s horizon (Free 30 days,

        Basic 90, Pro 180) returns 403 with an upgrade hint.


        **Delta polling** (`sort=ingested`): the same feed ordered by the

        moment rows became available, for "what is new since my last poll".

        Articles reach the feed later than their publish time (collection

        median ~30 min for general news, with a long tail), so a poller that

        filters by `time_published` misses most late arrivals; `sort=ingested`

        never misses a row. First call without `cursor` returns the newest

        `page_size` rows and a cursor at the feed head; each later call with

        the previous `next_cursor` returns only rows added since. In this mode

        `next_cursor` is always non-null — empty `results` means you are

        caught up, keep the cursor and poll again later (responses are cached

        for 60 s, so polling more often than once a minute buys nothing). New

        rows only: an update to an already-delivered article is not re-sent.

        `time_published` is not monotonic within a delta page; sort client-side

        if you need chronological order. A cursor is only valid with the sort

        mode that issued it.


        Delta mode carries live coverage only: when we add history in bulk

        (backfilling an earlier period), those rows do NOT enter this stream —

        they would arrive as thousands of "new" items that are months old and

        push your cursor far behind the live feed. They are served normally in

        `sort=published`, by date filters and by `/api/news/{uid}/`, so a

        one-off catch-up over the archive is a published-mode query.


        **Keeping up.** One call returns at most `page_size` rows, so a poller

        holds its position at the head only while `calls per day × page_size`

        stays above the daily volume of the stream it asked for. Below that it

        falls a little further behind every day, and the symptom misleads:

        `time_published` reads hours or days old while the data is current.

        What went stale is the cursor, not the feed. So drain instead of

        polling once per tick: when a page comes back with `results` filled,

        call again immediately, and sleep only once `results` is empty. That

        clears a burst within the same cycle and lets a poller catch up by

        itself after downtime. Two levers if the call budget is still short,

        and they multiply: `page_size` (up to 20, or 50 on a Pro key) and a

        narrower stream (`min_relevance`, `symbol`, `category`). For scale, at

        the default `>= 4` floor the feed carries roughly 2,700 rows a day,

        while `min_relevance=7` carries roughly 1,000.


        A poller that never catches up eventually meets the archive gate: the

        403 below is keyed on the age of the first row you have not read yet,

        so it fires on a delta poller that never paged back at all.

        '
      parameters:
      - in: query
        name: cursor
        description: Opaque cursor from a prior response's `next_cursor`. Omit for the newest page.
        schema:
          type: string
      - in: query
        name: symbol
        description: Filter to articles that mention this ticker. US equities use the bare symbol (`NVDA`); cryptocurrencies use the `<SYM>-USD` form (`BTC-USD`), and a bare crypto name resolves to it (`DOGE` matches `DOGE-USD`); foreign listings use the Yahoo suffix (`VOD.L`). A delisted symbol returns its news history. A renamed company's current ticker also matches articles tagged with its former ticker, and any share class of an issuer matches articles tagged with its other listed classes (`GOOGL` includes rows tagged `GOOG`); article tags stay as published.
        schema:
          type: string
          example: NVDA
      - in: query
        name: category
        description: Keep only these categories. Accepts a single value, a CSV list (`category=earnings,insider`), or a repeated parameter — matches any of them (OR).
        style: form
        explode: true
        schema:
          type: array
          items:
            $ref: '#/components/schemas/NewsCategory'
      - in: query
        name: exclude_categories
        description: Drop these categories from the feed. Same single / CSV / repeated forms as `category`.
        style: form
        explode: true
        schema:
          type: array
          items:
            $ref: '#/components/schemas/NewsCategory'
      - in: query
        name: min_relevance
        description: Override the default ≥4 threshold.
        schema:
          type: integer
          minimum: 1
          maximum: 10
          default: 4
      - in: query
        name: page_size
        description: 'Items per page, default `10`. Any value from `1` to `20` is accepted on every tier; `21`-`50` requires a Pro key. A value outside `1`-`50`, or an over-`20` page without a Pro key, returns 400 — the page is never silently clamped. Pagination is otherwise unchanged: pass each response''s `next_cursor` back as `cursor` regardless of page size.'
        schema:
          type: integer
          minimum: 1
          maximum: 50
          default: 10
      - in: query
        name: collapse
        description: '`story` collapses coverage of one event to a single representative row (the story root): syndicated reprints and independently written articles about the same event, matched on headline and lead similarity. Populates the `story_id`, `sources_count` and `sources` fields on each item. Omit for the full feed (every article, story fields `null`). Only `story` is accepted; any other value returns 400.

          Note: row-level filters (`symbol`, `category`, `min_relevance`) apply to the story''s representative root, so a story whose root does not match the filter is omitted entirely.'
        schema:
          type: string
          enum:
          - story
      - in: query
        name: sort
        description: '`published` (default) is the reverse-chronological feed. `ingested` is delta-polling mode: rows in the order they became available, ascending, with `next_cursor` always returned (empty `results` = caught up) — see the endpoint description. All other filters apply unchanged in both modes. Cursors are mode-specific: a cursor is only valid with the sort mode that issued it (a mismatch returns 400).'
        schema:
          type: string
          enum:
          - published
          - ingested
          default: published
      responses:
        '200':
          description: A page of enriched articles.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewsPagination'
        '400':
          description: Invalid cursor (opaque; reuse `next_cursor`)
          a cursor replayed into the other `sort` mode: null
          unsupported `collapse`/`sort` value: null
          or a `page_size` outside 1-50 or not permitted on this tier.: null
        '403':
          $ref: '#/components/responses/ArchiveGated'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/news/trending/:
    get:
      tags:
      - news
      summary: Trending news (last 48h)
      description: 'Top stories from the trailing 48h: `relevance_score >= 8`, ranked by relevance score decayed by article age, so a fresh story outranks an equally-scored stale one. At most two stories per ticker. Fixed size of 10, no pagination. Coverage of one story, syndicated reprints and independent write-ups of the same event alike, is collapsed to a single representative, and each item carries the `story_id`, `sources_count` and `sources` fields for the story it represents. Most stories are carried by a single outlet, so `sources_count` is usually 1; see its field description.'
      responses:
        '200':
          description: List of enriched articles.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RichNewsArticle'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/news/insider/:
    get:
      tags:
      - news
      summary: Insider-transaction news (SEC Form 4)
      description: 'Dedicated insider feed (`category=insider`): SEC EDGAR Form 4 insider

        activity ONLY — one deterministic-templated row per filing event

        (a filing''s non-derivative trades of one type and holding form,

        aggregated: total shares, volume-weighted average price and executed

        range, total value), surfaced shortly after filing, with the relevance

        score computed from the event''s total (size, buy vs. sell, 10b5-1 plan

        or not). Same enriched shape, ticker

        and `relevance_score >= 4` filtering, and cursor-based pagination as

        `/api/news/`. Equivalent to `/api/news/?category=insider`, surfaced as a

        first-class route. For aggregate stats instead of the stream, see

        `/api/symbols/{ticker}/insider-summary/`.


        To watch for new filings, poll with `sort=ingested` rather than

        re-reading the newest page: a Form 4 is filed up to several days after

        the trade it reports, so a new event often enters the feed already

        below the head of the publish-ordered page. Delta mode orders by the

        moment the row became available, so it never misses one. The contract

        is identical to `/api/news/?sort=ingested` — see that endpoint for the

        full description.

        '
      parameters:
      - in: query
        name: cursor
        description: Opaque cursor from a prior response's `next_cursor`. Omit for the newest page.
        schema:
          type: string
      - in: query
        name: symbol
        description: Filter to insider news that mentions this ticker. Delisted symbols return their history; a renamed company's current ticker also matches rows tagged with its former ticker.
        schema:
          type: string
          example: NVDA
      - in: query
        name: min_relevance
        description: Override the default ≥4 threshold. Insider rows score deterministically from the event's summed dollar value (plus buy/10b5-1 modifiers), so this acts as an "only large trades" dial — e.g. `min_relevance=7` keeps roughly $10M+ events.
        schema:
          type: integer
          minimum: 1
          maximum: 10
          default: 4
      - in: query
        name: page_size
        description: Items per page, default `10`. Any value from `1` to `20` is accepted on every tier; `21`-`50` requires a Pro key. A value outside `1`-`50`, or an over-`20` page without a Pro key, returns 400.
        schema:
          type: integer
          minimum: 1
          maximum: 50
          default: 10
      - in: query
        name: sort
        description: '`published` (default) is the reverse-chronological feed. `ingested` is delta-polling mode, with the same contract as on `/api/news/`: rows in the order they became available, ascending, `next_cursor` always returned (empty `results` = caught up), and all other filters applied unchanged. Prefer it for watching insider activity — a Form 4 is filed days after the trade it reports, so a new event routinely lands below the newest page of the publish-ordered feed and a poller reading only the head misses it. Cursors are mode-specific: a cursor is only valid with the sort mode that issued it (a mismatch returns 400).'
        schema:
          type: string
          enum:
          - published
          - ingested
          default: published
      responses:
        '200':
          description: A page of enriched insider-transaction articles. Each item additionally carries the structured `insider` event block (side / shares / average price / total value / reporting owner) — populated on this endpoint only.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewsPagination'
        '400':
          description: Invalid cursor (opaque; reuse `next_cursor`)
          a cursor replayed into the other `sort` mode: null
          an unsupported `sort` value: null
          or a `page_size` outside 1-50 or not permitted on this tier.: null
        '403':
          $ref: '#/components/responses/ArchiveGated'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/news/macro/:
    get:
      tags:
      - news
      summary: Market-wide macro news (central banks, prints, commodities, geopolitics)
      description: 'Dedicated macro feed: the `macro_economy`, `commodities` and

        `geopolitics` categories in one stream — central-bank decisions and

        statements (FOMC), inflation/jobs prints, oil and gold, geopolitical

        risk. Same enriched shape, `relevance_score >= 4` floor and

        cursor-based pagination as `/api/news/`; equivalent to

        `/api/news/?category=macro_economy,commodities,geopolitics`, surfaced

        as a first-class route.


        Macro rows are market-wide, so most carry an empty `tickers` list —

        that is by design (an FOMC statement is not news about one company),

        and the per-article analysis block is still populated. A macro row

        that IS about listed companies (an oil-price story tagged to majors)

        keeps its tickers.


        To watch for new releases, poll with `sort=ingested` — same delta

        contract as `/api/news/?sort=ingested`.

        '
      parameters:
      - in: query
        name: cursor
        description: Opaque cursor from a prior response's `next_cursor`. Omit for the newest page.
        schema:
          type: string
      - in: query
        name: category
        description: Narrow to a subset of the macro categories, comma-separated. Values outside `macro_economy`/`commodities`/`geopolitics` return 400 (the full category filter lives on `/api/news/`).
        schema:
          type: string
          example: macro_economy
      - in: query
        name: min_relevance
        description: Override the default ≥4 threshold. On release days the release itself scores 7-9, so `min_relevance=7` keeps roughly "the events" and drops the commentary.
        schema:
          type: integer
          minimum: 1
          maximum: 10
          default: 4
      - in: query
        name: page_size
        description: Items per page, default `10`. Any value from `1` to `20` is accepted on every tier; `21`-`50` requires a Pro key. A value outside `1`-`50`, or an over-`20` page without a Pro key, returns 400.
        schema:
          type: integer
          minimum: 1
          maximum: 50
          default: 10
      - in: query
        name: sort
        description: '`published` (default) is the reverse-chronological feed. `ingested` is delta-polling mode, with the same contract as on `/api/news/`. Cursors are mode-specific: a cursor is only valid with the sort mode that issued it (a mismatch returns 400).'
        schema:
          type: string
          enum:
          - published
          - ingested
          default: published
      responses:
        '200':
          description: A page of enriched macro articles.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewsPagination'
        '400':
          description: Invalid cursor (opaque; reuse `next_cursor`)
          a cursor replayed into the other `sort` mode: null
          a category outside the macro set: null
          or a `page_size` outside 1-50 or not permitted on this tier.: null
        '403':
          $ref: '#/components/responses/ArchiveGated'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/news/{uid}/:
    get:
      tags:
      - news
      summary: Get a news article by UID
      parameters:
      - in: path
        name: uid
        required: true
        schema:
          type: string
          pattern: ^[a-f0-9]{16}$
      responses:
        '200':
          description: A single enriched article.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RichNewsArticle'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/news/{uid}/related/:
    get:
      tags:
      - news
      summary: Articles related to one article
      description: 'Up to 6 articles related to the given article: fresh articles sharing

        a ticker first, then same-category recents as backfill. Same enriched

        shape and `relevance_score >= 4` filtering as `/api/news/`; insider

        (SEC Form 4) rows are excluded unless the base article is itself

        insider. No pagination — the list is a fixed-size block.

        '
      parameters:
      - in: path
        name: uid
        required: true
        schema:
          type: string
          pattern: ^[a-f0-9]{16}$
      responses:
        '200':
          description: Related enriched articles (possibly fewer than 6).
          content:
            application/json:
              schema:
                type: object
                required:
                - results
                properties:
                  results:
                    type: array
                    maxItems: 6
                    items:
                      $ref: '#/components/schemas/RichNewsArticle'
        '404':
          $ref: '#/components/responses/NotFound'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    KeyEntity:
      type: object
      properties:
        name:
          type: string
        type:
          type: string
          example: company
        description:
          type: string
    RichNewsArticle:
      type: object
      required:
      - original
      - enrichment
      properties:
        original:
          $ref: '#/components/schemas/OriginalArticle'
        enrichment:
          $ref: '#/components/schemas/EnrichedArticle'
        story_id:
          type: string
          nullable: true
          description: Populated whenever the response is story-collapsed — `?collapse=story` on `/api/news/`, and always on `/api/news/trending/`. The `uid` of this story's representative article — equal to this item's own `original.uid`, and resolvable via `/api/news/{uid}/`. `null` in the default (uncollapsed) feed.
          example: 788e477c66f3849b
        sources_count:
          type: integer
          nullable: true
          description: Story-collapsed responses only. Number of distinct outlets (source domains) covering this story; the same outlet running it more than once counts once. Most stories are carried by a single outlet, so this is usually 1. Treat a value above 1 as the signal, not the number itself. May exceed the length of `sources`, which is capped at 10. `null` in the default feed.
          example: 3
        sources:
          type: array
          nullable: true
          items:
            type: string
          description: Story-collapsed responses only. Distinct source domains covering this story, in first-appearance order, capped at 10. `null` in the default feed.
          example:
          - reuters.com
          - apnews.com
          - bloomberg.com
        insider:
          nullable: true
          allOf:
          - $ref: '#/components/schemas/InsiderEvent'
          description: '`/api/news/insider/` only — the structured SEC Form 4 event block. `null` on every other endpoint and on rows without paired transaction data.'
    IndirectMarketEffects:
      type: object
      properties:
        sector_implications:
          type: string
        regional_market_impact:
          type: string
        global_market_relevance:
          type: string
    NewsPagination:
      type: object
      required:
      - results
      - next_cursor
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/RichNewsArticle'
        next_cursor:
          type: string
          nullable: true
          description: 'Opaque cursor for the next page. Pass it back as `?cursor=` with the same `sort` mode. Default (`sort=published`) feed: the next older page, `null` when the end of the feed has been reached. Delta mode (`sort=ingested`): always non-null — it is your polling position; empty `results` means caught up, keep the cursor and poll again later.'
    EnrichedArticle:
      type: object
      properties:
        category:
          $ref: '#/components/schemas/NewsCategory'
        tickers:
          type: array
          items:
            type: string
          description: Validated tickers the article mentions — only symbols present in `/api/symbols/` survive enrichment-time verification against the article text. Mirrors `ai_trading_insights.ticker_analysis[].ticker`.
          example:
          - NVDA
        relevance_score:
          type: integer
          minimum: 1
          maximum: 10
          description: 'How much trading value the article itself carries (rates the

            article, not the company; deterministic — same article, same

            score):

            1–2 no trading relevance · 3–4 derivative content about

            already-known events · 5–6 macro/sector datapoints, minor-but-real

            company news · 7–8 real company news with a fresh catalyst ·

            9–10 primary, material, newly disclosed.

            SEC Form 4 rows are scored from the transaction itself (size,

            buy vs. sell, 10b5-1 plan or not) rather than by the model.

            '
        ai_trading_insights:
          $ref: '#/components/schemas/AITradingInsights'
        news_context_enhancement:
          $ref: '#/components/schemas/NewsContextEnhancement'
    Actionability:
      type: string
      enum:
      - high
      - medium
      - low
      - negligible
    NewsTradingValue:
      type: object
      properties:
        actionability_score:
          $ref: '#/components/schemas/Actionability'
        information_novelty:
          type: integer
          minimum: 0
          maximum: 10
          description: 'How much NEW information the article carries (1–10), kept separate from relevance: a mega-cap post-earnings recap is high relevance but low novelty. 0 on rows enriched before the field existed.'
        timing_relevance:
          type: string
        market_sentiment_alignment:
          type: string
        estimated_read_time:
          type: string
    ImpactAnalysis:
      type: object
      properties:
        summary:
          type: string
        sentiment:
          $ref: '#/components/schemas/Sentiment'
        price_impact_prediction:
          type: string
        confidence:
          $ref: '#/components/schemas/Confidence'
        reasoning:
          type: string
    InsiderEvent:
      type: object
      description: 'Structured SEC Form 4 event: the aggregate of the news row''s whole transaction group (one row fronts a filing''s non-derivative trades of one type and holding form, so a 10b5-1 ladder is ONE event). `shares` and `total_value_usd` are group sums; `avg_price_usd` is the value-weighted average over priced tranches. Money and share fields are decimal STRINGS to preserve precision.'
      required:
      - side
      - transaction_code
      - shares
      - is_10b5_1
      - insider_name
      - insider_title
      - is_officer
      - is_director
      - is_ten_percent_owner
      - transaction_date
      - filed_at
      - late_filing
      properties:
        side:
          type: string
          enum:
          - buy
          - sell
          - other
          description: 'Signal label from the transaction code: `buy` (P, open-market purchase), `sell` (S, open-market sale), `other` for everything else — including D (sale to the issuer: a buyback/redemption, not an open-market disposition). Use `transaction_code` for your own mapping.'
        transaction_code:
          type: string
          description: Raw SEC Form 4 transaction code (`P`, `S`, `D`, …).
          example: S
        shares:
          type: string
          description: Total shares across the event's tranches (decimal string).
          example: '25000'
        avg_price_usd:
          type: string
          nullable: true
          description: Value-weighted average price per share over priced tranches. `null` when the filing prices no tranche.
          example: '187.32'
        total_value_usd:
          type: string
          nullable: true
          description: Total USD value across priced tranches (a lower bound when some tranches are unpriced). `null` when no tranche is priced.
          example: '4683000'
        is_10b5_1:
          type: boolean
          description: True when any tranche executed under a pre-arranged 10b5-1 plan.
        insider_name:
          type: string
          example: STEVENS MARK A
        insider_title:
          type: string
          example: Director
        is_officer:
          type: boolean
        is_director:
          type: boolean
        is_ten_percent_owner:
          type: boolean
        transaction_date:
          type: string
          format: date
          description: Date of the group's last fill (a ladder can span days).
          example: '2026-07-09'
        filed_at:
          type: string
          format: date-time
          description: When EDGAR accepted the filing (UTC). Compare against `transaction_date` for your own lateness rule.
          example: '2026-07-11T20:31:04Z'
        late_filing:
          type: boolean
          description: 'The filing missed the SEC''s two-business-day deadline (Rule 16a-3(g)). Computed on Eastern dates, since EDGAR accepts filings until ~22:00 ET, and with one weekday of slack so a trade in a holiday week is not flagged: true when more than three weekday-days separate `transaction_date` from the filing. About 3% of events carry it; the long tail is catch-up filings covering trades from years earlier.'
    TickerAnalysis:
      type: object
      properties:
        ticker:
          type: string
        relevance_context:
          type: string
        impact_analysis:
          $ref: '#/components/schemas/ImpactAnalysis'
    Confidence:
      type: string
      enum:
      - high
      - medium
      - low
    NewsCategory:
      type: string
      description: '`market_movers` is for articles whose subject IS a notable price move ("AMD up 5% today"); `sector_analysis` is genuine sector-level analysis; `insider` covers SEC Form 4 insider transactions only. SEC 8-K filings categorize by their primary item: an earnings release (Item 2.02) is `earnings`, a completed acquisition or disposition (Item 2.01) is `mergers_acquisitions`, and the remaining events (material agreements, debt, executive changes, annual-meeting results) are `corporate_actions`.'
      enum:
      - earnings
      - mergers_acquisitions
      - regulation
      - macro_economy
      - sector_analysis
      - market_movers
      - technology
      - commodities
      - crypto
      - ipo
      - geopolitics
      - insider
      - corporate_actions
      - other
    Sentiment:
      type: string
      enum:
      - positive
      - neutral
      - negative
    Error:
      type: object
      properties:
        message:

# --- truncated at 32 KB (38 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/alphaai/refs/heads/main/openapi/alphaai-news-api-openapi.yml