AllVista API

Financial intelligence platform API โ€” Trump Truth Social, X/Twitter financial news, macro economics, US equities, options & institutional flow, earnings, China A-shares / Stock Connect and the Daily Brief. 42 endpoints in 12 groups.

Base URL: https://news.all-vista.org Spec: /api/openapi.json Version 2026.09.07
๐Ÿ”‘ Authentication

Every endpoint except this page and /api/openapi.json requires an API key, sent in one of two headers:

HeaderExample
X-API-KeyX-API-Key: avk_your_key_here
AuthorizationAuthorization: Bearer avk_your_key_here

Example

export KEY=avk_your_key_here
curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/posts?limit=5"
curl -H "Authorization: Bearer $KEY" "https://news.all-vista.org/api/posts?limit=5"
Requests without a valid key receive 401 Unauthorized with a JSON body {"error":"Unauthorized","hint":โ€ฆ,"docs":"/api/docs"}.
โฑ Rate Limits

Two classes of key exist. A server key (avs_โ€ฆ, issued for back-office integrations) is not rate limited. The public key (avk_โ€ฆ, the one the website itself uses) is limited per client IP over a rolling 5-minute window:

ScopeLimit / 5 minApplies to
api1500 per IPEvery endpoint that is not LLM-backed
llm60 per IP, 600 across all IPsPOST /api/analyze, POST /api/x-analyze, POST /api/x-analyze-batch, POST /api/x-digest, POST /api/bwe-analyze, POST /api/bwe-digest, GET /api/macro-analysis, POST /api/stock-analysis, POST /api/flow/recap
Exceeding a limit returns 429 with a Retry-After header and {"error":"Too many requests","retry_after_seconds":N}. Cached hits count too, so batch reads where you can.
๐Ÿงฉ Machine-readable Spec

The same reference is published as OpenAPI 3.1 at /api/openapi.json (no key required). Point Swagger UI, Redoc, Postman, an SDK generator or an LLM agent at it:

curl -s https://news.all-vista.org/api/openapi.json | jq '.paths | keys'
npx @redocly/cli preview-docs https://news.all-vista.org/api/openapi.json
Version 2026.09.07. LLM-backed operations are tagged x-llm: true; routes that set a cache header carry x-cache.
๐Ÿ“‹ Trump Truth Social

Truth Social posts with AI market-impact analysis, aggregate impact stats and intraday price reactions.

GET

/api/posts

Paginated feed of Trump's Truth Social posts with search & AI analysis

Newest first. Each post carries _analysis (the cached AnalysisResult from POST /api/analyze, or null). Media-only posts, posts that are only a URL, and posts the analysis flagged as empty are hidden from the feed.

Query Parameters

NameTypeDescription
pageintegeroptionaldefault 11โ€“โ€ฆPage number (1-based); values below 1 are clamped to 1, and 0 or a non-numeric value falls back to 1.
limitintegeroptionaldefault 201โ€“100Posts per page, clamped to 1โ€“100; 0 or a non-numeric value falls back to 20.
qstringoptionalCase-insensitive substring search over the post text and the linked card's title and description.
fromstringoptionalDate range start, YYYY-MM-DD (inclusive, from 00:00 UTC).
tostringoptionalDate range end, YYYY-MM-DD (inclusive, through 23:59:59.999 UTC).

Response โ€” PostPage

{
  "posts": [
    {
      "id": "114090816417968547",
      "created_at": "2025-02-28T14:23:00.000Z",
      "content": "<p>post HTML content</p>",
      "language": "en",
      "url": "https://truthsocial.com/@realDonaldTrump/...",
      "account": { "username": "realDonaldTrump", "followers_count": 9200000, "verified": true, ... },
      "media_attachments": [ { "id": "...", "type": "image", "url": "/media/....png", "preview_url": "...", ... } ],
      "card": { "url": "...", "title": "...", "description": "...", "image": "...", "provider_name": "..." },  // or null
      "replies_count": 1200,
      "reblogs_count": 2100,
      "favourites_count": 8500,
      "_analysis": { ... }  // AnalysisResult if available, else null
    }
  ],
  "total": 842,
  "page": 1,
  "limit": 20,
  "hasMore": true
}
page is clamped to at least 1 and limit to 1โ€“100 (the response echoes the effective values); the page window is (page - 1) * limit to page * limit over the filtered list.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/posts?q=tariff&from=2025-03-01&limit=5"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/posts?page=2&limit=50"
POST

/api/analyze

LLM AI analysis of a Trump post's market impact

Returns the cached AnalysisResult when one exists (with X-AI-Provider: cache); otherwise runs the LLM on the post text, its linked article excerpt and attached images, stores the result, and returns it with X-AI-Provider / X-AI-Model headers.

Request Body (JSON)

NameTypeDescription
post_idstringrequiredTruth Social post ID.

Response โ€” AnalysisResult

{
  "topic": "tariff",             // macro|energy|tariff|social_media|legal|election|other
  "impact_direction": "bearish", // bullish|bearish|neutral|uncertain
  "impact_magnitude": 2,         // 0-3
  "confidence": 0.85,            // 0.0-1.0
  "suggested_symbols": ["SPY", "QQQ", "EEM"],
  "headline": "...",             // Chinese headline
  "headline_en": "...",          // English headline
  "translation": "...",          // Chinese translation of post
  "rationale": "...",            // Chinese summary
  "rationale_en": "...",         // English summary
  "key_points": ["..."],         // Chinese key points
  "key_points_en": ["..."],      // English key points
  "market_impact": "...",        // Chinese detailed analysis
  "market_impact_en": "...",     // English detailed analysis
  "risk_factors": ["..."],       // Chinese risk factors
  "risk_factors_en": ["..."],    // English risk factors
  "time_horizon": "short_term"   // short_term|medium_term|long_term|unclear
}

Other Status Codes

CodeMeaning
202Analysis already in progress for this post โ€” retry later
400Missing post_id
404Post not found
500Analysis failed (all LLM providers errored)
This endpoint triggers an LLM call (typically ~60 s; the route allows up to 300 s). Returns 202 if analysis is already in progress for this post.
Cached results are served with X-AI-Provider: cache and X-AI-Model: cached; fresh results carry the provider and model that produced them.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/analyze -H "Content-Type: application/json" -d '{"post_id": "114090816417968547"}'
GET

/api/trump-impact

Market reaction to Trump posts โ€” per-post session moves or a rolling summary

Exactly one of ids or summary is required; ids takes precedence when both are present. Each post maps to the first US session it could move (posts at or after 16:00 ET, or on a weekend / holiday, roll to the next session) and the reaction is that session's close-to-close % move for SPY, QQQ, TLT, USO, GLD and IBIT.

Query Parameters

NameTypeDescription
idsstring[]optional1โ€“60 comma-separated numeric Truth Social post ids (duplicates removed) โ†’ { reactions: { [id]: PostReaction } }.
summaryintegeroptionaldefault 301โ€“365Look-back window in days โ†’ ImpactSummary. An empty value (?summary=) means 30.

Response โ€” PostReaction / ImpactSummary

// ?ids=114090816417968547
{
  "reactions": {
    "114090816417968547": {
      "session": "2025-02-28",   // US session the post maps to; null while pending / no_data
      "status": "done",          // done | pending | no_data
      "moves": { "SPY": -1.59, "QQQ": -2.11, "TLT": 0.42, "USO": -0.8, "GLD": 0.31, "IBIT": -3.2 }
    }
  }
}

// ?summary=30
{
  "days": 30,
  "posts": 212, "postsDone": 204, "postsPending": 8,
  "postsWithSpyMoveGe1pct": 61,
  "avgAbsSpyOnPostingDays": 0.84,   // mean |SPY %| over sessions with >= 1 post (null when none)
  "avgAbsSpyAllDays": 0.71,         // mean |SPY %| over every completed session in the window
  "sessions": 21, "postingSessions": 19,
  "latestSession": "2026-08-14",
  "topPosts": [
    { "id": "...", "created_at": "...", "headline": "...", "headline_en": "...", "url": "...",
      "session": "2026-08-14", "spyPct": -2.31, "sessionPosts": 4 }
  ],
  "generatedAt": "2026-08-15T12:00:00.000Z"
}

Other Status Codes

CodeMeaning
400Neither ids nor summary given; ids not 1โ€“60 numeric ids; or summary not an integer 1โ€“365
500Internal error
Close-to-close moves of the session a post landed in. This is correlation, not attribution โ€” several posts usually share one session.
pending means the session has not closed or its bars have not arrived yet (files land about 00:10 ET the next day); such rows are re-evaluated after 10 minutes. Unknown ids come back as no_data. The summary is memoised for 10 minutes per days value and lists at most 5 topPosts (one representative post per session).

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/trump-impact?summary=30"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/trump-impact?ids=114090816417968547,114090900000000000"
GET

/api/trump-intraday

15- and 60-minute market moves after each Trump post (SPY, QQQ, mentioned tickers)

Exactly one of ids or summary is required; ids takes precedence. Posts inside 09:30โ€“15:45 ET on a session are measured from the last 1-minute bar before the post (mode: rth); anything else is measured from the next session's 09:30 open (mode: session_open).

Query Parameters

NameTypeDescription
idsstring[]optional1โ€“60 comma-separated numeric post ids (duplicates removed) โ†’ { reactions: { [id]: IntradayReaction } }.
summarystringoptionalAny value (conventionally 1) returns the period summary instead of per-post rows.

Response โ€” IntradayReaction / IntradaySummaryResponse

// ?ids=115123456789012345
{
  "reactions": {
    "115123456789012345": {
      "status": "done",                        // done | pending | no_data
      "mode": "rth",                           // rth | session_open (null while pending / no_data)
      "session": "2026-09-01",
      "anchor_ts": "2026-09-01T14:42:00Z",     // last 1-minute bar before the post (UTC)
      "truncated": false,                      // session closed before the +60 mark; its last bar was used
      "moves": { "SPY": { "m15": 0.21, "m60": -0.05 }, "QQQ": { ... }, "TSLA": { ... } },
      "spy15_pct_rank": 62                     // |SPY 15m| percentile vs every 15-minute SPY window of the period
    }
  }
}

// ?summary=1
{
  "available": true,                           // false (other fields null / 0) until the AWS job has delivered a file
  "generated_at": "2026-09-02T13:57:00+00:00",
  "days": 90,
  "window": { "start": "2026-06-04", "end": "2026-09-01" },
  "last_session": "2026-09-01",
  "baseline": { "symbol": "SPY", "sessions": 62, "n15": 24180, "n60": 22940,
                "abs15": { "mean": 0.080, "p50": 0.05, "p75": 0.09, "p90": 0.16, "p95": 0.22, "p99": 0.41 },
                "abs60": { ... } },
  "summary": {
    "posts": 812, "done": 790, "pending": 12, "no_data": 10,
    "rth": 310, "session_open": 480, "truncated": 6,
    "big_move_pct": 0.5, "spy15_big": 14, "spy15_big_share": 0.018,
    "rth_mean_abs_spy15": 0.074, "rth_mean_abs_spy60": 0.15, "session_open_mean_abs_spy15": 0.12,
    "baseline_mean_abs15": 0.080, "baseline_mean_abs60": 0.17,
    "top": [ { "id": "...", "created_at": "...", "mode": "rth", "session": "...", "anchor_ts": "...",
               "spy15": -0.62, "spy60": -0.9, "spy15_pct_rank": 99, "truncated": false, "anchor_posts": 1,
               "moves": { ... }, "headline": "...", "headline_en": "...", "url": "..." } ]
  }
}

