Stock Sentiment Skill
The sentiment and smart-money layer for US equities. A quote skill tells you the price; this skill reads what the market feels about a stock (the SentiSense Score, sentiment polarity, mentions, share of voice), where the smart money is moving (insider, congressional, and institutional flows plus analyst actions), and what the AI read of the tape is (per-stock and market-wide insights, sentiment-tagged news), all through the read-only SentiSense API.
Read-only educational data interface. Output is informational context, never a personalized buy or sell recommendation.
When to Use
Reach for this skill when the question is about perception, positioning, or signal rather than raw price:
- "What is the sentiment on $NVDA?" or "Is the mood on $TSLA bullish or bearish?"
- "What is the smart money doing this week?" (insider cluster-buys, congressional trades, 13F flows, and analyst upgrades converging on the same tickers).
- "What is the overall market mood today, fear or greed?"
- "Is sentiment diverging from price on $COIN?" (price up while sentiment falls, or the reverse).
- "What is the pre-earnings sentiment setup on $AAPL?"
- "What is the AI insight on $MSFT, and what are people saying in the news?"
For "does this change my thesis?", hand off to the stocks-analysis skill when available. Pass the ticker, user thesis, horizon, and dated signal disagreements. Return an evidence-led bull/bear assessment and unresolved objections. Hand off only when the user changes the question; do not automatically route back. If the sibling is unavailable, answer the supported part here using a connected tool or the inline REST workflow, state any remaining gap, and never require an install.
Do not use it for order entry, portfolio management, or personalized advice. It has no write, trading, or wallet surface; every endpoint is a GET.
Prerequisites
- Python 3.8+ using only the standard library (
urllib,json); no third-party packages required. Any HTTP client or plaincurlworks too. On macOS python.org installs the client can raiseCERTIFICATE_VERIFY_FAILED(missing CA certs): run the bundledInstall Certificates.command, use the system/usr/bin/python3, or usecurl(which uses the system trust store). - A free
SENTISENSE_API_KEY. Get one at https://app.sentisense.ai/get-api-key. The key is required on every call; anonymous requests return401 api_key_required. - Network access to
https://app.sentisense.ai. - Read-only scope. Every endpoint here is a GET. Nothing this skill does can place a trade, move money, or modify account state.
Permissions
- Network: HTTPS to app.sentisense.ai only.
- Credentials: SENTISENSE_API_KEY from the environment.
- Shell: none required.
- Files: none.
Tiers:
| Tier | Quota | Rate |
|---|---|---|
| Free | 1,000 requests/month | 30 requests/min |
| PRO ($15/mo) | Unlimited | 300 requests/min |
The free tier exercises every workflow below. Preview-gated endpoints return a truncated but real slice on a free key (for example the top 3 insights); PRO removes the monthly cap and returns full history and full lists.
How to Run
This skill is invoked through the agent's terminal or shell tool: issue HTTP GET requests to the SentiSense API and synthesize the JSON into a concise, sourced answer. The base URL is https://app.sentisense.ai. Authenticate every request with the X-SentiSense-API-Key header; keep the key in the shell environment and never place it in a query string or in user-facing output.
Identify your client. Send a User-Agent naming your agent runtime and this skill, for
example OpenClaw/1.4 (stock-sentiment) or ClaudeCode/2.1 (stock-sentiment). Substitute your own runtime and
version if neither matches. You can also volunteer what your agent is called by adding an
agent/<your-agent-name> token inside the same parentheses, as in
OpenClaw/1.4 (stock-sentiment; agent/research-desk). All of it is optional, and it is what tells
us this skill has real integrations behind it, so it gets prioritized and you get notice before it
changes.
curl -s -H "X-SentiSense-API-Key: $SENTISENSE_API_KEY" \
"https://app.sentisense.ai/api/v2/metrics/entity/NVDA/metric/sentiment"
An anonymous call returns 401 api_key_required. A rate-limited call returns 429 with a Retry-After header; back off for the indicated seconds rather than retrying immediately or serving a stale value.
On Windows, use the bundled Python client (cross-platform) and reference the key as %SENTISENSE_API_KEY% (cmd) or $env:SENTISENSE_API_KEY (PowerShell) rather than the POSIX $SENTISENSE_API_KEY shown above.
The REST recipe in this file is the primary path. A maintained command-line client is available as the separate sentisense-cli skill for hosts that prefer one.
Two response envelopes exist; unwrap correctly before reading fields:
- Read FLAT (top-level, no
.data):stocks/price,stocks/prices,stocks/chart,stocks/popular,stocks/{T}/profile,market-mood, and the metric series (sentiment,sentisense,mentions, andsocial_dominanceare bare arrays).institutional/quartersis also a bare array. - Read WRAPPED as
{ isPreview, previewReason, data }(use.data):insider/*,politicians/*,institutional/holders,analyst/*,insights/*, andcalendar/earnings(heredatais a dict, so readdata.earnings[]). documents/tickerhas its own shape{ documents, totalCount }; read.documents[].
When unsure, accept both: rows = raw if isinstance(raw, list) else raw.get("data", raw).
An optional stdlib helper, scripts/sentiment_client.py, wraps all of this: it injects the auth header, prepends the base URL, and normalizes both envelopes (reading the metric scalar from the flat value) so the agent reasons over clean values. Use it or plain curl, whichever fits the host. The core of the helper is small enough to inline:
#!/usr/bin/env python3
"""Minimal stdlib client for the read-only SentiSense API."""
import json, os, urllib.parse, urllib.request
API_ORIGIN = "https://app.sentisense.ai"
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
def sentisense_api_url(path, params=None):
url = urllib.parse.urljoin(API_ORIGIN + "/", path)
parsed = urllib.parse.urlparse(url)
if (parsed.scheme != "https" or parsed.hostname != "app.sentisense.ai"
or parsed.netloc != "app.sentisense.ai"
or parsed.username is not None or parsed.password is not None
or parsed.port is not None):
raise ValueError("API URL must use https://app.sentisense.ai with no credentials or port")
if params:
url += ("&" if parsed.query else "?") + urllib.parse.urlencode(params)
return url
def get(path, **params):
url = sentisense_api_url(path, params)
req = urllib.request.Request(
url, headers={"X-SentiSense-API-Key": os.environ["SENTISENSE_API_KEY"]})
with urllib.request.build_opener(NoRedirect).open(req, timeout=20) as r:
return json.load(r)
def rows(raw):
"""Wrap-vs-flat: some endpoints return a bare array, others {isPreview, data}."""
if isinstance(raw, list):
return raw
if isinstance(raw, dict) and "data" in raw:
return raw["data"]
return raw
def latest_metric(ticker, slug="sentiment"):
"""Latest reading of any metric series. Read the flat top-level `value`: it is present
on every point and holds the scalar, while the nested metricValue is a dict for value
metrics and a bare number for count metrics like `mentions`."""
series = get(f"/api/v2/metrics/entity/{ticker}/metric/{slug}")
if not series or series[-1].get("value") is None:
return None
return float(series[-1]["value"])
python scripts/sentiment_client.py sentiment NVDA
python scripts/sentiment_client.py mood
Quick Reference
All paths are relative to https://app.sentisense.ai and are GET. Every call requires the X-SentiSense-API-Key header. {T} is an uppercase ticker, {slug} a member slug, {id} a story id. Full schema: https://sentisense.ai/skill.md.
RESOLVE A NAME (only when the user typed a company or fund name, not a symbol)
GET /api/v1/kb/entities/search?q={name}&type=company&limit=5
Bare array of {name, urlSlug, type, ticker}, best match first. Take the first match with a
non-null ticker. Use type=etf for fund names (SPY resolves only there). See Pitfalls.
SENTIMENT & MOOD
GET /api/v2/metrics/entity/{T}/metric/sentiment?startTime={epochMs}&endTime={epochMs}
Sentiment polarity time series. Omit params for the server default 7-day window.
Bare array; latest scalar is series[-1].value (a float in [-1, 1]). Every series
below reads the same way: take the flat top-level value, not the nested metricValue,
whose depth differs between value metrics and count metrics.
GET /api/v2/metrics/entity/{T}/metric/sentisense
The SentiSense Score (unbounded composite; report as-is, never normalize to 0-100).
GET /api/v2/metrics/entity/{T}/metric/mentions
Mention-volume time series (how much a ticker is being talked about).
GET /api/v2/metrics/entity/{T}/metric/social_dominance
Share-of-conversation time series (a ticker's dominance of the chatter).
GET /api/v2/metrics/entity/{E}/metric/app_review_count
New Apple App Store reviews per day for a product's iOS app. Products with a
tracked app only, so {E} is a product entity slug, never a ticker.
GET /api/v2/metrics/entity/{E}/metric/app_rating
Mean star rating (1 to 5) of that day's new App Store reviews. Same coverage.
GET /api/v2/market-mood
Composite fear/greed plus sub-signals and per-sector breakdowns. Flat, but the
composite is nested: market.currentScore, market.phase, market.weeklyChange,
market.signals[]; sectors.{SectorName}.{currentScore, phase, weeklyChange}.
SMART MONEY (wrapped in {isPreview, previewReason, data}; free key returns a preview slice)
GET /api/v1/insider/cluster-buys?lookbackDays=N Tickers with multiple insider buys.
GET /api/v1/insider/trades/{T}?lookbackDays=N Form 4 rows; transactionType BUY|SELL,
raw SEC letter in transactionCode.
GET /api/v1/politicians/activity?lookbackDays=N Congressional trades; PURCHASE|SALE.
GET /api/v1/politicians/filings/{T}?lookbackDays=N Per-ticker congressional filings.
GET /api/v1/politicians/member/{slug} Member profile (data.recentTrades[]).
GET /api/v1/institutional/quarters Call FIRST; bare array. Use reportDate of first entry whose pending is not true; skip pending:true (fall back to [0] only if all pending).
GET /api/v1/institutional/holders/{T}?reportDate={Q} Top 13F holders (data.holders[], largest first).
GET /api/v1/analyst/{T}/consensus Price-target band; data IS the consensus object (data.consensusLabel).
GET /api/v1/analyst/{T}/actions?lookbackDays=N Recent rating changes for one ticker.
GET /api/v1/analyst/{T}/estimates EPS band at data.estimates[0].{estimateLow/Mean/High,
numberOfAnalysts} + data.surprises[]; no revenue.
GET /api/v1/analyst/activity?lookbackDays=N Market-wide actions; add &actionTypes=UPGRADE,DOWNGRADE,INITIATE
for real rating changes (~83% of raw rows are REITERATE).
AI INSIGHTS (wrapped; batch, carry generatedAt)
GET /api/v1/insights/stock/{T} Per-stock signals ranked by importance; data[0].insightText is the headline. Free preview top 3.
GET /api/v1/insights/stock/{T}/types Available insight types for the ticker; bare string array.
GET /api/v1/insights/market Top market-wide signals (data[], insightText; ticker embedded in insightText).
NEWS & STORIES
GET /api/v1/documents/ticker/{T}?limit=N Sentiment-tagged feed ({documents, totalCount}); each doc
has url, source, sourceName, published (epoch seconds), averageSentiment; no title.
GET /api/v1/documents/stories?limit=N Pre-clustered stories; cluster.title is SentiSense-authored and safe to show.
GET /api/v1/documents/stories/ticker/{T}?limit=N Stories for one ticker.
GET /api/v1/documents/stories/{id} Story detail (PublicStoryDetailDto; aspectPerspectives[], bullishView/bearishView).
GET /api/v1/documents/search?query=... Topical document search.
SUPPORTING (price, prices, chart are 15-minute delayed; profile, popular, calendar, market-summary are reference or batch)
GET /api/v1/stocks/price?ticker={T} Flat (no wrapper): currentPrice, changePercent at root.
GET /api/v1/stocks/prices?tickers=A,B,C Batch quotes.
GET /api/v1/stocks/{T}/profile name, sector, industry (flat at root; no profile key).
GET /api/v1/stocks/chart?ticker={T}&timeframe=1D|5D|1W|1M|3M|6M|1Y|5Y|10Y|MAX Bars; read each point's timestamp (Unix ms). Invalid timeframe returns 400.
GET /api/v1/stocks/popular Bare array of ~75 ticker strings (screen universe).
GET /api/v1/calendar/earnings?ticker={T} data.earnings[]; next date + consensus EPS + confirmed.
GET /api/v1/market-summary Market-wide narrative headline.
Sentiment is polarity: a float in [-1, 1] where the sign is the direction (negative is bearish and meaningful, positive is bullish) and the magnitude is conviction. Represent the sign unmistakably; do not map it onto a 0-100 scale. The SentiSense Score is a separate, unbounded composite; report it as-is. Mentions and social dominance are their own metric series on the same /metric/{metricType} endpoint (mentions for talk volume, social_dominance for share of the conversation); all four series (sentiment, sentisense, mentions, social_dominance) are available on the Free tier, and like every metrics call each request counts against your monthly quota. Two more Free-tier series cover consumer products rather than tickers: app_review_count (new Apple App Store reviews for a product's iOS app that day) and app_rating (the mean star rating, 1 to 5, of those reviews). From 2026-09-05 mentions excludes App Store reviews, because a review is a rating rather than chatter and for review-heavy products the two read as one number; the App Store slice still appears under distribution/mentions?dimension=source, so read review volume from app_review_count. A separate /api/v2/metrics/entity/{T}/distribution/{metricType} endpoint breaks a metric down by source (share of voice, a "where this signal came from" view, not per-source sentiment values).
Workflows
Opinionated recipes. Each fans out its independent calls in parallel, then synthesizes; none recommends buying or selling. Frame every result as educational context on positioning and mood.
1. Sentiment read on a ticker
Answer "what is the market feeling about $T" in a few dense lines. Fire these in parallel:
GET /api/v2/metrics/entity/{T}/metric/sentimentfor the polarity trend (server default 7-day window; the latest scalar isseries[-1].value, a float in [-1, 1]).GET /api/v2/metrics/entity/{T}/metric/sentisensefor the composite score.GET /api/v1/documents/ticker/{T}?limit=8for mention volume (totalCount) and the sentiment-tagged feed.GET /api/v1/insights/stock/{T}for the top AI insight (data[0].insightText, withgeneratedAtfor freshness).
Synthesize as educational context, leading with the differentiated sentiment read, not the price: "$NVDA sentiment +0.42 over 7d and rising; SentiSense Score elevated; mention volume heavy; latest AI insight: 'Data-center demand commentary firming' (as of the batch time)." Show the generatedAt age so the reader knows these are batch metrics.
2. Market mood (fear and greed)
Answer "what is the overall market mood today."
GET /api/v2/market-mood.
The response is flat, but the composite is nested under market, not the root: market.currentScore, market.phase (e.g. Fear, Neutral, Optimism, Greed), market.weeklyChange, and market.signals[] (each sub-gauge with its value and change). Per-sector readings live at sectors.{SectorName}.{ currentScore, phase, weeklyChange }; sectors is a string-keyed dict, not an array, and its GICS labels have historically overlapped (Technology alongside Information Technology, Healthcare alongside Health Care), so treat the pairs defensively: if both members of a pair appear in one response, dedupe them before ranking top and bottom sectors. A clean response with neither pair duplicated is the common case and needs no special handling. Report as context: "Market mood 62 (Greed), +4 over the week. Greed leaders: Technology, Communications. Fear: Energy, Utilities." Optionally pair with GET /api/v1/market-summary for the narrative headline and GET /api/v1/insights/market for the top market-wide signals.
3. Smart-money convergence screen
Find tickers where insider buying, congressional purchases, and analyst upgrades line up in the same window; convergence is the signal a quote feed cannot produce.
GET /api/v1/insider/cluster-buys?lookbackDays=30.GET /api/v1/politicians/activity?lookbackDays=30, keeping rows withtransactionType == "PURCHASE".GET /api/v1/analyst/activity?lookbackDays=30&actionTypes=UPGRADE(server-side filter; also accepts a CSV likeUPGRADE,DOWNGRADE,INITIATE).
All three are wrapped: read .data. Intersect the three ticker lists and report names appearing in two or more buckets, ranked by total signal count, with a one-liner each: "$NVDA: 4 insiders bought, 1 congressional purchase, 2 analyst upgrades (30d)."
Start this one at lookbackDays=30, not 7. A 7-day window is too narrow for three slow feeds to overlap: on a representative run it returned 1 cluster-buy ticker, 1 congressional purchase ticker and 21 upgraded tickers, which intersected to zero names in two or more buckets. The same three calls at 30 days returned 8, 65 and 45 tickers and produced 7 convergent names. The trap is that no individual bucket was empty at 7 days, so an "is this bucket empty" check passes on all three and you still report nothing found. Widen when the INTERSECTION is thin, not when a bucket is empty, and say which window you used. Also expect the three-way overlap to be empty even at 30 days: two-of-three is the working bar for this screen, and requiring all three will show a blank almost every time. A genuinely empty bucket (quiet week, disclosure lag) is isPreview:false and not an error either way. For one ticker's full flow, run insider/trades/{T}, politicians/filings/{T}, institutional/quarters then institutional/holders/{T}?reportDate={Q}, and analyst/{T}/actions. Present as observed positioning, never as advice.
4. Pre-earnings sentiment check
Read the sentiment and positioning into an earnings print.
GET /api/v1/calendar/earnings?ticker={T}for the next report date and consensus (data.earnings[0].earningsDate,confirmed); an empty response means the name is outside the forward window, so ask the user for the date instead of guessing.GET /api/v2/metrics/entity/{T}/metric/sentiment?startTime={now-30d}&endTime={now}(epoch milliseconds) for the 30-day sentiment trend.GET /api/v1/insider/trades/{T}?lookbackDays=60for recent insider activity (transactionTypeBUY or SELL), droppingtransactionCode == "F"rows before you call anything selling (see the code-F note below).GET /api/v1/analyst/{T}/estimatesfor the EPS band andsurprises[]beat/miss history.GET /api/v1/analyst/{T}/actions?lookbackDays=30for recent rating changes.GET /api/v1/insights/stock/{T}for the current AI read.
Synthesize the setup as educational context: "$AAPL earnings in 5d: sentiment +0.22 over 30d and trending up; insiders net sellers (2 sells, 0 buys); EPS consensus $1.52 (range $1.48 to $1.55, 28 analysts), beat in 3 of the last 4 quarters; 3 upgrades in 30d. Setup reads mixed-to-constructive." Do not tell the user how to trade the print.
5. Sentiment-versus-price divergence
Surface names where perception and price disagree; a bullish gap (price down, sentiment up) and a bearish gap (price up, sentiment down) are the two shapes of interest.
GET /api/v1/stocks/popularfor the candidate list.- For each candidate, in parallel:
GET /api/v1/stocks/chart?ticker={T}&timeframe=1M(a bare array of intraday bars; filter totimestamp >= now-7dand compare the first versus last bar for the 7-day move) andGET /api/v2/metrics/entity/{T}/metric/sentiment(server default 7-day window; measure the trend across the returned series). - Rank by the absolute gap between the price move and the sentiment move; report the top few in each direction.
Frame the result as an observed divergence, not a signal to act: "Bullish divergence: $TSLA price -8% while sentiment +0.11 over 7d. Bearish divergence: $COIN price +14% while sentiment -0.09." Keep the delayed price and the batch sentiment labeled with their own freshness; do not blend them into one implied "now."
Pitfalls
- Company names are not tickers. When the user names the company ("sentiment on tesla", "is the mood on alphabet bullish") instead of typing a symbol, resolve it first with
GET /api/v1/kb/entities/search?q={name}&type=company&limit=5: a bare array of{name, urlSlug, type, ticker}, best match first (type=etffor a fund, sinceSPYresolves only there). Take the first match with a non-nullticker; a tracked subsidiary or private company can outrank its listed parent ("google" returns Google LLC withticker: nullbefore AlphabetGOOGL). Several plausible ticker-bearing matches means ask a one-line clarification; an empty array means say so. Never uppercase the word and hope:$TESLAfails the metric series with404 entity_not_found(that error carries up to threesuggestions, which is a resolution hint, not data), while the smart-money feeds return an emptydata: []that reads like a quiet name when the real failure was the identifier. An exact ticker the user typed skips this step, and one resolution call per name covers the whole session. - Nothing here is real time. Sentiment, the SentiSense Score, mentions, share of voice, news clustering, and AI insights are batch metrics computed on a schedule; quote, price, and chart points are the fresher class but carry a 15-minute delay. State a batch value with its
generatedAtage, annotate price withpriceAsOfwhere present, and never label either "real time." - Empty smart-money windows are normal. The 7-day insider and congressional feeds often return empty arrays on quiet weeks (disclosure lag,
isPreview:false, not an error). Widen that specific call tolookbackDays=30and note the wider window rather than showing a blank result. - Preview gating is data, not failure. On the free tier, preview-gated endpoints return
isPreview:truewith a real truncated slice (for example the top 3 insights, the current earnings week, a sliced holder list). Render the slice as the answer and tag it(preview). Mention PRO only when the truncation is materially limiting the answer. - Wrap versus flat differs by endpoint. Reading
.dataon a flat endpoint (or the reverse) yields nothing. Flat:stocks/price,stocks/prices,stocks/chart,stocks/popular,stocks/{T}/profile,market-mood, thesentiment,sentisense,mentions, andsocial_dominanceseries, andinstitutional/quarters. Wrapped under.data:insider/*,politicians/*,institutional/holders,analyst/*,insights/*, andcalendar/earnings. When unsure, accept both. - Read the metric scalar from the flat
value. Every point in a metric series carries a top-levelseries[i].valuealongside the nestedmetricValue, and it holds the reading: the polarity forsentiment, the composite forsentisense, the count formentions, the share forsocial_dominance. Prefer it, because the nested depth is not the same for every metric. A value metric (sentiment,sentisense,social_dominance) nests atmetricValue.value.valuebecausemetricValue.valueis itself a dict; a count metric (mentions) is{"type":"CountMetricValue","value":36,"count":36}, sometricValue.valueis already the integer andmetricValue.value.valuethrows. The flat field spares you the branch. A point with no reading omitsvalue; skip that point rather than reading it as zero. - Congress and insider use different verbs. Insider rows carry
transactionTypeBUY or SELL; congressional rows carry PURCHASE or SALE. Filter each with its own vocabulary. - Not every insider SELL is a sale.
transactionTypeis a simplified rollup of the SEC's one-letter codes, and codeFlands onSELL: those are shares the company withheld to cover the insider's taxes when a grant vested. Nobody chose to sell and no shares reached the market. On companies that grant heavily this is the majority of the reported "sold" dollars, so a bearish read built on a rawSELLfilter is describing a vesting schedule. ReadtransactionCodeand dropFbefore you tally selling. The market-wide/insider/activityrollup already excludes it for you;/insider/trades/{T}returns every filed row, so there you filter yourself. - Always fetch quarters first. Call
institutional/quartersand pass thereportDateof the first quarter whosependingis not true toinstitutional/holders; skip anypending:trueentry (within ~45 days of a quarter close the most-recent quarter is still filing and holds almost no holders), and fall back to[0]only if every entry ispending:true. Never hardcode a quarter. - Documents carry no article title. The document feed returns URLs,
source,published(epoch seconds), andaverageSentiment, not the publisher's headline. Pre-clustered story titles (cluster.title) are SentiSense-authored and safe to display verbatim; prefer stories when a readable title is needed. - No invented endpoints. There is no real-time options order flow and no dark pool (options exist, but only as end-of-day analytics at
/api/v1/options/overviewand/api/v1/stocks/{ticker}/options/summary), and no/congress(congressional data lives under/politicians). The earnings calendar is/api/v1/calendar/earnings. - No advice. When asked "should I buy," return data-grounded synthesis (sentiment, smart-money flow, analyst consensus, AI insight) framed as educational context, not a personal recommendation.
Verification
Confirm the skill is wired correctly before trusting a synthesis:
- Reachability and auth. Every endpoint here takes an API key, so one call checks both:
curl -s -o /dev/null -w "%{http_code}" -H "X-SentiSense-API-Key: $SENTISENSE_API_KEY" "https://app.sentisense.ai/api/v2/market-mood". A200confirms the base URL, the network, the header and the key. A401 api_key_requiredmeans the header orSENTISENSE_API_KEYis missing; a401 invalid_api_keymeans the key itself is wrong or revoked; a429means the per-minute rate was exceeded, so honor theRetry-Afterhint. - Sentiment parses. Fetch
/api/v2/metrics/entity/AAPL/metric/sentiment, confirm a non-empty array, and readseries[-1].value; it should be a float in [-1, 1]. A value outside that range means the wrong field was read. - Mood nests as expected. Fetch
/api/v2/market-moodand confirmmarket.currentScore,market.phase, andmarket.weeklyChangeare present (not at the root), and thatsectorsis a populated dict. - Envelope check. Confirm
institutional/quartersparses as a bare array andinsider/cluster-buys?lookbackDays=30parses as{ isPreview, data }withdataan array (an empty array on a quiet window is a valid result, not a failure). - Freshness is surfaced. Any batch value presented to the user carries its
generatedAt; if a synthesis omits the age on a sentiment or insight figure, or describes a batch surface as real time, it is not verified.
A run passes when every quoted number traces to a 200 response read this turn, batch and delayed-price surfaces are labeled distinctly with their own ages, and the output reads as educational context rather than a recommendation.
Use & Disclaimer
This skill is an educational data interface to SentiSense's read-only Data API. 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. Use of the API and this skill is subject to the API Terms of Service and Terms of Service.
Install: npx skills add SentiSenseApp/skills (add -s stock-sentiment for just this skill).