Every API here is available over the APIs.io API and to AI agents over MCP.
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