Other Status Codes

CodeMeaning
400Neither ids nor summary given, or ids not 1โ€“60 numeric ids
500Internal error
Pre-computed on the AWS market database from 1-minute bars (trump_intraday.json, twice daily). Posts outside regular hours are measured from the next session open and flagged session_open. available: false until the job has delivered a file.
Ids the job has not seen yet are pending when the post is newer than the snapshot it consumed, no_data otherwise. moves holds SPY, QQQ and up to three of the post's own symbols.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/trump-intraday?summary=1"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/trump-intraday?ids=115123456789012345"
๐• X Financial News

Curated X/Twitter financial posts, per-post analysis and rolling digests.

GET

/api/x-posts

Paginated feed of curated financial news from X/Twitter

Newest first. Each post carries _analysis (the cached XAnalysisResult from POST /api/x-analyze or POST /api/x-analyze-batch, or null).

Query Parameters

NameTypeDescription
pageintegeroptionaldefault 1Page number (1-based).
limitintegeroptionaldefault 50Posts per page.
beforestringoptionalCursor: only posts strictly older than this ISO 8601 timestamp (page then counts from there). Invalid โ†’ 400. Added 2026-10-20; the feed pages use it instead of offsets.
authorstringoptionalFilter by author handle, exact match (e.g. "WSJ", "zerohedge").

Response โ€” XPostPage

{
  "posts": [
    {
      "id": "x_2032098083790561289",
      "timestamp": "2026-03-17T08:32:00.000Z",
      "raw_text": "BREAKING: Fed holds rates steady...",
      "author": "Reuters",
      "_analysis": { "direction": "neutral", "translation": "...", "one_liner": "...", "one_liner_en": "..." }  // or null
    }
  ],
  "total": 915,
  "page": 1,
  "limit": 50,
  "hasMore": true
}

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/x-posts?limit=10"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/x-posts?author=Reuters&page=1&limit=20"
POST

/api/x-analyze

LLM AI sentiment analysis of an X financial news post

Returns the cached result when one exists (with X-AI-Provider: cache); otherwise classifies the headline with the LLM, stores it, and returns it with X-AI-Provider / X-AI-Model headers.

Request Body (JSON)

NameTypeDescription
message_idstringrequiredX post ID as returned by /api/x-posts.

Response โ€” XAnalysisResult

{
  "direction": "bearish",     // bullish|bearish|neutral|uncertain
  "translation": "...",       // Chinese translation
  "one_liner": "...",         // Chinese market commentary
  "one_liner_en": "..."       // English market commentary
}

Other Status Codes

CodeMeaning
202Analysis already in progress for this post โ€” retry later
400Missing message_id
404Message not found
500Analysis failed (all LLM providers errored)
Triggers an LLM call (~60 s). Returns 202 if in progress.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/x-analyze -H "Content-Type: application/json" -d '{"message_id": "x_2032098083790561289"}'
POST

/api/x-analyze-batch

LLM Classify up to 25 X headlines in one LLM call

Fills the same cache as POST /api/x-analyze for many posts at once. Ids that are already cached or unknown are dropped before the call; only the remaining headlines are sent, in a single prompt. The body reports counts only โ€” read the results back through /api/x-posts (_analysis).

Request Body (JSON)

NameTypeDescription
idsstring[]required1โ€“25 X post ids (non-empty strings of at most 64 characters; duplicates removed).

Response โ€” BatchAnalysisReport

{
  "requested": 20,          // distinct ids in the request
  "analyzed": 17,           // items the model returned well-formed; written to the cache
  "skipped_cached": 2,      // ids that already had an analysis (not sent)
  "skipped_unknown": 0,     // ids not present in the X archive (not sent)
  "failed": 1,              // sent ids the model skipped or returned malformed
  "provider": "anthropic",  // null when nothing was sent (no LLM call)
  "model": "claude-sonnet-4-6"
}

Other Status Codes

CodeMeaning
202The same set of uncached ids is already being analysed โ€” retry later
400Body is not JSON, or ids is not an array of 1โ€“25 non-empty strings (each โ‰ค 64 chars)
500Batch analysis failed (the LLM call errored; nothing was cached)
One LLM call per request, with a 240 s timeout (route maxDuration is 300 s). Use a client timeout of about 290 s.
When every id is cached or unknown the route answers 200 with analyzed: 0 and provider: null without calling the LLM. Otherwise X-AI-Provider / X-AI-Model headers name the provider that ran. Headlines are truncated to 600 characters in the prompt.
Driven hourly by scripts/analyze_trending.py, which sends the last 24 h of posts for the trending tickers in chunks of 20 (route limit is 25) and counts a 500 as a spent LLM call.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/x-analyze-batch -H "Content-Type: application/json" -d '{"ids": ["x_2032098083790561289", "x_2032098083790561290"]}'
POST

/api/x-digest

LLM AI-generated summary of X news over a time window

Cached per {window}_{start}: a repeat call with the same window and the identical start string returns the stored digest (with X-AI-Provider: cache). Posts with start <= timestamp < end are summarised.

Request Body (JSON)

NameTypeDescription
windowstringrequiredTime window label; becomes the prefix of the cache key.
4h12h1d1w
startstringrequiredWindow start, ISO 8601 (inclusive). Use the exact form YYYY-MM-DDTHH:MM:SS.000Z so the key matches GET /api/digests.
endstringrequiredWindow end, ISO 8601 (exclusive).

Response โ€” DigestResult

{
  "sentiment": "bearish",     // bullish|bearish|neutral|mixed
  "title": "...",             // Chinese one-line title
  "title_en": "...",          // English one-line title
  "summary": "...",           // Chinese 2-3 paragraph analysis
  "summary_en": "...",        // English 2-3 paragraph analysis
  "key_themes": ["tariffs", "fed policy", "tech earnings"],
  "notable_tickers": ["NVDA", "TSLA", "SPY"]
}

Other Status Codes

CodeMeaning
202Digest for this window/start is already being generated โ€” retry later
400Missing window, start or end; window not one of 4h, 12h, 1d, 1w (window must be one of 4h, 12h, 1d, 1w); or start / end not parseable as ISO-8601 timestamps
404No posts in range
500Digest failed (all LLM providers errored)
Triggers an LLM call (~120 s; the route allows up to 300 s). Returns 202 if in progress. Max 300 headlines per digest (the newest 300 in the window).
window must be one of the four listed values and start / end must parse as timestamps (Date.parse); all three are checked before the cache lookup. The cache key still uses the raw start string, so send the canonical YYYY-MM-DDTHH:MM:SS.000Z form to hit the same digest as GET /api/digests.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/x-digest -H "Content-Type: application/json" -d '{"window": "1d", "start": "2026-04-01T00:00:00.000Z", "end": "2026-04-02T00:00:00.000Z"}'
โ›“ BWE Crypto News

BWEnews crypto feed with analysis and digests.

GET

/api/bwe-posts

Paginated feed of BWE crypto/blockchain news with AI analysis

Newest first. source and headline are parsed from raw_text (format **SOURCE: headline**). Each post carries _analysis (the cached BWEAnalysisResult from POST /api/bwe-analyze, or null).

Query Parameters

NameTypeDescription
pageintegeroptionaldefault 1Page number (1-based).
limitintegeroptionaldefault 50Posts per page.
beforestringoptionalCursor: only posts strictly older than this ISO 8601 timestamp (page then counts from there). Invalid โ†’ 400. Added 2026-10-20; the feed pages use it instead of offsets.
sourcestringoptionalFilter by source, exact match (e.g. "DB", "AggrNews", "Binance EN").

Response โ€” BWEPostPage

{
  "posts": [
    {
      "id": "bwe_20260401_001",
      "timestamp": "2026-04-01T08:00:00.000Z",
      "raw_text": "**Binance EN: BTC breaks $90K...**\n...",
      "source": "Binance EN",
      "headline": "BTC breaks $90K resistance",
      "_analysis": {
        "direction": "bullish",
        "translation": "...",
        "one_liner": "...",
        "one_liner_en": "..."
      }
    }
  ],
  "total": 61,
  "page": 1,
  "limit": 50,
  "hasMore": true
}

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/bwe-posts?limit=10"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/bwe-posts?source=Binance%20EN&limit=20"
POST

/api/bwe-analyze

LLM AI sentiment analysis of a BWE crypto/blockchain headline

Returns the cached result when one exists (with X-AI-Provider: cache); otherwise classifies the parsed headline with the LLM, stores it, and returns it with X-AI-Provider / X-AI-Model headers.

Request Body (JSON)

NameTypeDescription
message_idstringrequiredBWE post ID as returned by /api/bwe-posts.

Response โ€” BWEAnalysisResult

{
  "direction": "bullish",     // bullish|bearish|neutral|uncertain
  "translation": "...",       // Chinese translation
  "one_liner": "...",         // Chinese market commentary
  "one_liner_en": "..."       // English market commentary
}

Other Status Codes

CodeMeaning
202Analysis already in progress for this post โ€” retry later
400Missing message_id
404Message not found
500Analysis failed (all LLM providers errored)
Triggers an LLM call (~60 s). Returns 202 if in progress.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/bwe-analyze -H "Content-Type: application/json" -d '{"message_id": "bwe_20260312_001"}'
POST

/api/bwe-digest

LLM AI-generated summary of BWE crypto news over a time window

Cached per {window}_{start}: a repeat call with the same window and the identical start string returns the stored digest (with X-AI-Provider: cache). Every post with start <= timestamp < end is sent to the model (no headline cap).

Request Body (JSON)

NameTypeDescription
windowstringrequiredTime window label; becomes the prefix of the cache key.
4h12h1d1w
startstringrequiredWindow start, ISO 8601 (inclusive). Use the exact form YYYY-MM-DDTHH:MM:SS.000Z so the key matches GET /api/digests.
endstringrequiredWindow end, ISO 8601 (exclusive).

Response โ€” DigestResult

{
  "sentiment": "mixed",       // bullish|bearish|neutral|mixed
  "title": "...",             // Chinese one-line title
  "title_en": "...",          // English one-line title
  "summary": "...",           // Chinese 2-3 paragraph analysis
  "summary_en": "...",        // English 2-3 paragraph analysis
  "key_themes": ["BTC ETF", "DeFi regulation"],
  "notable_tickers": ["BTC", "ETH", "SOL"]
}

Other Status Codes

CodeMeaning
202Digest for this window/start is already being generated โ€” retry later
400Missing window, start or end; window not one of 4h, 12h, 1d, 1w (window must be one of 4h, 12h, 1d, 1w); or start / end not parseable as ISO-8601 timestamps
404No posts in range
500Digest failed (all LLM providers errored)
Triggers an LLM call (~120 s; the route allows up to 300 s). Returns 202 if in progress.
window must be one of the four listed values and start / end must parse as timestamps (Date.parse); all three are checked before the cache lookup. The cache key still uses the raw start string, so send the canonical YYYY-MM-DDTHH:MM:SS.000Z form to hit the same digest as GET /api/digests.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/bwe-digest -H "Content-Type: application/json" -d '{"window": "1d", "start": "2026-04-01T00:00:00.000Z", "end": "2026-04-02T00:00:00.000Z"}'
๐Ÿ“Š Digests

