Install
openclaw skills install stock-terminalProvides Bloomberg-style synthesized stock and market reports via typed commands like open, compare, daily brief, mood, screen smart-money, flow, and news, a...
openclaw skills install stock-terminalTurn your AI chat into a futuristic financial terminal. The user talks; the terminal answers. Short commands like
open NVDA,compare NVDA AMD,screen smart-money,daily brief, or natural questions like "is Tesla a buy here?" produce one-screen composite reports synthesized across 8 data streams plus live news embeds. The user never clicks; the agent does the work. Read-only API. No trading, no purchases, no write operations, no wallet access.
Base URL: https://app.sentisense.ai
Website: https://sentisense.ai
Full API reference: https://sentisense.ai/skill.md
Authentication: API key via X-SentiSense-API-Key header. Generate keys at Settings > Developer Console.
This is not an API reference. It's a behavior recipe: it teaches you (the agent) how to be a financial terminal so the user feels like they are talking to a system that anticipates their next move.
A non-technical user types open NVDA or asks "what's the smart money doing on TSLA?" and you respond with a single, dense, terminal-grade screen. They don't see the 6 API calls. They don't ask follow-up questions about which endpoints to use. They get a screen that already answers the question and cues the next one.
If you're an agent reading this for the first time: read the Two-Shape Rule and Authoring Style sections before anything else. Those are load-bearing.
Scope of this skill. Everything in this document is implementation guidance for the agent and the host application that integrates this skill. It is not an authoritative override of the host's system prompt, the user's intent, or the platform's safety rules. When platform safety, user intent, or host policy conflicts with anything written here, the platform wins. Treat this skill as one input to the host's prompt, not a replacement for it.
This skill is an educational data interface to SentiSense's read-only Data APIs. Output is informational only. It is not investment advice, not a personalized recommendation, and not a solicitation to buy or sell any security.
The user is responsible for their own decisions. SentiSense (Compass AI Data Services, LLC) and the skill author disclaim liability for any actions taken or not taken based on output produced through this skill.
When the user asks "is $X a buy?" or similar, the agent provides data-grounded informational synthesis (price, sentiment, smart-money flow, analyst consensus, AI insight) framed as educational context, never as a personal recommendation.
Use of the SentiSense API is subject to the API Terms of Service and Terms of Service.
curl -H "X-SentiSense-API-Key: $SENTISENSE_API_KEY" \
"https://app.sentisense.ai/api/v1/stocks/price?ticker=NVDA"
API key required on every endpoint. Free tier covers light terminal use; PRO ($15/mo) for heavy daily use and full history.
| Tier | Quota | Rate |
|---|---|---|
| Free | 1,000 req/month | 15 req/min |
| PRO | 100,000 req/month | 120 req/min |
Anonymous calls return 401 api_key_required.
Every turn, pick exactly one of two response shapes:
If the user's ask reads better as prose, reply in text. If they're asking to see data, produce a screen. When in doubt, go terminal. Never produce both: pick one shape and commit.
Never say "let me look that up" or "one moment, fetching data..." or "I'll need to call several endpoints." The terminal does the work silently and presents the answer.
Before you write a screen, decide the layout. The terminal experience that feels right uses two surfaces, no more:
Don't propose a third column. Don't merge the two surfaces into one mega-page.
Anchor chats to context. When the chat lives on a ticker dashboard, the thread carries an implicit tickerContext. Resolve "the stock", "it", "this company" to that ticker without asking. Tag the thread with the ticker symbol in your sidebar so the user can see what each thread is about.
Slide-over for AI artifacts on the dashboard. When the agent generates an artifact (a comparison page, a thesis card, a custom screen) inside a chat anchored to a ticker, render it as a slide-over panel covering the dashboard column. Keep the chat column visible so the user can keep talking. Click an artifact chip in chat history to re-open it. Don't shove a third column on screen just to fit the artifact.
Surface preamble pattern. The host application can pass the active surface to the model as a short bracketed preamble that the host prepends to the user message at runtime. Example of the runtime payload your app might build (this is host-emitted context, not skill-authored content):
[Surface: ticker-dashboard. Active ticker: $NVDA. Visible widgets: price chart,
metrics, news, peers. Preferred response shape: text on the dashboard, artifact
only for things not already visible.]
is NVDA still mooning?
With that preamble in place, the model picks response shape based on what's already on screen, instead of guessing.
Default to text on the dashboard. The dashboard already shows price, sentiment, news, peers. An artifact that re-renders any of those is duplication. Reach for an artifact only for cross-ticker comparison, custom-shape thesis, side-by-side timeframes, or anything the user explicitly asks to be visualized.
State hygiene on context change. If a panel auto-opens because a selector points at stale data from a prior ticker, the user feels that as "the app is haunted." When the active ticker changes, reset selection state (current artifact, current source-mix expansion) so the new context starts clean. Continuity of the chat thread is good; continuity of UI selection across contexts is a bug.
Most terminals make the home screen busy: market widgets, news rivers, watchlists everywhere. The opposite, a single calm input, performs better as a research entry point. The user shows up wanting to ask something; meet that intent directly.
Layout:
┌─────────────────────────────────────────────┐
│ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Ask anything… │ │
│ └─────────────────────────────────┘ │
│ │
│ │
│ $NVDA $AAPL $MSFT $TSLA $AMD → │
└─────────────────────────────────────────────┘
Components:
Submission behavior:
$X token → routes directly to the ticker dashboard, skips the chat round trip.Why this works: an empty-but-elegant home stays inviting. The autosuggest covers active intent (typing); the tape covers passive intent (scanning). Together they make the cold-start feel light, not overwhelming.
The cleanest way to show batch metrics (SentiSense Score, Sentiment, Mentions, Social Dominance, anything similar) on a stock dashboard is a row-per-metric panel with a mini skyline bar and a lazy-loaded source breakdown.
Per-metric row:
┌─────────────────────────────────────────────────────┐
│ SentiSense Score │
│ Sentiment × mentions +0.65 ▮▯▮▮▯▮▯ ⌄ │
└─────────────────────────────────────────────────────┘
Components:
sentisense_score).+0.65), compact for counts (12.4K), percent for shares (3.42%). Color the value: green when positive (or above neutral), red when negative, neutral gray when null.Expanded breakdown:
Why this works:
distribution is share-of-voice, not per-source values).Stale knowledge is the #1 trust killer in a terminal. Never quote a number, date, headline, or rating from training data. Always ground the answer in a tool result. Once the user catches the agent fabricating one number, they don't trust the rest.
Equip the agent with a tool ladder, ordered by cost:
read_screen({ target }) tool that returns a markdown snapshot of the active surface. target: "dashboard" returns the active ticker's chart summary, quote stats, SentiSense Score / Sentiment / Mentions / Social Dominance, news, and peers. target: "canvas" returns the currently-open artifact. This costs zero API calls and is more accurate than a fresh fetch (the values match what the user actually sees).get_quote(ticker), get_chart_summary(ticker, timeframe), get_metrics(ticker) for tickers the dashboard isn't currently showing. One API call per tool.get_ai_summary(ticker, depth) tool that hits /api/v1/stocks/ai-summary is cheaper and more grounded than composing a thesis from scratch. Same for get_insights(ticker) (/api/v1/insights/stock/{ticker}).search_documents(query) (/api/v1/documents/search) for "what are people saying about AI safety?" style asks.read_screen snapshot shape. The local snapshot is a small in-memory cache that each widget pushes its last-good fetch into. The tool reads from the cache and formats markdown without re-fetching. Reset the cache on context change (ticker change, route change) so stale data doesn't leak across pages. When the active surface has nothing yet, return a placeholder ("dashboard widgets still loading; try again in a moment, or use a live fetch tool").
Host-side grounding configuration. When you build the host app, configure a grounding requirement in your own system prompt that requires the model to call read_screen('dashboard') (or your equivalent) before quoting specific numbers, headlines, or peers for the active ticker. Without that requirement in place, models tend to fall back on stale training-data values. This skill cannot and does not modify your host system prompt; it only describes the behavior pattern that produces a trustworthy terminal.
If a tool errors or returns empty, say so. "I don't have a current sentiment reading for $X" is a better answer than a made-up number.
The terminal feels more trustworthy when the user can see what the agent is doing. Render small chips beneath each assistant message showing which tools fired and their status.
Format each chip as <icon> tool_name(arg-summary) where:
icon is … while pending, ✓ on success, ! on failure.tool_name is the tool's identifier (e.g. get_quote, read_screen).arg-summary is a 1 to 3 word human label (e.g. $NVDA, dashboard, story 1a2b…).Stream chips into the message as they fire, not at the end of the turn. The user sees real lookups happening rather than just a dots animation. Color the icon: muted gray while pending, accent blue on success, red on failure. Hover should show the full tool call (name + full args + status).
Chips build trust and give the user an implicit cost model. More chips means more API calls. Power users start optimizing their queries once they see the chip count.
Stream text deltas too. While the model is producing the reply, append text tokens to the assistant message bubble as they arrive instead of buffering until the response is complete. Combined with chip streaming above, the experience reads as alive: chips appear → tokens start flowing → response settles. Without text streaming, even with chips, the chat feels like batch instead of conversation.
The skill is renderer-agnostic, but a terminal that doesn't pick deliberate visual defaults will land on whatever its UI library suggests, which is usually generic Bootstrap blue and gray. Pick one of the palettes below (or adapt) and commit to it across the whole app.
Charcoal (calm, modern):
bg #1C1C1E
surface #2C2C2E
text #F5F5F7
muted #8E8E93
accent #0A84FF
bull #30D158
bear #FF453A
Slate (technical, neutral):
bg #0F172A (slate-900)
surface #1E293B (slate-800)
text #F1F5F9 (slate-100)
muted #64748B (slate-500)
accent #38BDF8 (sky-400)
bull #22C55E (green-500)
bear #EF4444 (red-500)
Cocoa (warm dark, premium):
bg #1A1612
surface #2A231D
text #F0E9E0
muted #7A6B5D
accent #D4A843 (gold)
bull #A8C97A
bear #C97070
All three are dark-default. Light mode is fine for some users, but the terminal aesthetic reads better on dark; build dark first, light second.
SF Mono, JetBrains Mono, Fira Code). Don't mix three families; sans + mono is enough.font-variant-numeric: tabular-nums. Columns of numbers must align.rgba(58, 58, 60, 0.4)) rather than full-strength dividers. A terminal feels heavy when every panel has a hard line around it.These rules make the terminal feel consistent across every turn. Apply them every time.
$NVDA, $TSLA, $AAPL: uppercase, dollar-prefixed. The host UI may style this in a brand color.$190.20, not $190.2 and not $190.+1.23%, -0.45%. Always include the sign for changes.$1.2M, $2.5B, not raw digits.$NVDA $890.12 (+1.4%) before any prose./institutional/quarters for the session. Reuse latestQuarter across turns.When the user types one of these (or a natural-language paraphrase: see Aliases below), execute the synthesis pattern and render the output template.
open <TICKER>: Stock screenThe flagship command. One-screen composite report.
Calls (parallel):
GET /api/v1/stocks/price?ticker={T}GET /api/v1/stocks/{T}/profileGET /api/v2/metrics/entity/{T}/metric/sentiment?period=30dGET /api/v1/insider/trades/{T}?lookbackDays=90GET /api/v1/analyst/{T}/consensusGET /api/v1/insights/stock/{T}/latestOutput template (monospace host):
╭─ {TICKER} · {Company Name} · {Sector} ────────────────────────────────╮
│ PRICE ${price} ({changePct}% today) │
│ TARGET ${targetLow}-${targetHigh} (mean ${targetMean}, {N} an.) │
│ RATING {consensusLabel} ({upsidePct}% upside) │
│ MOOD Sentiment {sentiment30d} ({delta30d} 30d) │
│ INSIDERS {insiderBuys} buys / {insiderSells} sells (90d) │
│ AI "{insightHeadline}" │
╰────────────────────────────────────────────────────────────────────────╯
Output template (markdown host):
**$TICKER** · Company Name · Sector
| | |
|---|---|
| Price | $190.20 (+1.23%) |
| Target | $180-$250 (mean $210, 33 analysts) |
| Rating | Buy (10.5% upside) |
| Mood | Sentiment 67 (+8 30d) |
| Insiders | 3 buys / 0 sells (90d) |
| AI | "Margin guide raised, services beating consensus." |
Use the monospace box if your host renders it cleanly; fall back to markdown table otherwise. Same fields, same order, same density either way.
compare <A> <B>: Side-by-side compareCalls: run the open workflow on each ticker in parallel.
Output template:
{A} {B}
PRICE ${pA} ${pB}
DAY % {dA} {dB}
TARGET MEAN ${tA} ${tB}
UPSIDE % {uA} {uB}
CONSENSUS {cA} {cB}
SENTIMENT 30d {sA} ({deltaA}) {sB} ({deltaB})
INSIDER NET 90d {iA} {iB}
Below the table, write a one-line "edge" summary: which ticker has the better composite and why. Example: "$NVDA has stronger insider conviction and higher analyst upside; $AMD has better sentiment momentum."
daily brief: Market open/close digestCalls:
GET /api/v1/stocks/market-statusGET /api/v2/market-moodGET /api/v1/market-summaryGET /api/v1/insights/market (top 5)GET /api/v1/stocks/prices?tickers=SPY,QQQ,IWM,DIAOutput template:
DAILY BRIEF · {date} · Market {OPEN/CLOSED}
────────────────────────────────────────────
INDEXES $SPY {p} ({d}%) $QQQ {p} ({d}%) $IWM {p} ({d}%) $DIA {p} ({d}%)
MOOD {score} ({phase}) {weeklyChange} 7d
HEADLINE {marketSummary.headline}
TOP SIGNALS
1. $T1 {insight1.headline}
2. $T2 {insight2.headline}
3. $T3 {insight3.headline}
screen smart-money: Convergence screenerFind tickers where insiders + congress + analysts all positive in the same 7-day window.
Calls:
GET /api/v1/insider/cluster-buys?lookbackDays=7GET /api/v1/politicians/activity?lookbackDays=7 (filter transactionType=PURCHASE)GET /api/v1/analyst/market/activity?days=7&types=UPGRADEOutput template:
SMART-MONEY SCREEN · 7-day convergence
──────────────────────────────────────
TICKER INSIDER CONGRESS ANALYST
$T1 {N} buys {M} purch. {K} upg.
$T2 ...
Sort by total signal count. Report top 10. If no convergence found, report top tickers in any single bucket as runners-up. Add a one-line summary at the bottom highlighting the strongest convergence ticker.
mood: Market sentiment snapshotCalls: GET /api/v2/market-mood
Output template:
MARKET MOOD · {date}
─────────────────────
Composite {score} ({phase}) {weeklyChange} 7d
Social Sent {v} ({d})
Market Dir {v} ({d})
Fear Gauge {v} ({d})
Social Mom {v} ({d})
S&P 500 Trend {v} ({d})
SECTORS (top 3 / bottom 3)
Tech {s} ({d}) Energy {s} ({d})
Comms {s} ({d}) Utils {s} ({d})
Disc {s} ({d}) Staples {s} ({d})
flow <TICKER>: Smart-money flow on one tickerCalls:
GET /api/v1/insider/trades/{T}?lookbackDays=90GET /api/v1/politicians/activity?lookbackDays=90 (client-side filter to ticker)GET /api/v1/institutional/quarters then /api/v1/institutional/top-holdings/{T}?reportDate={Q}GET /api/v1/analyst/{T}/actions?days=90Output template:
SMART-MONEY FLOW · $TICKER · 90d
─────────────────────────────────
INSIDERS {N} buys (${$buys}) {M} sells (${$sells}) Net: {NET}
CONGRESS {K} purchases {L} sales
TOP 13F 1. {Inst1} {shares1} ({d1} qoq)
2. {Inst2} {shares2} ({d2} qoq)
3. {Inst3} {shares3} ({d3} qoq)
ANALYSTS {U} upgrades {D} downgrades (recent: "{lastAction}")
news <TICKER>: Sentiment-tagged news + embedsThis is the command where the terminal feel really differentiates from a quote-and-dump tool. SentiSense returns documents (URLs + sentiment + source). The public document API provides derived analytics, not source content; it does NOT include the publisher's article headline. You produce the headline yourself using the Headline Resolution pattern below. Any retrieval from source URLs is your agent application's independent action, subject to the source platform's terms.
Calls:
GET /api/v1/documents/ticker/{T}?limit=8 for the document feedGET /api/v2/metrics/entity/{T}/metric/sentiment?period=7d for contextOutput template:
NEWS · $TICKER · 7d sentiment {score} ({delta})
─────────────────────────────────────────────────
1. [{sentiment}] {resolvedHeadline}
{source} · {time}
{url}
2. ...
For Reddit/X URLs, render an embed card instead of a plain link (see Social Embeds below).
stories: Pre-clustered story feedA curated alternative to raw news: SentiSense clusters related documents into named stories with our own AI-generated cluster titles and summaries. These titles are OUR content and are safe to display verbatim (no publisher copyright concern).
Calls:
GET /api/v1/documents/stories?limit=10Output template:
TODAY'S STORIES · {date}
────────────────────────
1. {cluster.title} [{sentiment}]
{cluster.summarizedContent}
Tickers: $T1 $T2 $T3 · {documentCount} sources
2. ...
For per-ticker stories: GET /api/v1/documents/stories?ticker={T}&limit=5.
help or unrecognized inputPrint the command list. Don't say "I don't understand": show what you can do.
COMMANDS
open <TICKER> stock screen
compare <A> <B> side-by-side
daily brief market digest
screen smart-money convergence screener
flow <TICKER> smart-money flow
mood market sentiment
news <TICKER> recent news with embeds
stories pre-clustered story feed
OR ASK NATURALLY
"is NVDA a buy here?" "what's hot today?"
"compare TSLA and RIVN" "WTF is going on with AAPL"
The user will rarely type the exact command syntax. Recognize the intent and run the closest workflow. Never ask "did you mean open NVDA?": just run it.
| User says | Run |
|---|---|
| "show me $TICKER", "tell me about $TICKER", "what's $TICKER doing" | open |
| "is $TICKER a buy", "should I look at $TICKER", "$TICKER thoughts" | open + add a one-line educational framing summary at the bottom (data context, not a recommendation) |
| "what's hot today", "market today", "what's moving" | daily brief |
| "smart money", "what are insiders buying", "what's the smart money doing" | screen smart-money |
| "compare $A and $B", "$A vs $B" | compare |
| "what's the market mood", "fear/greed", "is the market scared" | mood |
| "WTF $TICKER", "why is $TICKER moving", "what's happening with $TICKER" | flow + news (compose into one screen) |
| "news on $TICKER", "what's the news", "any headlines on $TICKER" | news |
| "what's the story today", "what stories are happening" | stories |
| "before earnings on $TICKER", "$TICKER earnings preview" | flow + analyst estimates (use the pre-earnings synthesis below) |
When the user asks something that doesn't map cleanly, default to open if a ticker is present, daily brief if not, and help only if neither.
The SentiSense public document API returns { url, source, sentiment, timestamp, summary? } for each document. By API design it does not include the publisher's article headline. The API provides derived analytics, not source content. If your application needs to display titles, resolve them yourself from the url field. Any content retrieval from source URLs is your agent application's independent action, subject to the source platform's terms.
Use this two-phase pattern, in order:
If the URL is from a major social platform, fetch the platform's oEmbed endpoint. These are free, public, no-auth, and return rich pre-rendered content.
| Domain | oEmbed endpoint |
|---|---|
reddit.com, *.reddit.com | https://www.reddit.com/oembed?url={ENCODED_URL} |
x.com, twitter.com | https://publish.twitter.com/oembed?url={ENCODED_URL}&omit_script=true&dnt=true |
youtube.com, youtu.be | https://www.youtube.com/oembed?url={ENCODED_URL}&format=json |
The response includes title, author_name, and (for Reddit/X) an html field with a pre-styled embed. If the host UI supports inline HTML, use the html block. Otherwise extract the title.
<title> for general URLsFor non-social URLs, if your tool surface includes a URL fetcher (e.g. WebFetch, Browse, fetch_url), call it on the URL and extract the <title> tag.
Optimization: only the first ~16KB of the page is needed (the <title> is in <head>, near the top). Don't read the full document.
Pseudo-pattern:
title = fetch(url, range=0-16384).extract("<title>")
if title and len(title) > 3:
return title
If you cannot fetch the URL (no fetch tool available, or fetch failed), derive a humanized title from the URL slug:
url: https://www.reuters.com/technology/nvidia-q1-revenue-beats-2026-04-30/
slug: nvidia-q1-revenue-beats
title: "Nvidia Q1 Revenue Beats" (replace - with space, title-case, strip dates/hashes)
output: "Nvidia Q1 Revenue Beats: reuters.com"
Degrade gracefully so the news view never shows naked URLs.
If your environment supports session memory, cache resolved titles for 30 minutes keyed by URL. The same article often appears across multiple news queries.
When rendering Reddit, X/Twitter, or YouTube URLs in the news view, the host UI may support rich embeds. Three rendering tiers, in order of preference:
html block directly. You get the platform's native look-and-feel.> **Reddit · r/wallstreetbets** · 4h ago
> "{post title or excerpt}"
> {url}
news line format with the resolved title.Never strip the url even when embedding: users want the option to click through.
For complex queries the user may ask, you may need to compose multiple commands into one screen. Three reusable shapes:
When the user asks "before earnings on $X" or "$X earnings preview":
EARNINGS PREVIEW · $TICKER · ER {date} ({daysOut}d out)
────────────────────────────────────────────────────────
SETUP Sentiment {s30d} ({d30d} 30d) · Insiders {netInsider}
CONSENSUS EPS ${epsConsensus} (revised from ${epsPrior}) · Rev ${revC}
ESTIMATES Range ${epsLow}-${epsHigh}, {N} analysts
ACTIONS {U} upgrades, {D} downgrades (30d)
Recent: "{lastAction}"
THESIS {one-line synthesis: bullish/bearish/mixed}
Calls: /profile, /analyst/{T}/estimates, /analyst/{T}/actions?days=30, sentiment 30d, insider 60d.
When the user asks "what's happening in tech today" or "energy sector":
SECTOR · Technology · {date}
─────────────────────────────
MOOD {sectorScore} ({phase}) {weeklyChange} 7d
TOP MOVERS $T1 +X% $T2 +X% $T3 +X%
LAGGARDS $T4 -X% $T5 -X%
DRIVERS "{topInsight1}"
"{topInsight2}"
Calls: /market-mood (filter to sector), /insights/market (filter by sector tag), /stocks/popular (filter by sector if available).
When the user provides multiple tickers (e.g. "watch NVDA AMD INTC"):
WATCHLIST · {date}
───────────────────
$NVDA {price} {chgPct} Mood {s} {smartMoneyFlag}
$AMD {price} {chgPct} Mood {s} {smartMoneyFlag}
$INTC {price} {chgPct} Mood {s} {smartMoneyFlag}
The smartMoneyFlag is ↑ if any of (insider cluster buy, congress purchase, recent upgrade) hit in 7d; ↓ for the inverse; blank if neutral.
The "terminal feel" comes from being predictable. Every screen follows these rules.
$TICKER always. Uppercase, dollar-prefixed.+1.23%, -0.45%, never bare numbers.open fires 6 calls at once; don't await sequentially./institutional/quarters rarely changes.These are the failure modes the skill is designed to steer around.
(preview) in the corner if the user is on Free.open screen: it shows both.$NVDA $890.12 (+1.4%)). They don't get a 30-line open screen for a price quote./api/v1/options/flow, /api/v1/dark-pool, /api/v1/earnings, /api/v1/alerts, /api/v1/chat, /api/v1/congress (use /politicians).(preview) truncation is meaningfully limiting the answer.get_quote.Real shapes differ from what a naive reading of the endpoint URL might suggest. These cost time to discover the hard way; here they are upfront.
stocks/chart returns a bare ChartDataPoint[] at the top level, not { data: [...] }. Accept both shapes defensively:
const points = Array.isArray(raw) ? raw : (raw?.data ?? []);
Each point has both a timestamp (Unix ms) and a pre-formatted display date string. Read x-axis values from timestamp. Parsing the formatted date string falls back to the current year on some JS date parsers (e.g. Apr 6 becomes the current year instead of the year the bar belongs to).
entityMetrics/metrics returns ServingMetric[] where the scalar lives at metricValue.value.value (nested). Rank info, when present, is at metricValue.value.properties.{rank, percentile, totalStocks} or metricValue.properties.{rank, percentile, totalStocks}. Top-level value: number is a legacy fallback; handle it but don't rely on it.
function extractMetric(m) {
return m?.metricValue?.value?.value
?? m?.metricValue?.value
?? m?.metricValue
?? m?.value
?? null;
}
entityMetrics/distribution wraps the payload in distribution (singular), not distributions:
{ entityId, metricType, timestamp, dimension: "source",
distribution: { News: 37.4, Reddit: 37.4, X: 18.2, Substack: 7.0 } }
The values are share-of-voice percentages summing to roughly 100, not per-source sentiment scores. Render the breakdown as a "where this signal came from" panel, not as four sentiment bars.
stocks/getImages returns third-party CDN URLs that don't carry an embedded API key. Direct <img src> will 401/403. Wrap with the SentiSense anonymous image proxy:
https://app.sentisense.ai/api/v1/stocks/proxy-image?imageUrl=<encoded-url>
Story detail (documents/stories/:id) is a flat PublicStoryDetailDto, not the nested { cluster, entities, documents } you might expect. Inside aspectPerspectives[i], the fields bullishView and bearishView are structured objects (hook, risksOrCatalysts: string[], conclusion, confidence), not markdown strings. The top-level bullishView / bearishView ARE markdown strings. Same field names, different shapes. Type-check before calling string methods.
Chart timeframes accepted by stocks/chart: 1D / 5D / 1W / 1M / 3M / 6M / 1Y / ALL. Anything else falls back to 1M and logs a warning.
Sentiment, SentiSense Score, news clustering, AI summaries, and insights are batch metrics. Quote / price / chart points are real-time. Show the generatedAt timestamp on insight and AI summary surfaces so the user knows how fresh the analysis is. Don't claim "real time" on analytical surfaces.
For full schemas: https://sentisense.ai/skill.md.
PRICE GET /api/v1/stocks/price?ticker={T}
GET /api/v1/stocks/prices?tickers=A,B,C
GET /api/v1/stocks/chart?ticker={T}&timeframe=1M|3M|6M|1Y
GET /api/v1/stocks/{T}/profile
GET /api/v1/stocks/market-status
SENTIMENT GET /api/v2/metrics/entity/{T}/metric/sentiment?period=7d|30d|90d
GET /api/v2/market-mood
DOCUMENTS GET /api/v1/documents/ticker/{T}?limit=N (no titles)
GET /api/v1/documents/stories?limit=N (cluster.title is OURS, safe)
GET /api/v1/documents/stories?ticker={T}&limit=N
INSIDER GET /api/v1/insider/cluster-buys?lookbackDays=N
GET /api/v1/insider/trades/{T}?lookbackDays=N
CONGRESS GET /api/v1/politicians/activity?lookbackDays=N
GET /api/v1/politicians/member/{slug}/trades
INSTITUTIONAL GET /api/v1/institutional/quarters (always FIRST)
GET /api/v1/institutional/top-holdings/{T}?reportDate={Q}
ANALYST GET /api/v1/analyst/{T}/consensus
GET /api/v1/analyst/{T}/actions?days=N
GET /api/v1/analyst/{T}/estimates
GET /api/v1/analyst/market/activity?days=N&types=UPGRADE,DOWNGRADE
INSIGHTS GET /api/v1/insights/stock/{T}/latest (quota-counted)
GET /api/v1/insights/stock/{T}/types
GET /api/v1/insights/market
MARKET GET /api/v1/market-summary
Wrap unwrap: most preview-gated endpoints return { isPreview, previewReason, data }. Iterate data.
A cold ticker dashboard typically fans out 10 to 12 API calls on first load:
| Surface element | Calls |
|---|---|
| Profile + logo | 1 to 2 |
| Quote (header) | 1 (plus refresh interval) |
| Chart | 1 |
| Metrics panel (4 metrics in parallel) | 4 |
| Recent stories | 1 |
| Peers (similar + batch prices) | 2 |
| Cold total | 10 to 12 |
A grounded chat turn adds another 1 to 4 calls on top, depending on the tool ladder depth.
Mitigations:
Promise.allSettled for parallel widget fetches; widgets render independently as data arrives. Don't gate the page on the slowest call.Rate limits. Per-minute limits exist (Free 15 req/min, PRO 120 req/min). Exceeding them returns 429 with a Retry-After hint. Surface 429s gracefully ("rate limit reached, retrying in N seconds") rather than silently retrying or rendering a stale value.
Per-stock budget mental model. A heavy daily-driver visiting 15 tickers and chatting on each (~15 grounded turns × 3 calls) eats ~225 calls per session, comfortably under the PRO daily envelope. A light user (3 tickers per day, no chat) uses ~50. Plan defaults around that envelope so a typical user never sees a 429 in normal use.
| Command | Free | PRO |
|---|---|---|
| open | Full screen, AI insight quota-counted | Unlimited |
| compare | Two tickers anytime | Unlimited |
| daily brief | Once per day comfortably (5 calls) | Unlimited refreshes |
| screen smart-money | Top items per bucket | Full ranked lists |
| flow | Preview slice | Full holder list, full history |
| mood | Full data, no quota cost | Same |
| news | 8 most recent | Full feed |
| stories | 10 most recent | Full feed |
PRO at $15/month: https://sentisense.ai/pricing