Read-only access to the hourly X and BWE digests.

GET

/api/digests

Read cached AI digests โ€” no LLM call, returns pre-computed summaries

Lists every digest stored for a source, optionally narrowed to one window. Entries are sorted by cache key descending, i.e. newest first within each window.

Query Parameters

NameTypeDescription
sourcestringrequiredWhich digest store to read.
xbwe
windowstringoptionalOnly digests whose key starts with this window; must be one of the four values when given, omit for all windows (window: "all" in the response).
4h12h1d1w
fromstringoptionalISO 8601 lower bound (inclusive) on the digest window start. The feed pages use from/to to fetch digests for older posts as they page in (since 2026-10-20).
tostringoptionalISO 8601 upper bound (exclusive) on the digest window start. Invalid from/to โ†’ 400.

Response โ€” DigestList

{
  "source": "x",
  "window": "1d",              // "all" when the query had no window
  "total": 42,
  "digests": [
    {
      "key": "1d_2026-04-01T00:00:00.000Z",   // "{window}_{start}"
      "window": "1d",
      "start": "2026-04-01T00:00:00.000Z",
      "sentiment": "bearish",
      "title": "...",
      "title_en": "Tariff fears dominate as markets sell off",
      "summary": "...",
      "summary_en": "...",
      "key_themes": ["tariffs", "fed policy"],
      "notable_tickers": ["SPY", "QQQ", "NVDA"]
    }
  ]
}

Other Status Codes

CodeMeaning
400Missing or invalid source (must be x or bwe), or window given but not one of 4h, 12h, 1d, 1w (window must be one of 4h, 12h, 1d, 1w)
This endpoint only reads from cache โ€” it never triggers an LLM call. Use POST /api/x-digest or POST /api/bwe-digest to generate new digests.
There is no limit or since parameter: the response is the whole store for the selected window (over a thousand 4h digests, several MB). Always pass window and slice digests[] client-side for the newest N; they are sorted newest first.
window, when given, is validated against the same four values as the digest POST routes and then matched as the key prefix {window}_.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/digests?source=x&window=1d"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/digests?source=bwe"
๐ŸŒ Macro

Daily macro brief, live indicators and the economic events calendar.

GET

/api/macro-analysis

LLMcached AI-generated daily macro market brief with live data

Formats the latest macro snapshot (live Polygon overlay for equities/ETFs) into a prompt and asks the LLM for a ~350-word institutional-style macro brief. The result is cached in memory per snapshot id and locale, so repeat calls are instant until a new snapshot lands.

Query Parameters

NameTypeDescription
localestringoptionaldefault enLanguage of the brief; any value other than zh falls back to en.
enzh

Response โ€” text/plain

Content-Type: text/plain; charset=utf-8
X-AI-Provider: anthropic        // or "cache" when served from the in-memory cache
X-AI-Model: claude-opus-4-6     // or "cached"

Growth is cooling but not collapsing: payrolls printed +142K against a 4.2% unemployment
rate while real GDP held at 2.1% QoQ ... (~350 words, no title)

Other Status Codes

CodeMeaning
404No snapshot data available (plain-text body) โ€” macro.json has no valid snapshot yet
Response headers X-AI-Provider and X-AI-Model indicate which LLM generated the analysis. Cached replays report X-AI-Provider: cache / X-AI-Model: cached.
Returns text/plain (~350 words). The prompt includes live S&P 500, Nasdaq, 10Y yield, oil, VIX, BTC, gold, and 20+ macro indicators; indicators without a reading are passed to the model as n/a.
Generation order: remote Claude (when enabled) first, then the API provider fallback chain (ANTHROPIC_MODEL_MACRO, Codex, Qwen). Up to 20 snapshot/locale pairs are kept in the in-memory cache.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/macro-analysis?locale=zh"
GET

/api/macro-indicators

cached Raw macro indicator values across all eight factor groups โ€” no LLM call

Returns the latest macro snapshot as JSON. Each indicator is either a price item { close, change_pct } or an economic indicator { value, date }. Categories: market, growth, inflation, risk_appetite, liquidity, fiat_credit, ai_tech, global_markets.

Query Parameters

NameTypeDescription
categorystringoptionalRestrict to one factor group.
marketgrowthinflationrisk_appetiteliquidityfiat_creditai_techglobal_markets
keysstring[]optionalComma-separated indicator keys, e.g. cpi,unemployment_rate,vix; matches are returned grouped by their category (categories with no match are omitted).
livestringoptionaldefault trueOverlay equity/ETF prices with live Polygon.io quotes (indices, crypto and economic indicators stay from the snapshot). Accepts exactly true, false, 1 or 0; any other value is a 400.
truefalse10
historystringoptionaldefault falseAppend a history map (~120 daily points each) for indicators without a public ticker: total_pc_ratio, equity_pc_ratio, us_debt_gdp, fiscal_deficit_gdp. Accepts exactly true, false, 1 or 0; any other value is a 400.
truefalse10

Response โ€” MacroIndicators

{
  "snapshot_id": "macro_20260511_080005",
  "timestamp": "2026-05-11T08:00:05Z",
  "live": true,
  "indicators": {
    "market": {
      "sp500":        { "close": 7399.2, "change_pct": 0.81 },
      "nasdaq":       { "close": 26247.1, "change_pct": 1.72 },
      "treasury_10y": { "close": 4.36,   "change_pct": -0.45 },
      "oil":          { "close": 99.1,   "change_pct": 0.55 }
    },
    "inflation": {
      "cpi":       { "value": 3.3, "date": "2026-04-01" },
      "core_pce":  { "value": 3.2, "date": "2026-03-01" }
    }
    // ... growth, risk_appetite, liquidity, fiat_credit, ai_tech, global_markets
  },
  "history": {                                   // only with history=true
    "total_pc_ratio": [ { "date": "2026-01-02", "value": 0.91 }, ... ],
    "equity_pc_ratio": [ ... ], "us_debt_gdp": [ ... ], "fiscal_deficit_gdp": [ ... ]
  }
}

Other Status Codes

CodeMeaning
400live or history not one of true, false, 1, 0 (live and history accept true|false|1|0); invalid category (body lists valid_categories); or an empty keys parameter
404No snapshot data available
Pure data endpoint โ€” no LLM, no AI provider headers. Use /api/macro-analysis when you want a written brief.
live and history are checked before the snapshot is read, so a bad flag value is a 400 even when no snapshot exists; the response echoes the effective live as a JSON boolean.
close and value are null when a series has no reading yet (e.g. mag7_avg before the live overlay); render a dash rather than zero.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/macro-indicators?category=inflation"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/macro-indicators?keys=cpi,unemployment_rate,vix,btc"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/macro-indicators?live=false&history=true"
GET

/api/events

Merged economic-events calendar: US/CN/HK macro releases, central-bank decisions, mega-cap US and HK earnings

Builds a day-bucketed calendar from today (UTC) through today plus days (inclusive). Macro and central-bank releases come from events.json (Unusual Whales for the US, TuShare for CN/HK/GLOBAL), HK results dates from hk_earnings.json, and US earnings from the Finnhub calendar file restricted to revenue estimates of at least $10B. US earnings are tagged with the options-implied move.

Query Parameters

NameTypeDescription
daysintegeroptionaldefault 141โ€“30Number of days ahead to include (window is today through today+days, inclusive); non-numeric values fall back to 14.

Response โ€” CalendarPayload

{
  "fetched_at": "2026-09-07T05:05:12+00:00",      // events.json fetch time; null when the file is missing
  "hk_checked_at": "2026-09-06T02:10:00+00:00",   // hk_earnings.json check time; null when missing
  "days": [
    {
      "date": "2026-09-10",
      "events": [                                  // sorted by time_utc, untimed events last
        {
          "id": "3f9c1a2b7d4e",                    // sha1(source|date|title)[:12] for macro rows
          "date": "2026-09-10",
          "time_utc": "12:30", "time_et": "08:30", "time_hkt": "20:30",   // null when the release has no time
          "region": "US",                          // US | CN | HK | GLOBAL
          "type": "macro",                         // macro | central_bank | earnings
          "importance": "high",                    // high | normal
          "title_zh": "็พŽๅ›ฝCPIๅŒๆฏ”", "title_en": "CPI YoY",
          "prev": "2.9%", "forecast": "3.0%",      // as published (strings); null when unknown
          "source": "uw",                          // uw | tushare | hk_list | finnhub
          "meta": {}
        },
        {
          "id": "us-BABA-2026-09-10", "date": "2026-09-10",
          "time_utc": null, "time_et": null, "time_hkt": null,
          "region": "US", "type": "earnings", "importance": "high",
          "title_zh": "BABA ่ดขๆŠฅ", "title_en": "BABA earnings",
          "prev": null, "forecast": null, "source": "finnhub",
          "meta": { "symbol": "BABA", "hour": "bmo", "hkCode": "09988.HK", "impliedMovePct": 6.8 }
        },
        {
          "id": "hk-00700-2026-09-10", "date": "2026-09-10",
          "time_utc": null, "time_et": null, "time_hkt": null,
          "region": "HK", "type": "earnings", "importance": "high",
          "title_zh": "่…พ่ฎฏๆŽง่‚ก ไธš็ปฉ", "title_en": "Tencent results",
          "prev": null, "forecast": null, "source": "hk_list",
          "meta": { "symbol": "00700", "confirmed": true }
        }
      ]
    }
  ]
}
importance is high for every earnings and central-bank row; macro releases are high only when the title matches a market-moving keyword list (CPI, PPI, PCE, GDP, PMI, ISM, payrolls, FOMC, retail sales, jobless claims, LPR/MLF, ...) and normal otherwise.
meta.impliedMovePct (percent, one decimal) is added to at most the first 40 US earnings rows from the Unusual Whales IV term structure (first expiry on/after the report date, cached 12 h); it is absent when no usable term structure exists. meta.hkCode is present only for US tickers that are also listed in Hong Kong.
The response is memoised in memory for 10 minutes per days value. Missing source files are not an error โ€” the affected rows are simply absent and fetched_at / hk_checked_at are null.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/events"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/events?days=7"
๐Ÿ“… Earnings

Earnings calendar, realised results and implied-vs-realised moves.

GET

/api/earnings

US-equity earnings calendar (stored window: ~7 days back to ~30 days ahead, refreshed daily 05:15 UTC)

Filters the Finnhub earnings calendar file. Without an explicit date or from the response is forward-looking from today (ET); past results with surprises and price reactions live at /api/earnings/results.

Query Parameters

NameTypeDescription
tierstringoptionaldefault allRevenue-estimate filter: big = at least $100M, mega = at least $10B; case-insensitive, any other value is rejected with 400.
allbigmega
datestringoptionalExact-day filter (YYYY-MM-DD); when set, from no longer defaults to today.
fromstringoptionaldefault today (ET)Inclusive lower bound (YYYY-MM-DD). When neither date nor from is given the response starts at today (ET); pass from explicitly to reach the past week.
tostringoptionalInclusive upper bound (YYYY-MM-DD).
symbolstring[]optionalOne or more comma-separated tickers (case-insensitive), e.g. AAPL,MSFT.
hourstringoptionalReporting time, exact match on the stored value: bmo = before market open, amc = after market close, dmh = during market hours.
bmoamcdmh
limitintegeroptionaldefault 5000โ€ฆโ€“10000Cap on returned items (total still reports the unfiltered count).
group_bystringoptionalSet to date to receive days[] (each { date, count, entries[] }, ascending by date) instead of a flat items[].
date

Response โ€” EarningsCalendar

{
  "source": "finnhub",
  "fetched_at": "2026-04-27T05:15:08Z",
  "from_date": "2026-04-20",                  // stored window (file), not the filtered range
  "to_date":   "2026-05-27",
  "total": 7,                                 // matches before limit
  "returned": 7,
  "filters": { "tier": "mega", "date": null, "from": "2026-04-27", "to": null, "symbol": null, "hour": null },
  "items": [
    {
      "date": "2026-04-29",
      "symbol": "AMZN",
      "hour": "amc",
      "quarter": 1,
      "year": 2026,
      "epsActual": null,
      "epsEstimate": 1.667,
      "revenueActual": null,
      "revenueEstimate": 180846456490
    }
  ]
  // with group_by=date: "days": [ { "date": "2026-04-29", "count": 3, "entries": [ ... ] } ] replaces "items"
}

Other Status Codes

CodeMeaning
400Unknown tier โ€” body is { "error": "invalid tier", "valid_tiers": ["all", "big", "mega"] }
hour values: bmo = before market open, amc = after market close, dmh = during market hours, empty string = unannounced. *Actual fields are null until the company reports.
filters.from echoes the effective lower bound, i.e. today (ET) when it was defaulted; filters.tier echoes the lower-cased tier. Items keep the file order (by date); within a day they are not re-sorted.
tier is lower-cased before the check, so MEGA works, but anything outside all / big / mega is a 400 (valid_tiers in the body) rather than being treated as all.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/earnings?symbol=AAPL,MSFT,GOOGL,META,AMZN,NVDA,TSLA&from=2026-04-27&to=2026-05-11"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/earnings?tier=mega&group_by=date"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/earnings?date=2026-04-29&hour=amc"
GET

/api/earnings/results

Recent reports (revenue est โ‰ฅ $1B): EPS / revenue surprise and price reaction

Companies with a revenue estimate of at least $1B that reported in the last days ET calendar days (inclusive of today), with surprise percentages, a beat/miss/mixed verdict and the close-to-close move of the session that priced the report.

Query Parameters

NameTypeDescription
daysintegeroptionaldefault 71โ€“30Look-back window in ET calendar days; must be an integer within range (out-of-range or non-integer values are rejected, not clamped).

Response โ€” ResultsPayload

{
  "generated_at": "2026-09-02T12:00:03Z",
  "from": "2026-08-26", "to": "2026-09-02", "days": 7,
  "source_fetched_at": "2026-09-02T05:15:08Z",
  "summary": { "reported": 38, "beats": 27, "mixed": 2, "misses": 9, "withReaction": 35, "avgReactionPct": 1.12 },
  "items": [
    {
      "date": "2026-08-27", "symbol": "NVDA", "hour": "amc", "quarter": 2, "year": 2027,
      "epsActual": 1.05, "epsEstimate": 1.01, "revenueActual": 46743000000, "revenueEstimate": 46020000000,
      "epsSurprisePct": 3.96, "revenueSurprisePct": 1.57,
      "verdict": "beat",             // beat | miss | mixed | null (no surprise computable)
      "beat": true,                  // compatibility flag: true = beat, false = miss or mixed, null = no verdict
      "reactionPct": -0.82,          // close-to-close move of the session that priced the report
      "reactionBasis": "amc",        // amc โ†’ next session (also used for dmh / unknown); bmo โ†’ same session
      "reactionSession": "2026-08-28"
    }
  ]
}

Other Status Codes

CodeMeaning
400days must be an integer between 1 and 30
500failed to build earnings results
Sorted by date desc, then revenue estimate desc, then symbol. reactionPct is null while the pricing session is still open (sessions count as closed at 16:15 ET) or when a bar is missing.
verdict is beat when every available surprise is โ‰ฅ 0, miss when every one is < 0, mixed when EPS and revenue disagree in sign. Results are memoised for 5 minutes on the source file's mtime.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/earnings/results?days=14"
GET

/api/earnings-vol

Realised vs options-implied move for a symbol's last eight earnings reports

Per-symbol record of how the stock actually moved across each of its recent reports versus what the ATM straddle implied the day before. Either ticker or top=1 is required.

Query Parameters

NameTypeDescription
tickerstringoptionalSymbol (case-insensitive, a leading $ is stripped) that must match ^[A-Z][A-Z0-9.-]{0,9}$ after normalisation โ†’ { symbol, generated_at, as_of, record | null }.
topstringoptionalSet to 1 to instead list the symbols with the largest |avg realised โˆ’ avg implied| gap (needs at least 4 paired events) โ†’ { generated_at, as_of, coverage, items[] }; takes precedence over ticker.
1
limitintegeroptionaldefault 201โ€“100Row cap for the top=1 board; non-numeric values fall back to the default.

Response โ€” EarningsVolResponse (ticker=NVDA)

{
  "symbol": "NVDA",
  "generated_at": "2026-09-02T18:15:00+00:00",
  "as_of": "2026-09-01",
  "record": {
    "n": 8, "n_implied": 8,
    "avg_abs_realised": 6.1,            // % close-to-close across the report (BMO: day vs prior; AMC: next vs day)
    "avg_implied": 7.4,                 // % ATM straddle / spot at T-1, nearest expiry after the report
    "ratio": 0.82,                      // realised รท implied
    "hit_rate": 0.25,                   // share of reports where |realised| > implied
    "next": { "date": "2026-11-19", "hour": "amc" } | null,
    "events": [ { "date": "2026-08-27", "hour": "amc", "period": "Q2 FY27", "session": "2026-08-28", "spot": 181.2, "realised_pct": -3.4, "implied_pct": 6.8, "expiry": "2026-08-29" }, ... ]
  } | null
}

// top=1
{
  "generated_at": "...", "as_of": "2026-09-01",
  "coverage": { "symbols": 1120, "symbols_with_implied": 980, "events": 8400, "events_with_realised": 8100, "events_with_implied": 6900 },
  "items": [ { "symbol": "SMCI", "avg_abs_realised": 14.2, "avg_implied": 9.1, "gap": 5.1, "ratio": 1.56, "hit_rate": 0.75, "n_implied": 8, "next": { ... } | null }, ... ]   // sorted by |gap| desc
}

Other Status Codes

CodeMeaning
400ticker missing or not matching ^[A-Z][A-Z0-9.-]{0,9}$ (only when top is not 1)
500earnings vol unavailable (unexpected read error)
503earnings vol data unavailable โ€” earnings_vol.json has not been pulled yet
Built on the AWS market database from daily stock and option bars (earnings_vol.json, ~1.1k symbols: market cap โ‰ฅ $10B plus anything reporting within ยฑ30 days). 503 while the file is missing; record: null for an uncovered symbol. Powers the card on /stocks and the calendar detail pane.
hour can also be dmh (during market hours) or empty; both use the AMC rule for the realised move. events are oldest first, at most 8.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/earnings-vol?ticker=NVDA"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/earnings-vol?top=1&limit=10"
๐Ÿ“ˆ Stocks

Ticker search, prices, news, fundamentals, factor exposure and options history.

GET

/api/stock-ohlc

Daily OHLCV candlestick bars for a ticker

Reads the local daily aggregates for every weekday in the range (both ends inclusive); weekends and days without a bar are skipped.

Query Parameters

NameTypeDescription
tickerstringrequiredStock/ETF ticker symbol (case-insensitive; echoed back upper-cased).
startstringoptionaldefault 180 days agoStart date, YYYY-MM-DD (inclusive).
endstringoptionaldefault today (UTC)End date, YYYY-MM-DD (inclusive).

Response โ€” OHLCResponse

{
  "ticker": "NVDA",
  "bars": [
    {
      "date": "2026-03-17",
      "open": 142.50,
      "high": 145.20,
      "low": 141.80,
      "close": 144.90,
      "volume": 38500000
    }
  ]
}

Other Status Codes

CodeMeaning
400ticker missing
An unknown ticker or a range with no data returns 200 with an empty bars array (no 404).

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/stock-ohlc?ticker=AAPL&start=2026-01-01"
GET

/api/stock-news

X/Twitter posts mentioning a stock (matched by ticker, $tag, or company name)

Looks the ticker up in the server-side news index built from every X post (cashtag, then bare symbol, then company name, with false-positive guards). Posts are newest first with exact-duplicate texts removed.

Query Parameters

NameTypeDescription
tickerstringrequiredStock ticker symbol; dual share classes share one list (GOOGL resolves to GOOG).

Response โ€” StockNews

{
  "ticker": "TSLA",
  "news": [
    {
      "id": "x_20320980...",
      "timestamp": "2026-03-15T12:00:00.000Z",
      "raw_text": "$TSLA deliveries beat expectations...",
      "author": "CNBC",
      "matchScore": 1.0,          // 1.0 = cashtag, 0.9 = bare symbol, 0.8 = company name
      "matchReason": "$TSLA",
      "matchTier": "cashtag"      // "cashtag" | "symbol" | "name"
    }
  ]
}

Other Status Codes

CodeMeaning
400ticker missing
The full match list is returned (no paging). An unknown ticker returns 200 with an empty news array.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/stock-news?ticker=TSLA"
GET

/api/stock-returns

Forward returns (T+1, T+3, T+5 trading days) from an event date

Closes on the next 1, 3 and 5 trading days after the event date, expressed as percentage change from the event-day close.

Query Parameters

NameTypeDescription
tickerstringrequiredStock ticker symbol (case-insensitive).
datestringrequiredEvent date, YYYY-MM-DD (must be a trading day with a bar for the ticker).

Response โ€” ForwardReturns

{
  "eventDate": "2026-03-17",
  "eventClose": 144.90,
  "t1": { "date": "2026-03-18", "close": 146.20, "returnPct": 0.90 },
  "t3": { "date": "2026-03-20", "close": 143.50, "returnPct": -0.97 },
  "t5": { "date": "2026-03-24", "close": 148.00, "returnPct": 2.14 }   // null until 5 trading days have elapsed
}

Other Status Codes

CodeMeaning
400ticker or date missing
When date is not a trading day (or the ticker has no bar that day) the route still answers 200 with eventClose: null and t1/t3/t5 all null.
returnPct is a percentage (0.90 = +0.90%). A horizon is null while fewer than that many trading days have elapsed since the event.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/stock-returns?ticker=NVDA&date=2026-03-17"
POST

/api/stock-analysis

LLMcached AI-generated equity analysis with technicals + news synthesis

Builds a prompt from the ticker's OHLC summary, the X posts matched inside the window and (when on file) the SEC fundamentals brief, then returns the model's ~400-word write-up as plain text.

Request Body (JSON)

NameTypeDescription
tickerstringrequiredStock ticker symbol.
startDatestringoptionaldefault 180 days agoAnalysis start date, YYYY-MM-DD.
endDatestringoptionaldefault today (UTC)Analysis end date, YYYY-MM-DD.
localestringoptionaldefault enOutput language; any value other than zh falls back to en.
enzh

Response โ€” text/plain

// Content-Type: text/plain; charset=utf-8
// X-AI-Provider: <provider>   X-AI-Model: <model>   ("cache" / "cached" on a memoised hit)

NVDA has spent the last six months in a steady uptrend ...
(~400 words covering technical setup, news impact, 1-2 week outlook and key risk factors)

Other Status Codes

CodeMeaning
400ticker missing (plain-text body)
Triggers an LLM call (~120s; the route allows up to 300s).
Results are memoised in process memory per ticker/startDate/endDate/locale (last 50 keys); a hit returns immediately with X-AI-Provider: cache.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST https://news.all-vista.org/api/stock-analysis -H "Content-Type: application/json" -d '{"ticker":"NVDA","locale":"zh"}'
GET

/api/ticker-hub

One symbol across every module โ€” earnings, flow, dark pool, congress, Trump, X mentions

Server-side aggregate of everything the site knows about a symbol. Each section is built independently and is null when the module has nothing for the symbol OR its upstream failed; only flow, darkpool and congress carry fetchedAt/stale to tell the two apart. earnings is null whenever the next report is outside the stored earnings window (about 7 days back to 30 days ahead), which is the common case for a name that reported recently.

Query Parameters

NameTypeDescription
tickerstringrequiredSymbol matching ^[A-Z][A-Z0-9.-]{0,9}$ (leading $ and lower case are normalised).

Response โ€” TickerHub

{
  "symbol": "NVDA",
  "generatedAt": "2026-09-02T12:00:03Z",
  "todayEt": "2026-09-02",
  "info":       { "ticker": "NVDA", "name": "NVIDIA Corp", "exchange": "XNAS", "type": "CS", "marketCap": 4.4e12, "marketCapTier": "Mega Cap (>$100B)", "industry": "SEMICONDUCTORS & RELATED DEVICES" } | null,   // tier labels are display strings, not enums
  "earnings":   { "date": "2026-09-24", "hour": "amc", "daysUntil": 15, "quarter": 3, "year": 2026, "epsEstimate": 1.21, "revenueEstimate": 54.1e9, "impliedMovePct": 6.4 } | null,   // null unless the next report is within ~30 days
  "flow":       { "date": "2026-09-02", "count": 42, "highSignalCount": 5, "capped": false, "callPremium": 18400000, "putPremium": 6100000,
                  "top": [ { "id": "...", "type": "call", "strike": 190, "expiry": "2026-09-19", "premium": 2100000, "isSweep": true, "isFloor": false, "highSignal": true, "createdAt": "..." } ],
                  "fetchedAt": "...", "stale": false } | null,
  "darkpool":   { "date": "2026-09-02", "count": 17, "totalPremium": 210000000, "minPremium": 1000000,
                  "top": [ { "price": 181.2, "size": 172000, "premium": 31200000, "executedAt": "..." } ], "fetchedAt": "...", "stale": false } | null,
  "congress":   { "count": 4, "latest": [ { "name": "...", "memberType": "house", "txnType": "Purchase", "amounts": "$15Kโ€“$50K", "transactionDate": "...", "filedAtDate": "..." } ],
                  "fetchedAt": "...", "stale": false } | null,
  "trumpPosts": { "count90d": 2, "latest": [ { "id": "...", "created_at": "...", "headline": "...", "headline_en": "...", "impact_direction": "bullish", "url": "..." } ] } | null,
  "mentions":   { "daily": [ { "date": "2026-08-20", "count": 12 }, ... ],   // 14 ET days, oldest first
                  "count24h": 9, "count14d": 131,
                  "latest": [ { "id": "...", "author": "...", "text": "...", "timestamp": "..." } ] } | null,
  "optionsHistory": { "ticker": "NVDA", "date": "2026-09-01", "pc_ratio": 0.63, "pct_pc": 41, "total_vol": 2100000, "pct_vol": 55, "median_pc": 0.71 } | null
}

Other Status Codes

CodeMeaning
400ticker missing or not matching ^[A-Z][A-Z0-9.-]{0,9}$
500ticker hub unavailable
Each block is null when the module has nothing for the symbol or its source failed; mentions has no staleness flag of its own, so check /api/health (x file age) before trusting a low count24h. Powers the strip at the top of /stocks?ticker=.
The assembled hub is memoised for 2 minutes per symbol; a hub with a failed upstream section (flow, darkpool or congress null) is rebuilt after 20s. Each Unusual Whales call times out after 8s, so a slow upstream nulls its own block instead of failing the request.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/ticker-hub?ticker=NVDA"
GET

/api/fundamentals

cached SEC company facts per symbol โ€” 12 quarters + 5 fiscal years, TTM, margins, valuation

Serves the per-symbol SEC fundamentals JSON written by the AWS job (a plain file read memoised on mtime; nothing queries the market database per request). Money is in whole currency units, ratios are decimal fractions.

Query Parameters

NameTypeDescription
tickerstringrequiredSymbol matching ^[A-Z][A-Z0-9.-]{0,9}$ (leading $ and lower case are normalised).

Response โ€” Fundamentals

{
  "symbol": "NVDA",
  "name": "NVIDIA Corp",
  "entity_name": "NVIDIA CORP",
  "cik": "0001045810",
  "sic": "3674",
  "currency": "USD",                 // statement currency; valuation multiples only when USD
  "mixed_currency": false, "has_ifrs": false,
  "security_type": "CS", "adr": false,   // ADR: market cap + multiples omitted (ADS ratio unknown)
  "snapshot_date": "2026-09-01",
  "as_of": "2026-09-01",
  "latest_filed": "2026-08-27",
  "price": { "close": 181.20, "session": "2026-09-01" },
  "shares": { "outstanding": 24.3e9, "as_of": "2026-08-22", "source": "dei" },     // source: "dei" | "diluted_wavg" | null
  "ttm": { "revenue": 187e9, "gross_margin": 0.73, "operating_margin": 0.61, "net_margin": 0.54, "fcf": 78e9, "quarters": [ ...4 period ends ], ... } | null,
  "valuation": { "market_cap": 4.4e12, "pe_ttm": 44.1, "ps_ttm": 23.5, "pb": 41.2, "fcf_yield": 0.018, "eps_ttm": 4.11, "revenue_ttm": 187e9 },
  "quarters": [ { "label": "Q2 FY27", "end": "2026-07-26", "fy": 2027, "fq": 2, "filed": "2026-08-27", "derived": false, "revenue": ..., "net_income": ..., "eps_diluted": ..., "revenue_yoy": 0.56, ... } ],   // <= 12, oldest first
  "annual":   [ { "label": "FY26", "fq": null, ... } ],                                                                                                              // <= 5, oldest first
  "balance":  { "end": "2026-07-26", "cash": ..., "debt_total": ..., "net_cash": ..., "total_assets": ..., "total_liabilities": ..., "equity": ... } | null,
  "generated_at": "2026-09-02T18:05:58+00:00"
}

Other Status Codes

CodeMeaning
400ticker missing or not matching ^[A-Z][A-Z0-9.-]{0,9}$
404no company facts for the symbol (ETFs, funds, most foreign filers)
500fundamentals unavailable
Built twice daily on the AWS market database from the SEC companyfacts table (fundamentals/<TICKER>.json, ~5,100 symbols). Powers the fundamentals panel on /stocks and the "SEC fundamentals" section of POST /api/stock-analysis.
pe_ttm is null when TTM EPS is zero or negative or when statements are not in USD; net_cash = cash + short-term investments - total debt.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/fundamentals?ticker=NVDA"
GET

/api/factors

cached Fama-French 5 + momentum factor returns, and per-stock factor exposures

Reads factors.json from the AWS job: Ken French daily factors (ff5 + momentum) and a one-year daily OLS of excess total returns for the ~500 largest names plus a few benchmark ETFs. Returns are decimal fractions (0.01 = 1%); sparkline values are cumulative percent. Every window is anchored on latest_factor_date, not today.

Query Parameters

NameTypeDescription
tickerstringoptionalWithout it: the factor performance table. With it: that symbol's 1-year regression betas (exposure: null outside the ~500 largest names + benchmark ETFs). Leading $ and lower case are normalised; BRK-B is accepted for BRK.B.

Response โ€” FactorSummary | FactorExposureResponse

// GET /api/factors  (table)
{
  "as_of": "2026-09-02",
  "latest_factor_date": "2026-06-30",     // Ken French data lags the market by weeks
  "factor_lag_days": 64,
  "models": { "ff5": [ ... ], ... },
  "factor_order": ["MKT_RF", "SMB", "HML", "RMW", "CMA", "MOM", "RF"],
  "factors": { "MKT_RF": { "model": "ff5", "last": 0.004, "mtd": 0.012, "qtd": 0.031, "ytd": 0.084, "y1": 0.152, "vol_ann": 0.14,
                           "spark": [ ...60 ], "spark_start": "2026-04-02", "latest_date": "2026-06-30", "n_1y": 250 }, ... },
  "exposure_window": { "start": "2025-06-30", "end": "2026-06-30", "n_dates": 250, "regressors": [ ... ], "min_n": 120 },
  "universe": { "count": 512, "benchmarks": ["SPY", "QQQ", ...], "min_market_cap": 2.1e10, "skipped": 3, ... },
  "generated_at": "2026-09-02T14:02:00+00:00"
}

// GET /api/factors?ticker=NVDA
{
  "ticker": "NVDA",
  "exposure": { "beta": { "MKT_RF": 1.47, "SMB": -0.19, "HML": -0.12, "RMW": 0.40, "CMA": -1.45, "MOM": 0.30 },
                "alpha_ann": 0.21, "r2": 0.62, "idio_vol_ann": 0.31, "n": 250 } | null,
  "latest_factor_date": "2026-06-30",
  "exposure_window": { "start": "2025-06-30", "end": "2026-06-30", "n_dates": 250, "regressors": [ ... ], "min_n": 120 },
  "generated_at": "2026-09-02T14:02:00+00:00"
}

Other Status Codes

CodeMeaning
400ticker given but not matching ^[A-Z][A-Z0-9.-]{0,9}$
500factor data unavailable
503factors.json has not been produced yet
503 while factors.json has not been produced. Powers the factor table on /macro and the exposure chip in the ticker hub strip on /stocks.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/factors"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/factors?ticker=NVDA"
GET

/api/options-history

cached One year of daily option volume, put/call ratio and the recent OI / GEX / max-pain snapshots

Reads options_history.json from the AWS job: for ~36 symbols a 1-year daily option volume series (call/put/P-C/total with percentiles of the latest values) and, for the snapshot window, daily OI totals, a GEX proxy and front-week max pain.

Query Parameters

NameTypeDescription
tickerstringoptionaldefault SPYSPY, QQQ, IWM, DIA, TLT, GLD or one of the ~30 largest stocks; symbols in the response lists the universe. Trimmed, a leading $ stripped and upper-cased like the other ticker routes, then must match ^[A-Z][A-Z0-9.-]{0,9}$.

Response โ€” OptionsHistoryResponse

{
  "generated_at": "2026-09-02T18:16:00+00:00",
  "as_of": "2026-09-01",
  "window": { "start": "2025-09-02", "end": "2026-09-01" },
  "snapshot_start": "2026-08-11",          // first day with greeks / OI snapshots
  "max_pain_horizon_days": 7,
  "symbols": ["SPY", "QQQ", ..., "AAPL", ...],   // ETFs first, then stocks
  "universe": { "etfs": [ ... ], "stocks": [ ... ] },
  "ticker": "SPY",
  "data": {
    "kind": "etf",
    "dates": [ ...250 ], "call_vol": [ ... ], "put_vol": [ ... ], "pc_ratio": [ ... ], "total_vol": [ ... ],   // oldest first, aligned by index
    "oi": { "dates": [ ... ], "call_oi": [ ... ], "put_oi": [ ... ], "pc_oi": [ ... ], "gex": [ ... ], "max_pain": [ ... ], "mp_exp": [ ... ], "spot": [ ... ] },
    "pct":    { "pc_ratio": 62, "total_vol": 48 },   // percentile of the latest value within the year
    "median": { "pc_ratio": 0.91, "total_vol": 2950000 },
    "latest": { "date": "2026-09-01", "call_vol": 1640000, "put_vol": 1360000, "total_vol": 3000000, "pc_ratio": 0.83 }
  } | null
}

Other Status Codes

CodeMeaning
400ticker not matching ^[A-Z][A-Z0-9.-]{0,9}$ (1-10 characters)
500options history unavailable
503options_history.json missing
Pre-computed twice daily on the AWS market database (options_history.json). GEX is a proxy (ฮฃ gamma ร— OI ร— 100 ร— spotยฒ ร— 0.01, calls โˆ’ puts); OI comes from Polygon snapshots (a few % under OCC). data: null outside the universe, 503 while the file is missing. Powers the History tab on /flow and the P/C context line in the ticker hub strip.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/options-history?ticker=QQQ"
๐ŸŒŠ Options & Institutional Flow

Market tide, gamma exposure, dark pool prints, congressional trades, mood and the daily flow recap.

GET

/api/flow/tide

Intraday market tide โ€” cumulative net call/put premium for the whole US market

Unusual Whales market tide (/api/market/market-tide), one point per minute-ish bucket for the current session, mapped to short keys. Served from the in-process UW cache: 2 min TTL while the market is open, 30 min otherwise; the last good payload is returned with degraded: true if UW fails, and the route answers 503 when UW fails with nothing cached.

Parameters

None.

Response โ€” TideResponse

{
  "items": [
    {
      "t": "2026-09-04T09:30:00-04:00", // ET-offset ISO timestamp of the bucket
      "call": 12345678.9,               // net call premium, USD (cumulative for the session)
      "put": -4567890.1,                // net put premium, USD
      "vol": 154321                     // net option volume, contracts
    },
    ...
  ],
  "fetched_at": "2026-09-04T14:31:02.117Z", // when the UW payload was fetched (null in the 503 body)
  "market_open": true,                      // Monโ€“Fri 09:30โ€“16:00 ET
  "degraded": false, // true = stale-on-error payload (200) or the empty 503 body
  "freshness": { "kind": "intraday", "state": "live", "asOf": "2026-09-04T14:31:02.117Z", "ageMinutes": 0.4 } // live | delayed | stale | offline (session-aware)
}

// 503 โ€” Unusual Whales failed and nothing was cached yet
{ "items": [], "fetched_at": null, "market_open": true, "degraded": true, "error": "Unusual Whales unavailable" }

Other Status Codes

CodeMeaning
503Unusual Whales unavailable and nothing cached โ€” same body shape with items: [], fetched_at: null, degraded: true and error
A 200 with degraded: true is a stale cache hit served because the live UW call failed. A 503 (error: "Unusual Whales unavailable", empty items, fetched_at: null) means UW was unreachable and nothing was cached yet.
freshness (every flow route since 2026-10-20): state is live (fetched โ‰ค 5 min ago in session, or since the latest session open when the market is closed), delayed (โ‰ค 60 min in session, or the previous session), stale (older) or offline (nothing cached โ€” the 503 bodies). asOf is the fetch time, ageMinutes its age. The cache is persisted to disk, so a restart keeps the last good payloads.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/tide"
GET

/api/flow/gex

Dealer gamma exposure by strike near spot, with call/put walls

Unusual Whales spot exposures per strike (/api/stock/{ticker}/spot-exposures/strike, full chain of up to 500 strikes). The route keeps strikes within ยฑ12% of spot, picks the 34 closest to spot and orders them top-down; callWall is the strike with the largest call gamma and putWall the strike with the most negative put gamma inside that window. Same UW cache as the other flow routes (2 min open / 30 min closed): stale-on-error with degraded: true, 503 when UW fails with nothing cached.

Query Parameters

NameTypeDescription
tickerstringoptionaldefault SPYUnderlying symbol (case-insensitive); only the ten listed tickers are accepted.
SPYQQQIWMNVDAAAPLTSLAMSFTMETAAMZNGOOGL

Response โ€” GexResponse

{
  "ticker": "SPY",
  "spot": 648.12,                 // underlying price reported by UW (0 when no data)
  "strikes": [                    // โ‰ค 34 strikes within ยฑ12% of spot, highest strike first
    {
      "strike": 660,
      "callG": 812345678.5,       // dealer call gamma exposure (open-interest based), USD per 1% move
      "putG": -234567890.2,       // dealer put gamma exposure, always โ‰ค 0 (sign normalised)
      "net": 577777788.3          // callG + putG
    },
    ...
  ],
  "callWall": 650,                // strike with the largest callG in the window
  "putWall": 640,                 // strike with the most negative putG in the window
  "fetched_at": "2026-09-04T14:31:02.117Z",
  "market_open": true,
  "degraded": false,
  "freshness": { "kind": "intraday", "state": "live", "asOf": "2026-09-04T14:31:02.117Z", "ageMinutes": 0.4 } // live | delayed | stale | offline (session-aware)
}

Other Status Codes

CodeMeaning
400Unsupported ticker โ€” ticker is not one of the ten allowed symbols
503Unusual Whales unavailable and nothing cached โ€” same body shape with spot: 0, empty strikes, both walls 0, fetched_at: null, degraded: true and error
On upstream failure the last good payload is served with degraded: true (200). With nothing cached the route answers 503 with spot: 0, empty strikes, both walls 0, fetched_at: null and error: "Unusual Whales unavailable".

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/gex?ticker=SPY"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/gex?ticker=nvda"
GET

/api/flow/darkpool

Ten largest recent dark-pool prints by notional

Unusual Whales recent dark-pool prints (/api/darkpool/recent, UW caps the page at 200) sorted by premium and cut to the top 10. Served from the in-process UW cache (2 min while the market is open, 30 min otherwise) with stale-on-error fallback; 503 when UW fails with nothing cached.

Parameters

None.

Response โ€” DarkpoolResponse

{
  "items": [                                   // top 10 by premium, largest first
    {
      "ticker": "NVDA",
      "price": 176.42,                         // execution price
      "size": 1250000,                         // shares
      "premium": 220525000,                    // notional, USD (price ร— size)
      "executedAt": "2026-09-04T14:05:11.000Z" // ISO UTC
    },
    ...
  ],
  "fetched_at": "2026-09-04T14:31:02.117Z",
  "market_open": true,
  "degraded": false,
  "freshness": { "kind": "intraday", "state": "live", "asOf": "2026-09-04T14:31:02.117Z", "ageMinutes": 0.4 } // live | delayed | stale | offline (session-aware)
}

Other Status Codes

CodeMeaning
503Unusual Whales unavailable and nothing cached โ€” same body shape with items: [], fetched_at: null, degraded: true and error
A 200 with degraded: true is a stale cache hit; a 503 carries error: "Unusual Whales unavailable" and empty items.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/darkpool"
GET

/api/flow/congress

Congressional stock trades filed in the last 14 days

Unusual Whales recent congress trades (/api/congress/recent-trades, latest 100 filings) filtered to disclosures whose filedAtDate falls within the last 14 calendar days. Served from the in-process UW cache (2 min while the market is open, 30 min otherwise) with stale-on-error fallback; 503 when UW fails with nothing cached.

Parameters

None.

Response โ€” CongressResponse

{
  "items": [
    {
      "name": "Nancy Pelosi",          // reporter name (falls back to the filer name)
      "memberType": "house",           // house | senate
      "ticker": "NVDA",
      "txnType": "Buy",                // Buy | Sell | ... as reported by UW
      "amounts": "$1,000,001 - $5,000,000", // disclosure bracket, not an exact figure
      "transactionDate": "2026-08-21", // YYYY-MM-DD
      "filedAtDate": "2026-09-02"      // YYYY-MM-DD; only filings โ‰ค 14 days old are kept
    },
    ...
  ],
  "fetched_at": "2026-09-04T14:31:02.117Z",
  "market_open": true,
  "degraded": false,
  "freshness": { "kind": "intraday", "state": "live", "asOf": "2026-09-04T14:31:02.117Z", "ageMinutes": 0.4 } // live | delayed | stale | offline (session-aware)
}

Other Status Codes

CodeMeaning
503Unusual Whales unavailable and nothing cached โ€” same body shape with items: [], fetched_at: null, degraded: true and error
Filings are disclosed weeks after the trade; transactionDate can be well outside the 14-day window that applies to filedAtDate.
A 200 with degraded: true is a stale cache hit; a 503 carries error: "Unusual Whales unavailable" and empty items.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/congress"
GET

/api/flow/mood

Market mood: index-ETF fund flows, insider buy/sell tallies and sector-ETF option flow

Aggregates three Unusual Whales feeds in parallel: daily in/outflow for SPY, QQQ, IWM and DIA (/api/etfs/{T}/in-outflow, latest 10 days), market-wide insider purchases vs sales per filing day (/api/market/insider-buy-sells, 14 days) and the sector ETF table (/api/market/sector-etfs). Each feed uses the shared UW cache (2 min open / 30 min closed); a failed feed comes back empty and flips degraded while the rest is still returned. When every feed fails with nothing cached the route answers 503.

Parameters

None.

Response โ€” MoodResponse

{
  "etf": {
    "SPY": [                              // newest first, โ‰ค 10 days
      { "date": "2026-09-03", "prem": -812345678, "close": 646.9 }, // prem = net fund flow, USD
      ...
    ],
    "QQQ": [ ... ], "IWM": [ ... ], "DIA": [ ... ]   // an empty array means that feed failed
  },
  "insider": [                            // per filing day, โ‰ค 14 rows
    {
      "date": "2026-09-03",
      "buys": 118,                        // number of purchase filings
      "buyN": 45120000,                   // purchase notional, USD (absolute)
      "sells": 402,
      "sellN": 1210500000
    },
    ...
  ],
  "sectors": [
    {
      "ticker": "XLK",
      "name": "Technology Select Sector SPDR Fund",
      "last": 245.31,
      "changePct": 0.84,                  // (last โˆ’ open) / open ร— 100, 0 when open is missing
      "callPrem": 91234567,               // today's call premium, USD
      "putPrem": 45678901,                // today's put premium, USD
      "flow": -12345678                   // latest daily in/out flow, USD
    },
    ...
  ],
  "fetched_at": "2026-09-04T14:29:40.002Z", // OLDEST UW cache timestamp among the feeds that answered (null in the 503 body)
  "market_open": true,
  "degraded": false, // true if any of the three feeds was stale or failed
  "freshness": { "kind": "intraday", "state": "live", "asOf": "2026-09-04T14:31:02.117Z", "ageMinutes": 0.4 } // live | delayed | stale | offline (session-aware)
}

Other Status Codes

CodeMeaning
503Unusual Whales unavailable โ€” every feed failed with nothing cached; same body shape with empty etf arrays, insider: [], sectors: [], fetched_at: null, degraded: true and error
fetched_at is the oldest cache timestamp among the six UW requests that answered (four ETF flows, insiders, sectors), so every section is at least that fresh; individual sections may be newer. It is null only in the 503 body.
Partial failures stay 200: a feed that failed with nothing cached is simply empty and degraded is true. Only when all feeds fail is the status 503 with error: "Unusual Whales unavailable".

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/mood"
GET

/api/flow/alerts

Large options flow alerts (โ‰ฅ $250K, 1โ€“60 DTE) with a high-signal flag

Unusual Whales flow alerts (/api/option-trades/flow-alerts, 200 most recent with min_premium=250000) normalised to numbers and filtered to premium โ‰ฅ $250K and 1โ€“60 days to expiry, newest first, at most 120 rows. highSignal marks the strict subset used by the daily recap: premium โ‰ฅ $500K, 7โ€“42 DTE, sweep or floor print, and ask-side premium above bid-side. Served from the shared UW cache (2 min open / 30 min closed) with stale-on-error fallback; 503 when UW fails with nothing cached.

Parameters

None.

Response โ€” FlowAlertsResponse

{
  "items": [                                   // newest first, โ‰ค 120
    {
      "id": "1a2b3c4d-...",                    // UW alert id
      "ticker": "NVDA",
      "type": "call",                          // call | put
      "strike": 180,
      "expiry": "2026-09-19",                  // YYYY-MM-DD
      "premium": 1250000,                      // total premium, USD
      "askSidePremium": 1100000,
      "bidSidePremium": 150000,
      "isSweep": true,
      "isFloor": false,
      "rule": "RepeatedHits",                  // UW alert rule name
      "createdAt": "2026-09-04T14:02:31.000Z", // ISO
      "underlyingPrice": 176.4,
      "volume": 8420,
      "openInterest": 15310,
      "highSignal": true                       // โ‰ฅ $500K, 7โ€“42 DTE, sweep/floor, ask > bid
    },
    ...
  ],
  "fetched_at": "2026-09-04T14:31:02.117Z",
  "market_open": true,
  "degraded": false,
  "freshness": { "kind": "intraday", "state": "live", "asOf": "2026-09-04T14:31:02.117Z", "ageMinutes": 0.4 } // live | delayed | stale | offline (session-aware)
}

Other Status Codes

CodeMeaning
503Unusual Whales unavailable and nothing cached โ€” same body shape with items: [], fetched_at: null, degraded: true and error
A 200 with degraded: true is a stale cache hit; a 503 carries error: "Unusual Whales unavailable" and empty items.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/alerts"
GET

/api/flow/recap

Latest AI smart-money recap (bilingual), as generated by the daily POST

Returns the recap file written by POST /api/flow/recap (stored at /root/data/flow/recap.json, overridable via FLOW_RECAP_PATH). It is regenerated once per US trading day after the close, so date is the ET trading date the recap covers; the previous recap is kept if a generation fails. A store file that is not valid JSON yields 503 until the next successful POST replaces it.

Parameters

None.

Response โ€” FlowRecapFile

{
  "date": "2026-09-04",                        // ET trading date the recap covers
  "generated_at": "2026-09-04T21:16:40.512Z",  // ISO UTC
  "provider": "remote_claude",                 // LLM provider that produced it
  "model": "claude-sonnet-4-6",
  "data": {
    "sentiment": "mixed",                      // bullish | bearish | neutral | mixed
    "title": "ๅŠๅฏผไฝ“็œ‹ๆถจๅคงๅ•ไธปๅฏผ๏ผŒๆŒ‡ๆ•ฐๅฏนๅ†ฒไปๅœจ",
    "title_en": "Semis lead call buying while index hedges persist",
    "summary": "2โ€“3 ๆฎตไธญๆ–‡ๅค็›˜ โ€ฆ",
    "summary_en": "2โ€“3 paragraph English recap ...",
    "key_tickers": ["NVDA", "SPY", "AMD"]      // uppercase, โ‰ค 8
  }
}

Other Status Codes

CodeMeaning
404No recap yet โ€” the file has never been generated
503Recap store file is corrupt (regenerate with POST) โ€” body is { error, hint }

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/flow/recap"
POST

/api/flow/recap

LLM Generate today's AI smart-money recap from live Unusual Whales data and persist it (idempotent per ET trading day)

Idempotent per ET trading day. On a weekend or NYSE holiday nothing is generated (200, skipped: true). When the store already holds a recap whose date is today and force is not true, that recap is returned with cached: true and X-AI-Provider: cache โ€” no LLM or Unusual Whales call. Otherwise the route pulls four Unusual Whales feeds (flow alerts โ‰ฅ $250K, market tide, dark-pool prints, latest 50 congress trades), keeps only high-signal alerts (โ‰ฅ $500K, 7โ€“42 DTE, sweep/block, ask-side), aggregates the top 8 tickers by premium, top 5 dark-pool prints and 8 congress trades, then asks the LLM for a strict-JSON bilingual recap. The result is written to the recap store and returned; the cron calls this on weekdays after the US close.

Request Body (JSON)

NameTypeDescription
forcebooleanoptionaldefault falseRegenerate even if today's recap already exists; must be the JSON boolean true (any other value, or no body, means false).

Response โ€” FlowRecapFile

// Fresh generation โ€” same shape as GET /api/flow/recap, plus X-AI-Provider / X-AI-Model response headers
{
  "date": "2026-09-04",
  "generated_at": "2026-09-04T21:16:40.512Z",
  "provider": "remote_claude",
  "model": "claude-sonnet-4-6",
  "data": {
    "sentiment": "mixed",            // bullish | bearish | neutral | mixed
    "title": "...", "title_en": "...",
    "summary": "...", "summary_en": "...",
    "key_tickers": ["NVDA", "SPY"]
  }
}

// Same day, recap already stored and force is not true โ€” X-AI-Provider: cache, X-AI-Model: cached
{ "date": "2026-09-04", "generated_at": "...", "provider": "...", "model": "...", "data": { ... }, "cached": true }

// NYSE closed (America/New_York date) โ€” nothing is generated, nothing is stored
{ "skipped": true, "reason": "weekend", "date": "2026-09-06" }
{ "skipped": true, "reason": "holiday", "holiday": "Labor Day", "date": "2026-09-07" }
{ "skipped": true, "reason": "stale_upstream", "as_of": "2026-10-17T01:15:02.000Z", "date": "2026-10-20" }

// 202 โ€” another generation is already running in this process
{ "status": "in_progress", "date": "2026-09-04" }

Other Status Codes

CodeMeaning
202Another generation is already running โ€” body { status: "in_progress", date }; retry in a minute (the finished recap is then served as cached: true)
500Recap generation failed โ€” both LLM attempts failed (or the store refused the write); the previous recap file is left untouched
502Upstream data unavailable โ€” one of the four Unusual Whales requests failed with nothing cached
A fresh generation costs one LLM request (up to two on retry, maxTokens 4096, route maxDuration 300 s) and four Unusual Whales requests, and is counted against the stricter LLM rate limit (60 per IP / 600 global per 5 min) for the public key. Same-day repeats without force: true are free: they return the stored recap with cached: true and X-AI-Provider: cache / X-AI-Model: cached.
The scheduled 21:15 ET run sends { "force": true }, so an earlier same-day call (for example a pre-market regeneration) never blocks the end-of-day recap. The JSON body is optional and only force is read; a missing or non-JSON body counts as force: false. Provider chain and models come from ANTHROPIC_MODEL_FLOW_RECAP, CODEX_MODEL_FLOW_RECAP and QWEN_MODEL_FLOW_RECAP (falling back to the site-wide defaults); the picked provider/model are echoed in X-AI-Provider / X-AI-Model.
The closure check uses the computed NYSE calendar (src/lib/market-calendar.ts): weekends plus New Year's Day, Martin Luther King Jr. Day, Presidents' Day, Good Friday, Memorial Day, Juneteenth, Independence Day, Labor Day, Thanksgiving and Christmas (observed dates), plus the ad-hoc closures listed in that module. Early-close days are trading days. The skip response carries reason (weekend, holiday or, since 2026-10-20, stale_upstream when Unusual Whales has not refreshed since the latest session open โ€” as_of is the newest fetch), the holiday name in holiday, and the ET date; force does not override it.
If the store file is not valid JSON it is moved aside as recap.json.corrupt-<timestamp> before the LLM call, so the new recap can always be written; until then GET /api/flow/recap answers 503.
Counts against the LLM rate limit. The response carries X-AI-Provider / X-AI-Model headers.

Example

curl -H "X-API-Key: $KEY" -X POST "https://news.all-vista.org/api/flow/recap"

curl -H "X-API-Key: $KEY" -X POST "https://news.all-vista.org/api/flow/recap" -H "Content-Type: application/json" -d '{"force": true}'
๐Ÿ‡จ๐Ÿ‡ณ China

A-share market overview, A/H premium and Stock Connect flows.

GET

/api/china/market

cached A-share indices, industry performance, market breadth, top movers and Stock Connect turnover

Both payloads are pre-computed files (china_market.json from the AWS job, hsgt.json from the Finland TuShare fetch); nothing is queried per request. No query parameters.

Parameters

None.

Response โ€” ChinaMarketBundle

{
  "market": {
    "session_date": "2026-09-01",
    "indices":    [ { "code": "000001.SH", "name_en": "SSE Composite", "close": 3851.2, "pct": 0.42, "amount_bn": 944.1, "spark": [ ... 60 ] } ],
    "industries": [ { "industry": "ๅŠๅฏผไฝ“", "name_en": "Semiconductors", "count": 162, "med_pct": 1.8, "adv": 120, "dec": 40, "amount_bn": 210.3, "top": { ... } } ],
    "breadth":    { "total": 5312, "advancers": 3105, "decliners": 1980, "limit_up": 61, "limit_down": 7, "st_count": 168, "turnover_bn": 2054.0, "buckets": [ ... ], "by_board": [ ... ] } | null,
    "movers":     { "by_amount": [ ... ], "gainers": [ ... ], "losers": [ ... ] },
    "generated_at": "2026-09-02T13:49:00+00:00"
  } | null,
  "hsgt": {
    "session_date": "2026-09-01",
    "flows": [ { "date": "2026-09-01", "north": 1350.2, "hgt": 640.1, "sgt": 710.1, "south": 480.3 } ],   // CNY 100m, 60 sessions
    "top10": { "sh": [ { "symbol": "600519.SH", "name": "่ดตๅทž่Œ…ๅฐ", "close": 1480.0, "pct": 0.9, "amount_m": 2130.5 } ], "sz": [ ... ] },
    "generated_at": "..."
  } | null,
  "ages": { "market_hours": 4.5, "hsgt_hours": 4.6 }   // hours since each file's generated_at; null when that file is missing
}

Other Status Codes

CodeMeaning
500Failed to read China market data
503China market data not available yet โ€” neither china_market.json nor hsgt.json exists (body also carries the empty bundle)
Prices in CNY. Northbound figures are gross turnover โ€” HKEX stopped publishing daily net buying in Aug 2024. 503 only while neither file exists yet; when just one is missing its block is null and the response is still 200.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/china/market"
GET

/api/china/ah

A/H premium for ~40 dual listings and ADR/H premium for ~15 US-listed Chinese names

Joins the two AWS job files (A + H closes, ADR closes + FRED DEXCHUS) into one snapshot with an equal-weight premium index, sector medians and per-pair rows. Memoised until one of the files changes on disk. No query parameters.

Parameters

None.

Response โ€” AhSnapshot

{
  "available": true,                       // false (with "reason") until both job files and an FX rate exist
  "generated_at": "2026-09-02T13:57:00+00:00",
  "stale": false,                          // true when either source file is older than 72 h
  "as_of": "2026-09-01",
  "fx": { "cny_per_usd": 7.12, "cny_per_hkd": 0.913, "hkd_per_usd": 7.80, "date": "2026-08-29" },
  "index": { "latest": 41.9, "date": "2026-09-01", "chg1d": -0.3, "chg20d": 1.8, "min": 35.1, "max": 47.8, "median": 40.2, "pairs_used": 38,
             "series": [ { "d": "2026-03-10", "v": 38.2 }, ... 120 ] },   // equal-weight A/H premium, %
  "sectors": [ { "sector": "Banks", "count": 9, "median": 27.4 } ],
  "pairs": [ { "a": "601398.SH", "h": "01398.HK", "name_en": "ICBC", "sector": "Banks", "date": "2026-09-01", "a_cny": 7.21, "h_hkd": 5.90, "a_hkd": 7.89, "premium": 33.8, "chg1d": ..., "chg20d": ..., "spark": [ ... ] } ],
  "adr":   [ { "adr": "BABA", "h": "09988.HK", "ratio": 8, "adr_usd": 132.1, "h_hkd": 128.9, "adr_hkd": 128.8, "premium": -0.34, ... } ],
  "adr_skipped": [ { "adr": "...", "h": "...", "reason": "ratio_unverified" } ],   // ratio_unverified | no_adr_bars | no_h_bars | no_overlap
  "dropped": []
}

Other Status Codes

CodeMeaning
500A/H premium unavailable (unexpected read error)
Premium = A price in HKD รท H price โˆ’ 1 (CNYโ†’HKD via FRED DEXCHUS and the 7.80 peg); ADR premium uses the ADS ratio. Joined from the CN/HK and US job files (ah_pairs_cnhk.json, ah_pairs_us.json); available: false with a reason (no_cnhk, no_us, no_fx, no_sessions) until both exist.
Missing data never yields a non-200 status โ€” check available and stale instead. fx and index are null in the unavailable case; sectors, pairs and adr are empty arrays.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/china/ah"
GET

/api/china/pro

Public-fund heavy holdings (latest two report periods) and convertible bonds

Serves cn_pro.json produced by the AWS CN/HK job. Both sections are returned by default; section restricts the payload to one of them.

Query Parameters

NameTypeDescription
sectionstringoptionalReturn one section only; an empty value is treated as no filter.
fundscbs

Response โ€” CnProResponse

{
  "generated_at": "2026-09-02T14:07:00+00:00",
  "age_hours": 4.2,
  "dates": { "trade_date": "2026-09-01", "fund_period": "2026-06-30", "fund_prev_period": "2026-03-31" },
  "funds": {                                       // omitted when section=cbs
    "period": "2026-06-30", "prev_period": "2026-03-31",
    "universes": { "all": { "top": [ { "code": "600519.SH", "name": "่ดตๅทž่Œ…ๅฐ", "industry": "็™ฝ้…’", "funds": 1412, "mkv": 1580.2, "d_funds": -38, "d_mkv": -92.1 } ], "increases": [ ... ], "decreases": [ ... ], "n_funds": 8123, "total_mkv": 27110.5 }, "active": { ... } },
    "top_funds": [ { "code": "005827.OF", "name": "...", "top10_mkv": 210.4, "ret_1m": 3.1, "ret_3m": 9.8 } ]
  },
  "cbs": {                                         // omitted when section=funds
    "session_date": "2026-09-01",
    "rows": [ { "code": "113050.SH", "name": "ๅ—้“ถ่ฝฌๅ€บ", "stk_code": "601009.SH", "close": 121.3, "pct_chg": 0.4, "conv_price": 8.02, "stk_close": 9.10, "premium": 6.9, "ytm": -1.2, "call": { "type": "ๅ…ฌๅ‘Šๆ็คบๅผบ่ตŽ", "call_date": "2026-09-20" } | null, ... } ],
    ...
  }
}

Other Status Codes

CodeMeaning
400section must be 'funds' or 'cbs'
500cn_pro unavailable (unexpected read error)
503cn_pro data not available yet โ€” the AWS job has not produced cn_pro.json
Money in ไบฟๅ…ƒ (CNY 100m), shares in ไธ‡่‚ก; premiums and returns are fractions. 503 while cn_pro.json has not been produced; 400 for an unknown section.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/china/pro"

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/china/pro?section=cbs"
๐Ÿ“ฐ Daily Brief

The cross-source morning brief.

GET

/api/brief

cached The aggregated Daily Brief behind /brief โ€” markets, calendar, AI digests, smart money, Trump

Read-only aggregation of caches the site already maintains (macro snapshot, today's calendar, latest X and BWE digests, flow recap, Trump impact). No query parameters; the brief is memoised server-side for 60 s and shared across concurrent callers.

Parameters

None.

Response โ€” Brief

{
  "generated_at": "2026-09-02T11:30:00.000Z",
  "date_et": "2026-09-02",
  "glance": { "zh": "...", "en": "..." } | null,        // one-line header
  "markets": { "source_updated_at": "...", "headline": [ { "key": "sp500", "label": "S&P 500", "close": 6512.3, "change_pct": -0.42 } ], "movers": [ ... ] } | null,
  "calendar": { "date": "2026-09-02", "items": [ ... ] } | null,   // high-importance events + mega-cap earnings today
  "x_digest":  { "window": "1d", "sentiment": "bearish", "title_en": "...", "summary_en": "...", "key_themes": [...], "notable_tickers": [...] } | null,
  "bwe_digest": { ... } | null,
  "flow_recap": { "date": "2026-09-01", "title_en": "...", "summary_en": "...", "key_tickers": ["NVDA", "TSLA"] } | null,
  "trump": { "since": "2026-09-01T11:30:00.000Z", "total_posts": 14, "analyzed_posts": 11, "items": [ { "id": "...", "headline_en": "...", "impact_direction": "bearish", "impact_magnitude": 2, "suggested_symbols": ["SPY"] } ] }
}

Other Status Codes

CodeMeaning
500Brief unavailable
Read-only aggregation of existing caches โ€” no LLM call. Each section is null when its source is unavailable, so consumers should treat every block as optional (trump is always present, possibly with empty items). scripts/push_brief.py posts this payload to a webhook at 07:30 ET.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/brief"
๐Ÿ”Ž Search & Trending

Site-wide search and the hourly trending board.

๐Ÿฉบ System

Health and freshness of every data file behind the site.

GET

/api/health

Data-freshness check for every upstream feed the site depends on

Reports the age of each data file the site reads and whether it is older than that file's threshold. Pushed feeds (x, macro, bwe, events, earnings under /root/data/*) are judged by file mtime against 48 h. AWS market-database files under aws_data_dir are judged by their payload generated_at (mtime as fallback): china_market and hsgt 80 h (A-share files only move on CN trading days), ah_pairs_cnhk and ah_pairs_us 48 h, trump_intraday, factors, cn_pro, earnings_vol and options_history 36 h. fundamentals is judged by fundamentals_index.json: index โ‰ค 36 h old, count โ‰ฅ 4000 symbols and zero failed_batches. A missing file reports ageHours: null and stale: true; anyStale is the overall status and also drives the HTTP status: 200 when every file is fresh, 503 when any file is stale (same JSON body either way).

Parameters

None.

Response โ€” HealthResponse

// This example is served with HTTP 503 because anyStale is true; the body is identical on a 200
{
  "checked_at": "2026-09-04T14:31:02.117Z",
  "stale_threshold_hours": 48,             // default threshold for the pushed feeds
  "aws_data_dir": "/root/data/aws",
  "files": [                               // 15 entries: 5 pushed, 9 AWS, fundamentals
    { "key": "x",        "ageHours": 0.4,  "stale": false, "thresholdHours": 48 },
    { "key": "macro",    "ageHours": 6.1,  "stale": false, "thresholdHours": 48 },
    { "key": "bwe",      "ageHours": 1.2,  "stale": false, "thresholdHours": 48 },
    { "key": "events",   "ageHours": 9.5,  "stale": false, "thresholdHours": 48 },
    { "key": "earnings", "ageHours": 52.3, "stale": true,  "thresholdHours": 48 },
    { "key": "china_market",   "ageHours": 20.7, "stale": false, "thresholdHours": 80 },
    { "key": "hsgt",           "ageHours": 20.9, "stale": false, "thresholdHours": 80 },
    { "key": "ah_pairs_cnhk",  "ageHours": 20.7, "stale": false, "thresholdHours": 48 },
    { "key": "ah_pairs_us",    "ageHours": 8.6,  "stale": false, "thresholdHours": 48 },
    { "key": "trump_intraday", "ageHours": 8.6,  "stale": false, "thresholdHours": 36 },
    { "key": "factors",        "ageHours": 8.6,  "stale": false, "thresholdHours": 36 },
    { "key": "cn_pro",         "ageHours": null, "stale": true,  "thresholdHours": 36 }, // file missing
    { "key": "earnings_vol",   "ageHours": 8.6,  "stale": false, "thresholdHours": 36 },
    { "key": "options_history","ageHours": 8.6,  "stale": false, "thresholdHours": 36 },
    { "key": "fundamentals",   "ageHours": 8.7,  "stale": false, "thresholdHours": 36,
      "note": "5012 symbols, snapshot 2026-09-03" }   // or the problem list, e.g. "index too old; 3 failed batches"
  ],
  "flow": { "fetched_at": "2026-10-20T14:02:11.000Z", "state": "live", "age_minutes": 3.0, "market_open": true }, // Unusual Whales cache freshness (informational; not in files[]/anyStale)
  "anyStale": true                         // overall status: true if any file is stale (โ†’ HTTP 503)
}

Other Status Codes

CodeMeaning
503At least one file is stale (anyStale: true) โ€” same JSON body as the 200 response, so uptime monitors can alert on the status code alone
The status code mirrors anyStale: 200 only when every file is fresh, 503 otherwise. The body does not change, so consumers that parse it (the site layout banner and scripts/healthcheck_alert.py, every 30 min) still read anyStale / per-file stale.
ageHours is rounded to one decimal. For AWS files the age is measured from the payload's generated_at, so a file re-synced without new content still ages.

Example

curl -H "X-API-Key: $KEY" "https://news.all-vista.org/api/health"
โš  Error Codes
CodeMeaning
200Success
202Analysis already in progress for this ID โ€” retry later
400Missing or invalid required parameters
401Unauthorized โ€” missing or invalid API key
404Post/ticker not found, or no data available
429Rate limit exceeded โ€” see the Retry-After header and retry_after_seconds in the body
500Server error or LLM provider failure
502Upstream data provider failed
All AI-powered endpoints return X-AI-Provider and X-AI-Model response headers naming the LLM that produced the result (for example codex / gpt-5.5), or cache / cached for cache hits.