sentisense 0.45.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -184
- package/dist/cli.cjs +84 -12
- package/dist/index.cjs +69 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +213 -3
- package/dist/index.d.ts +213 -3
- package/dist/index.mjs +69 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,25 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/sentisense)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
Official JavaScript/TypeScript SDK for the [SentiSense](https://sentisense.ai) market intelligence API.
|
|
6
|
+
Official JavaScript/TypeScript SDK and CLI for the [SentiSense](https://sentisense.ai) market intelligence API: stock prices, news and social sentiment, the SentiSense Score, insider and congressional trading, institutional 13F flows, options positioning, analyst ratings, earnings analysis, and a cross-signal screener.
|
|
7
|
+
|
|
8
|
+
- Full TypeScript support with detailed type definitions
|
|
9
|
+
- Works in Node.js 18+, Deno, Bun, and browsers
|
|
10
|
+
- Zero runtime dependencies (native `fetch`)
|
|
11
|
+
- Namespaced resources (`stocks`, `documents`, `institutional`, ...) and a typed error hierarchy
|
|
12
|
+
- A complete command line interface in the same package, runnable via `npx` with nothing to install
|
|
13
|
+
|
|
14
|
+
Get a free API key at [app.sentisense.ai/get-api-key](https://app.sentisense.ai/get-api-key). Full API docs at [sentisense.ai/docs/api](https://sentisense.ai/docs/api).
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- [Install](#install)
|
|
19
|
+
- [Quick start](#quick-start)
|
|
20
|
+
- [The CLI](#the-cli)
|
|
21
|
+
- [Configuration](#configuration)
|
|
22
|
+
- [Response shapes](#response-shapes)
|
|
23
|
+
- [API reference](#api-reference)
|
|
24
|
+
- [Error handling](#error-handling)
|
|
7
25
|
|
|
8
26
|
## Install
|
|
9
27
|
|
|
@@ -11,7 +29,7 @@ Official JavaScript/TypeScript SDK for the [SentiSense](https://sentisense.ai) m
|
|
|
11
29
|
npm install sentisense
|
|
12
30
|
```
|
|
13
31
|
|
|
14
|
-
## Quick
|
|
32
|
+
## Quick start
|
|
15
33
|
|
|
16
34
|
```typescript
|
|
17
35
|
import SentiSense from "sentisense";
|
|
@@ -26,7 +44,7 @@ console.log(price.currentPrice);
|
|
|
26
44
|
const flows = await client.institutional.getFlows();
|
|
27
45
|
```
|
|
28
46
|
|
|
29
|
-
## CLI
|
|
47
|
+
## The CLI
|
|
30
48
|
|
|
31
49
|
The same package ships a command line tool. Nothing to install:
|
|
32
50
|
|
|
@@ -44,8 +62,6 @@ npx -y sentisense@latest auth <your key>
|
|
|
44
62
|
npx -y sentisense@latest health
|
|
45
63
|
```
|
|
46
64
|
|
|
47
|
-
Get a key at [app.sentisense.ai/get-api-key](https://app.sentisense.ai/get-api-key).
|
|
48
|
-
|
|
49
65
|
### Commands
|
|
50
66
|
|
|
51
67
|
| Command | What you get |
|
|
@@ -67,7 +83,7 @@ Get a key at [app.sentisense.ai/get-api-key](https://app.sentisense.ai/get-api-k
|
|
|
67
83
|
|
|
68
84
|
Run `sentisense help <command>` for its flags and examples.
|
|
69
85
|
|
|
70
|
-
### Output
|
|
86
|
+
### Output modes
|
|
71
87
|
|
|
72
88
|
Readable in a terminal, plain text when piped, and exact API JSON on request:
|
|
73
89
|
|
|
@@ -77,40 +93,13 @@ npx -y sentisense@latest quote NVDA | cat # plain text, no escape codes
|
|
|
77
93
|
npx -y sentisense@latest quote NVDA --json | jq # the API response untouched
|
|
78
94
|
```
|
|
79
95
|
|
|
80
|
-
`--json` prints what the API returned, envelope and all, so `isPreview` and `totalCount` stay
|
|
81
|
-
visible. For `quote` that is the exact quote response for one ticker, and an object keyed by
|
|
82
|
-
ticker for several. `--full` widens any command. `--no-color` and `NO_COLOR` drop the colour,
|
|
83
|
-
`--plain` and `--pretty` force a layout, and `--debug` prints stack traces.
|
|
84
|
-
|
|
85
|
-
Commands spend requests on the answer, not on decoration: `quote` looks up the company name
|
|
86
|
-
only for the terminal layout, so piped and `--json` output cost one request per ticker. When
|
|
87
|
-
something supplementary does not come back, such as the Score history behind a sparkline, the
|
|
88
|
-
command still prints its answer and exits 0 with a `note:` line on stderr, so stdout stays
|
|
89
|
-
clean for a pipe and the gap is never silent.
|
|
90
|
-
|
|
91
|
-
### Saying who is calling
|
|
96
|
+
`--json` prints what the API returned, envelope and all, so `isPreview` and `totalCount` stay visible. For `quote` that is the exact quote response for one ticker, and an object keyed by ticker for several. `--full` widens any command. `--no-color` and `NO_COLOR` drop the colour, `--plain` and `--pretty` force a layout, and `--debug` prints stack traces.
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
slug of the skill driving it), requests carry that identity, so usage can be understood and
|
|
95
|
-
the tools improved. Both are optional, never required, and nothing is inferred when they are
|
|
96
|
-
absent.
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
export SENTISENSE_AGENT_NAME=research-desk
|
|
100
|
-
export SENTISENSE_SKILL=stock-analysis
|
|
101
|
-
npx -y sentisense@latest quote NVDA
|
|
102
|
-
# User-Agent: sentisense-node/0.44.0 sentisense-cli/0.44.0 (stock-analysis; agent/research-desk)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Either can also be a flag (`--agent`, `--skill`) or a stored setting
|
|
106
|
-
(`sentisense auth --agent research-desk --skill stock-analysis`), resolved flag first, then
|
|
107
|
-
environment, then config. Values are reduced to letters, digits, dot, underscore and hyphen,
|
|
108
|
-
and capped at 32 characters, so nothing you set can reshape the header.
|
|
98
|
+
Commands spend requests on the answer, not on decoration: `quote` looks up the company name only for the terminal layout, so piped and `--json` output cost one request per ticker. When something supplementary does not come back, such as the Score history behind a sparkline, the command still prints its answer and exits 0 with a `note:` line on stderr, so stdout stays clean for a pipe and the gap is never silent.
|
|
109
99
|
|
|
110
100
|
### Exit codes
|
|
111
101
|
|
|
112
|
-
Failures print two lines to stderr, what went wrong and what to do about it, and exit with a
|
|
113
|
-
code you can branch on. The CLI does not retry, so a 5 is yours to handle.
|
|
102
|
+
Failures print two lines to stderr, what went wrong and what to do about it, and exit with a code you can branch on. The CLI does not retry, so a 5 is yours to handle.
|
|
114
103
|
|
|
115
104
|
| Code | Meaning |
|
|
116
105
|
|------|---------|
|
|
@@ -122,30 +111,52 @@ code you can branch on. The CLI does not retry, so a 5 is yours to handle.
|
|
|
122
111
|
| 5 | Rate limited |
|
|
123
112
|
| 6 | Network failure or timeout |
|
|
124
113
|
|
|
114
|
+
### Saying who is calling
|
|
115
|
+
|
|
116
|
+
If you set `SENTISENSE_AGENT_NAME` (what your agent is called) and `SENTISENSE_SKILL` (the slug of the skill driving it), requests carry that identity, so usage can be understood and the tools improved. Both are optional, never required, and nothing is inferred when they are absent.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
export SENTISENSE_AGENT_NAME=research-desk
|
|
120
|
+
export SENTISENSE_SKILL=us-stocks-analysis
|
|
121
|
+
npx -y sentisense@latest quote NVDA
|
|
122
|
+
# User-Agent: sentisense-node/{version} sentisense-cli/{version} (us-stocks-analysis; agent/research-desk)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Either can also be a flag (`--agent`, `--skill`) or a stored setting (`sentisense auth --agent research-desk --skill us-stocks-analysis`), resolved flag first, then environment, then config. Values are reduced to letters, digits, dot, underscore and hyphen, and capped at 32 characters, so nothing you set can reshape the header.
|
|
126
|
+
|
|
125
127
|
Research data, not investment advice.
|
|
126
128
|
|
|
127
|
-
##
|
|
129
|
+
## Configuration
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
```typescript
|
|
132
|
+
const client = new SentiSense({
|
|
133
|
+
apiKey: process.env.SENTISENSE_API_KEY, // Get yours at app.sentisense.ai/get-api-key
|
|
134
|
+
baseUrl: "https://...", // Default: https://app.sentisense.ai
|
|
135
|
+
timeout: 30000, // Default: 30s (in milliseconds)
|
|
136
|
+
maxRetries: 3, // Default: 3
|
|
137
|
+
userAgentSuffix: "my-bot/1.4", // Default: none
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
| Option | Default | What it does |
|
|
142
|
+
|--------|---------|--------------|
|
|
143
|
+
| `apiKey` | none | Sent as `X-SentiSense-API-Key`. Required by every endpoint. |
|
|
144
|
+
| `baseUrl` | `https://app.sentisense.ai` | Override for a non-production host. |
|
|
145
|
+
| `timeout` | `30000` | Per-request timeout in milliseconds. |
|
|
146
|
+
| `maxRetries` | `3` | Retries on 429 and 5xx, honouring `Retry-After`. Set `0` to fail fast. |
|
|
147
|
+
| `userAgentSuffix` | none | Appended to the User-Agent, after `sentisense-node/{version}`. |
|
|
148
|
+
|
|
149
|
+
`userAgentSuffix` is how you say what is calling on top of the SDK, so your traffic is legible in your own logs and in ours. A tool name and version works (`"my-bot/1.4"`), optionally with an agent label (`"my-bot/1.4 agent/research-desk"`). Node only, since browsers set the header themselves. Newlines are collapsed and an empty value is ignored.
|
|
150
|
+
|
|
151
|
+
Keep the key in the environment rather than in source. Committing a literal key leaks it into git history and into every registry security scan that reads your repo.
|
|
134
152
|
|
|
135
153
|
## Response shapes
|
|
136
154
|
|
|
137
|
-
Most methods resolve to the payload directly, but two families wrap it. The return types
|
|
138
|
-
describe the wrapper, so `.data` / `.documents` type-check natively, no cast.
|
|
155
|
+
Most methods resolve to the payload directly, but two families wrap it. The return types describe the wrapper, so `.data` / `.documents` type-check natively, no cast.
|
|
139
156
|
|
|
140
|
-
**1. Tier-gated endpoints return a preview envelope.** The payload is in `data`, and
|
|
141
|
-
`isPreview` tells you whether it was truncated for your tier. `totalCount` carries the
|
|
142
|
-
untruncated size whenever the server knows it: on a truncated response, so you can render
|
|
143
|
-
"showing N of M", and on a paged endpoint such as `politicians.getActivity`, where it is
|
|
144
|
-
the full match count on every tier including PRO. A missing `totalCount` means "count
|
|
145
|
-
`data` yourself", never "zero results".
|
|
157
|
+
**1. Tier-gated endpoints return a preview envelope.** The payload is in `data`, and `isPreview` tells you whether it was truncated for your tier. `totalCount` carries the untruncated size whenever the server knows it: on a truncated response, so you can render "showing N of M", and on a paged endpoint such as `politicians.getActivity`, where it is the full match count on every tier including PRO. A missing `totalCount` means "count `data` yourself", never "zero results".
|
|
146
158
|
|
|
147
|
-
Affected: `institutional.getFlows` / `getHolders` / `getActivists`, and all five
|
|
148
|
-
`insights` methods.
|
|
159
|
+
Affected: `institutional.getFlows` / `getHolders` / `getActivists`, and all five `insights` methods.
|
|
149
160
|
|
|
150
161
|
```typescript
|
|
151
162
|
const flows = await client.institutional.getFlows();
|
|
@@ -168,11 +179,9 @@ for (const insight of insights.data) {
|
|
|
168
179
|
}
|
|
169
180
|
```
|
|
170
181
|
|
|
171
|
-
**2. Document endpoints return a search wrapper.** This is not the preview envelope:
|
|
172
|
-
the rows are in `documents` and there is no `isPreview`.
|
|
182
|
+
**2. Document endpoints return a search wrapper.** This is not the preview envelope: the rows are in `documents` and there is no `isPreview`.
|
|
173
183
|
|
|
174
|
-
Affected: `documents.getByTicker` / `getByTickerRange` / `getByEntity` / `search` /
|
|
175
|
-
`getBySource`. Also `stocks.getFundamentalsPeriods`, whose periods are in `periods`.
|
|
184
|
+
Affected: `documents.getByTicker` / `getByTickerRange` / `getByEntity` / `search` / `getBySource`. Also `stocks.getFundamentalsPeriods`, whose periods are in `periods`.
|
|
176
185
|
|
|
177
186
|
```typescript
|
|
178
187
|
const results = await client.documents.search("NVDA earnings", { days: 7 });
|
|
@@ -182,34 +191,33 @@ for (const doc of results.documents) {
|
|
|
182
191
|
}
|
|
183
192
|
```
|
|
184
193
|
|
|
185
|
-
Everything else, including `stocks.getPrice()`, `documents.getStories()`,
|
|
186
|
-
`insights.types()` and `institutional.getQuarters()`, resolves to the value itself with no
|
|
187
|
-
wrapper.
|
|
194
|
+
Everything else, including `stocks.getPrice()`, `documents.getStories()`, `insights.types()` and `institutional.getQuarters()`, resolves to the value itself with no wrapper.
|
|
188
195
|
|
|
189
|
-
> **Upgrading from 0.28.x or earlier?** These return types were corrected in 0.29.0. If your
|
|
190
|
-
> code read the flat shape (`flows.inflows`, `holders.filter(...)`), it was returning
|
|
191
|
-
> `undefined` / throwing at runtime already; switch to `flows.data.inflows` /
|
|
192
|
-
> `holders.data.holders`. See [CHANGELOG.md](./CHANGELOG.md) for the full mapping.
|
|
196
|
+
> **Upgrading from 0.28.x or earlier?** These return types were corrected in 0.29.0. If your code read the flat shape (`flows.inflows`, `holders.filter(...)`), it was returning `undefined` / throwing at runtime already; switch to `flows.data.inflows` / `holders.data.holders`. See [CHANGELOG.md](./CHANGELOG.md) for the full mapping.
|
|
193
197
|
|
|
194
|
-
## API
|
|
198
|
+
## API reference
|
|
195
199
|
|
|
196
200
|
### Stocks
|
|
197
201
|
|
|
198
202
|
```typescript
|
|
199
203
|
client.stocks.list() // All ticker symbols
|
|
200
204
|
client.stocks.listDetailed() // All stocks with details
|
|
201
|
-
client.stocks.getPrice("AAPL") //
|
|
205
|
+
client.stocks.getPrice("AAPL") // Latest price
|
|
202
206
|
client.stocks.getPrices(["AAPL", "NVDA"]) // Batch prices
|
|
207
|
+
client.stocks.getQuote("AAPL") // Fuller quote: ranges, market cap, P/E
|
|
203
208
|
client.stocks.getProfile("AAPL") // Company profile
|
|
204
209
|
client.stocks.getChart("AAPL", { timeframe: "6M" }) // OHLCV chart data
|
|
205
210
|
client.stocks.getMarketStatus() // Market open/closed
|
|
206
211
|
client.stocks.getFundamentals("AAPL") // Financial data
|
|
207
212
|
client.stocks.getShortInterest("GME") // Short interest
|
|
208
|
-
client.stocks.getOptionsSummary("NVDA")
|
|
213
|
+
client.stocks.getOptionsSummary("NVDA") // End-of-day options dossier
|
|
214
|
+
client.stocks.getOptionsHistory("NVDA", { window: "2y" }) // Daily options aggregates over time
|
|
209
215
|
client.stocks.getAISummary("AAPL", { depth: "deep" }) // AI report (PRO)
|
|
210
216
|
```
|
|
211
217
|
|
|
212
|
-
|
|
218
|
+
Price fields carry `priceAsOf` (Unix milliseconds) for the age of the market data; read that for freshness rather than `timestamp`, which is when the response was served.
|
|
219
|
+
|
|
220
|
+
### Documents & news
|
|
213
221
|
|
|
214
222
|
```typescript
|
|
215
223
|
client.documents.getByTicker("AAPL", { source: "news", days: 3 })
|
|
@@ -218,7 +226,7 @@ client.documents.getStories({ limit: 10 })
|
|
|
218
226
|
client.documents.getStoryDetail("cluster_abc123")
|
|
219
227
|
```
|
|
220
228
|
|
|
221
|
-
### Institutional
|
|
229
|
+
### Institutional flows (13F)
|
|
222
230
|
|
|
223
231
|
```typescript
|
|
224
232
|
client.institutional.getQuarters()
|
|
@@ -227,12 +235,7 @@ client.institutional.getHolders("AAPL", "2025-02-14")
|
|
|
227
235
|
client.institutional.getActivists("2025-02-14")
|
|
228
236
|
```
|
|
229
237
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
A widely held ticker returns thousands of rows: a megacap quarter is roughly 6,000
|
|
233
|
-
holders and 1.5 MB on the wire. Pass `limit` unless you really want the whole list.
|
|
234
|
-
Omitting the options object sends the original unbounded request, so existing code
|
|
235
|
-
keeps working.
|
|
238
|
+
**Paging the holder list.** A widely held ticker returns thousands of rows: a megacap quarter is roughly 6,000 holders and 1.5 MB on the wire. Pass `limit` unless you really want the whole list; omitting the options object sends the original unbounded request, so existing code keeps working.
|
|
236
239
|
|
|
237
240
|
| Option | Values |
|
|
238
241
|
|--------|--------|
|
|
@@ -241,14 +244,9 @@ keeps working.
|
|
|
241
244
|
| `sortBy` | `"shares"` (server default), `"valueUsd"`, or `"sharesChangePct"`. Requires `limit`. |
|
|
242
245
|
| `sortDir` | `"desc"` (server default) or `"asc"`. Requires `limit`. |
|
|
243
246
|
|
|
244
|
-
`limit` is the switch for the whole set
|
|
245
|
-
and the server ignores them, returning the full unsorted list with a 200 and no warning.
|
|
247
|
+
`limit` is the switch for the whole set: send `offset`, `sortBy`, or `sortDir` without it and the server ignores them, returning the full unsorted list with a 200 and no warning.
|
|
246
248
|
|
|
247
249
|
```typescript
|
|
248
|
-
import SentiSense from "sentisense";
|
|
249
|
-
|
|
250
|
-
const client = new SentiSense({ apiKey: process.env.SENTISENSE_API_KEY });
|
|
251
|
-
|
|
252
250
|
// Top 10 holders by position value, largest first
|
|
253
251
|
const top = await client.institutional.getHolders("AAPL", "2026-03-31", {
|
|
254
252
|
limit: 10,
|
|
@@ -267,39 +265,19 @@ const page = await client.institutional.getHolders("AAPL", "2026-03-31", {
|
|
|
267
265
|
console.log(`${page.data.holders.length} rows of ${page.data.holderCount}`);
|
|
268
266
|
```
|
|
269
267
|
|
|
270
|
-
A response to a request carrying `limit` also has three fields the unbounded response does
|
|
271
|
-
not: `returnedCount` (rows on this page, smaller than your `limit` on the last one),
|
|
272
|
-
`offset` (echoed back), and `notableChanges`, a ticker-wide summary of the quarter's biggest
|
|
273
|
-
position moves so you do not have to scan every page to find them.
|
|
268
|
+
A response to a request carrying `limit` also has three fields the unbounded response does not: `returnedCount` (rows on this page, smaller than your `limit` on the last one), `offset` (echoed back), and `notableChanges`, a ticker-wide summary of the quarter's biggest position moves so you do not have to scan every page to find them. Each holder row also carries `entitySlug`, which you can hand straight to `institutional.getInstitutionDetail()`, and `cikCount` when the row rolls up several SEC filers under one manager; both are null for filers not matched to an institution page, so check before building a link.
|
|
274
269
|
|
|
275
|
-
|
|
276
|
-
const page = await client.institutional.getHolders("AAPL", "2026-03-31", { limit: 100 });
|
|
277
|
-
console.log(`${page.data.returnedCount} of ${page.data.holderCount} holders`);
|
|
278
|
-
for (const mover of page.data.notableChanges?.top ?? []) {
|
|
279
|
-
console.log(mover.filerName, mover.changeType, mover.sharesChangePct);
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Each holder row also carries `entitySlug`, which you can hand straight to
|
|
284
|
-
`institutional.getInstitutionDetail()`, and `cikCount` when the row rolls up several SEC
|
|
285
|
-
filers under one manager. Both are null for filers we have not matched to an institution
|
|
286
|
-
page, so check before building a link.
|
|
287
|
-
|
|
288
|
-
### Congressional Trading
|
|
270
|
+
### Congressional trading
|
|
289
271
|
|
|
290
272
|
```typescript
|
|
291
273
|
client.politicians.getActivity({ lookbackDays: 90 }) // Market-wide STOCK Act feed
|
|
292
274
|
client.politicians.getFilings("NVDA") // Trades in one stock
|
|
293
275
|
client.politicians.getMembers() // Tracked members + trade stats
|
|
294
276
|
client.politicians.getMember("nancy-pelosi") // One member's profile and trades
|
|
277
|
+
client.politicians.getDirectory({ q: "tex" }) // Discover slugs, including former members
|
|
295
278
|
```
|
|
296
279
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
A 90-day window is routinely well over a thousand disclosures, and without `limit` the
|
|
300
|
-
server returns the first 200 with nothing in the payload to say it stopped. `totalCount` on
|
|
301
|
-
the envelope is the real size on every tier, so size the walk from that rather than from
|
|
302
|
-
`data.length`.
|
|
280
|
+
**Paging the activity feed.** A 90-day window is routinely well over a thousand disclosures, and without `limit` the server returns the first 200 with nothing in the payload to say it stopped. `totalCount` on the envelope is the real size on every tier, so size the walk from that rather than from `data.length`.
|
|
303
281
|
|
|
304
282
|
| Option | Values |
|
|
305
283
|
|--------|--------|
|
|
@@ -319,63 +297,37 @@ for (let offset = 100; offset < (first.totalCount ?? 0); offset += 100) {
|
|
|
319
297
|
}
|
|
320
298
|
```
|
|
321
299
|
|
|
322
|
-
###
|
|
300
|
+
### Insider trading (Form 4)
|
|
323
301
|
|
|
324
302
|
```typescript
|
|
325
|
-
//
|
|
326
|
-
client.
|
|
327
|
-
client.
|
|
328
|
-
metricType: "mentions",
|
|
329
|
-
startTime: Date.now() - 7 * 86400000,
|
|
330
|
-
endTime: Date.now(),
|
|
331
|
-
maxDataPoints: 100,
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
// Distribution by source
|
|
335
|
-
client.entityMetrics.getDistribution("AAPL", "sentiment")
|
|
336
|
-
client.entityMetrics.getDistribution("AAPL", "mentions", { dimension: "source" })
|
|
303
|
+
client.insider.getActivity({ lookbackDays: 30 }) // Market-wide buys and sells by ticker
|
|
304
|
+
client.insider.getTrades("NVDA", { lookbackDays: 90 }) // Individual filed transactions
|
|
305
|
+
client.insider.getClusterBuys({ lookbackDays: 90 }) // 3+ distinct insiders buying the same stock
|
|
337
306
|
```
|
|
338
307
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
### Knowledge Base
|
|
308
|
+
Each trade row carries both the raw SEC `transactionCode` and a simplified `transactionType`. Only codes `P` and `S` are open-market trades; awards, gifts, exercises, and code `F` (shares withheld to cover taxes at vest, served as `SELL`) are corporate mechanics, so read `transactionCode` when you tally discretionary buying or selling. The market-wide activity endpoint's sells already exclude code `F` server-side.
|
|
342
309
|
|
|
343
|
-
|
|
344
|
-
client.kb.getPopularEntities()
|
|
345
|
-
```
|
|
310
|
+
### Analyst ratings
|
|
346
311
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
The **price target cone** (mean, high, low, upside %) and consensus are **free for everyone, full data via API**: we give it away. Upgrade/downgrade feeds and forward EPS estimates are limited on free, unlimited on PRO.
|
|
312
|
+
The price target cone (mean, high, low, upside %) and consensus are free for everyone with full data. Upgrade/downgrade feeds and forward EPS estimates are limited on free, unlimited on PRO.
|
|
350
313
|
|
|
351
314
|
```typescript
|
|
352
|
-
client.analyst.consensus("AAPL") // Price target cone + consensus. Free
|
|
353
|
-
client.analyst.actions("AAPL", { lookbackDays: 30 }) // Upgrade/downgrade feed. Free: 3 most recent.
|
|
354
|
-
client.analyst.estimates("AAPL") // Forward EPS +
|
|
315
|
+
client.analyst.consensus("AAPL") // Price target cone + consensus. Free, full data.
|
|
316
|
+
client.analyst.actions("AAPL", { lookbackDays: 30 }) // Upgrade/downgrade feed. Free: 3 most recent.
|
|
317
|
+
client.analyst.estimates("AAPL") // Forward EPS + surprises. Free: 1 quarter.
|
|
355
318
|
client.analyst.marketActivity({ lookbackDays: 7 }) // Market-wide analyst actions (PRO).
|
|
356
319
|
```
|
|
357
320
|
|
|
358
|
-
### Company KPIs (PRO)
|
|
359
|
-
|
|
360
|
-
```typescript
|
|
361
|
-
client.stocks.getKpis("AAPL") // Product metrics and segment revenue time-series. Free returns metadata only (empty kpis array); PRO returns full series.
|
|
362
|
-
client.stocks.listKpiCoverage() // All tickers with curated KPI data (free, no quota cost)
|
|
363
|
-
```
|
|
364
|
-
|
|
365
321
|
### Earnings
|
|
366
322
|
|
|
367
323
|
The earnings analysis report is the assembled version of a quarter: one object per fiscal period carrying the editorial headline, the KPI cards with year-over-year deltas, the guidance language as management phrased it, and a summary of the earnings call. Pair it with the recent-reporters feed to drive a post-earnings sweep. Both return the preview envelope.
|
|
368
324
|
|
|
369
325
|
```typescript
|
|
370
|
-
client.earnings.getSummaries("AAPL", { limit: 4 }) // Per-quarter analysis
|
|
326
|
+
client.earnings.getSummaries("AAPL", { limit: 4 }) // Per-quarter analysis, newest first. Free: latest quarter, shaped.
|
|
371
327
|
client.earnings.getRecent({ days: 7, limit: 25 }) // Who reported in the last N days. Full window on every key.
|
|
372
328
|
```
|
|
373
329
|
|
|
374
330
|
```typescript
|
|
375
|
-
import SentiSense from "sentisense";
|
|
376
|
-
|
|
377
|
-
const client = new SentiSense({ apiKey: process.env.SENTISENSE_API_KEY });
|
|
378
|
-
|
|
379
331
|
const res = await client.earnings.getSummaries("AAPL", { limit: 1 });
|
|
380
332
|
const quarter = res.data[0];
|
|
381
333
|
|
|
@@ -397,22 +349,63 @@ if (quarter) {
|
|
|
397
349
|
|
|
398
350
|
The forward-looking half of the family is `client.calendar.getEarnings()`, which covers scheduled dates and consensus EPS rather than results.
|
|
399
351
|
|
|
352
|
+
### Company KPIs (PRO)
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
client.stocks.getKpis("AAPL") // Product metrics and segment revenue. Free: metadata only. PRO: full series.
|
|
356
|
+
client.stocks.listKpiCoverage() // All tickers with curated KPI data (free, no quota cost)
|
|
357
|
+
```
|
|
358
|
+
|
|
400
359
|
### ETFs (beta)
|
|
401
360
|
|
|
402
|
-
Composition data is public; the holdings-weighted aggregate views follow the same PRO-with-preview pattern as
|
|
361
|
+
Composition data is public; the holdings-weighted aggregate views follow the same PRO-with-preview pattern as analyst and insider data. Aggregates synthesize fund-level views from each constituent's per-stock data, weighted by allocation, with a `coverage` block on every response.
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
client.etfs.list() // Every ETF tracked
|
|
365
|
+
client.etfs.holdings("QQQ") // Full composition + freshness metadata
|
|
366
|
+
client.etfs.analystAggregate("QQQ") // Holdings-weighted analyst consensus
|
|
367
|
+
client.etfs.insiderAggregate("ARKK", { lookbackDays: 90 }) // Holdings-weighted Form 4 net flow
|
|
368
|
+
client.etfs.sentimentAggregate("QQQ") // Constituent-weighted vs direct Score
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Entity metrics
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// Time-series metrics (v2 API)
|
|
375
|
+
client.entityMetrics.getMetrics("AAPL", { metricType: "sentiment" })
|
|
376
|
+
client.entityMetrics.getMetrics("AAPL", {
|
|
377
|
+
metricType: "mentions",
|
|
378
|
+
startTime: Date.now() - 7 * 86400000,
|
|
379
|
+
endTime: Date.now(),
|
|
380
|
+
maxDataPoints: 100,
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
// Distribution by source
|
|
384
|
+
client.entityMetrics.getDistribution("AAPL", "sentiment")
|
|
385
|
+
client.entityMetrics.getDistribution("AAPL", "mentions", { dimension: "source" })
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Available metric types: `mentions`, `sentiment`, `sentisense`, `social_dominance`, `creators`.
|
|
389
|
+
|
|
390
|
+
### Options
|
|
391
|
+
|
|
392
|
+
End-of-day options positioning: where implied volatility, put/call flow and skew are unusual today, and how a name's readings have trended.
|
|
403
393
|
|
|
404
394
|
```typescript
|
|
405
|
-
client.
|
|
406
|
-
client.
|
|
407
|
-
client.
|
|
408
|
-
client.etfs.insiderAggregate("ARKK", { lookbackDays: 90 }) // Holdings-weighted Form 4 net flow. Free: headline. PRO: + topContributors.
|
|
409
|
-
client.etfs.sentimentAggregate("QQQ") // SentiSense readings side-by-side: constituent-weighted vs direct.
|
|
395
|
+
client.options.getOverview() // Market-wide radar, ranked
|
|
396
|
+
client.stocks.getOptionsSummary("NVDA") // One name's full dossier
|
|
397
|
+
client.stocks.getOptionsHistory("NVDA", { window: "2y" }) // That name's daily series
|
|
410
398
|
```
|
|
411
399
|
|
|
412
|
-
|
|
400
|
+
The radar carries two separately-ranked boards: `data.rows` for stocks and `data.etfRows` for ETFs. Keep them apart. Every reading behind a row's `interestScore` is a percentile of that ticker's own trailing history, so a ranking built across both boards compares numbers measured against different baselines. The aggregates split the same way, with the `etf`-prefixed fields describing the ETF board alone.
|
|
401
|
+
|
|
402
|
+
A row whose baseline is still building carries its raw readings with the percentiles and `interestScore` omitted, which means "not enough history yet" rather than "nothing interesting". `getOptionsSummary` reports an uncovered ticker as a `null` payload; `getOptionsHistory` reports it as an empty `series` instead, so check the array rather than null-checking there.
|
|
403
|
+
|
|
404
|
+
### Market mood & knowledge base
|
|
413
405
|
|
|
414
406
|
```typescript
|
|
415
|
-
client.marketMood.get()
|
|
407
|
+
client.marketMood.get() // Composite market sentiment with sub-signals
|
|
408
|
+
client.kb.getPopularEntities() // Most-tracked entities
|
|
416
409
|
```
|
|
417
410
|
|
|
418
411
|
### Screener
|
|
@@ -456,15 +449,15 @@ Three field semantics are worth stating outright, because guessing them wrong pr
|
|
|
456
449
|
|
|
457
450
|
- **`ANALYST_RATING_MEAN` is inverted.** It is the vendor's 1-to-5 scale where **1.0 is strong buy**, so bullish is `LTE 2.5`. Prefer `ANALYST_BUY_RATIO_PCT`, which runs the intuitive direction.
|
|
458
451
|
- **`MA_CROSS_STATE` is ordinal**, not a percentage: `1` golden cross, `-1` death cross, `0` neither. Use `EQ`.
|
|
459
|
-
- **`SENTIMENT_DIRECTION` is the sign of the 7-day SentiSense Score** (`1` / `0` / `-1`) with a neutral band of plus-or-minus 5. Despite the name it is not sentiment polarity
|
|
452
|
+
- **`SENTIMENT_DIRECTION` is the sign of the 7-day SentiSense Score** (`1` / `0` / `-1`) with a neutral band of plus-or-minus 5. Despite the name it is not sentiment polarity.
|
|
460
453
|
|
|
461
454
|
The Score fields (`SENTI_SCORE_7D`, `SENTI_SCORE_1M`, `SCORE_CHANGE_7D`) are the SentiSense Score, not polarity: unbounded, banded at 5 / 13 / 23 either side of zero. Filter on those band edges, not on values like `0.5`, which behave as "any positive score". Nulls never match in either direction, so `RETURN_1Y >= 0` and `RETURN_1Y < 0` do not partition the universe: a stock listed four months ago is in neither result. If a screen returns fewer rows than you expect, check coverage before you check your thresholds.
|
|
462
455
|
|
|
463
456
|
On the ETF side, `CONSTITUENTS_WEIGHTED_SENTISENSE` is the holdings-weighted Score across what the fund owns and is usually the one you want; `DIRECT_SENTISENSE` is the Score from chatter about the fund ticker itself. `WEIGHT_COVERED_PCT` tells you how much of the fund's weight had constituent data behind the weighted number.
|
|
464
457
|
|
|
465
|
-
Screens read a snapshot that refreshes every 20 minutes, so this is not a quote feed. Use `client.stocks.getQuote()` for
|
|
458
|
+
Screens read a snapshot that refreshes every 20 minutes, so this is not a quote feed. Use `client.stocks.getQuote()` for current quotes.
|
|
466
459
|
|
|
467
|
-
## Error
|
|
460
|
+
## Error handling
|
|
468
461
|
|
|
469
462
|
```typescript
|
|
470
463
|
import SentiSense, { AuthenticationError, RateLimitError } from "sentisense";
|
|
@@ -480,7 +473,7 @@ try {
|
|
|
480
473
|
}
|
|
481
474
|
```
|
|
482
475
|
|
|
483
|
-
| Error
|
|
476
|
+
| Error class | HTTP status | When |
|
|
484
477
|
|------------|-------------|------|
|
|
485
478
|
| `AuthenticationError` | 401, 403 | Invalid API key or insufficient tier |
|
|
486
479
|
| `NotFoundError` | 404 | Resource not found |
|
|
@@ -489,39 +482,13 @@ try {
|
|
|
489
482
|
|
|
490
483
|
All errors extend `SentiSenseError` and include `status`, `code`, and `message` properties.
|
|
491
484
|
|
|
492
|
-
##
|
|
493
|
-
|
|
494
|
-
```typescript
|
|
495
|
-
const client = new SentiSense({
|
|
496
|
-
apiKey: process.env.SENTISENSE_API_KEY, // Get yours at app.sentisense.ai/settings/developer
|
|
497
|
-
baseUrl: "https://...", // Default: https://app.sentisense.ai
|
|
498
|
-
timeout: 30000, // Default: 30s (in milliseconds)
|
|
499
|
-
maxRetries: 3, // Default: 3
|
|
500
|
-
userAgentSuffix: "my-bot/1.4", // Default: none
|
|
501
|
-
});
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
| Option | Default | What it does |
|
|
505
|
-
|--------|---------|--------------|
|
|
506
|
-
| `apiKey` | none | Sent as `X-SentiSense-API-Key`. Required by every endpoint. |
|
|
507
|
-
| `baseUrl` | `https://app.sentisense.ai` | Override for a non-production host. |
|
|
508
|
-
| `timeout` | `30000` | Per-request timeout in milliseconds. |
|
|
509
|
-
| `maxRetries` | `3` | Retries on 429 and 5xx, honouring `Retry-After`. Set `0` to fail fast. |
|
|
510
|
-
| `userAgentSuffix` | none | Appended to the User-Agent, after `sentisense-node/{version}`. |
|
|
511
|
-
|
|
512
|
-
`userAgentSuffix` is how you say what is calling on top of the SDK, so your traffic is legible
|
|
513
|
-
in your own logs and in ours. A tool name and version works (`"my-bot/1.4"`), optionally with
|
|
514
|
-
an agent label (`"my-bot/1.4 agent/research-desk"`). Node only, since browsers set the header
|
|
515
|
-
themselves. Newlines are collapsed and an empty value is ignored.
|
|
516
|
-
|
|
517
|
-
Keep the key in the environment rather than in source. Committing a literal key leaks it
|
|
518
|
-
into git history and into every registry security scan that reads your repo.
|
|
519
|
-
|
|
520
|
-
## Get an API Key
|
|
485
|
+
## Links
|
|
521
486
|
|
|
522
|
-
|
|
487
|
+
- Get a free API key: [app.sentisense.ai/get-api-key](https://app.sentisense.ai/get-api-key)
|
|
488
|
+
- API documentation: [sentisense.ai/docs/api](https://sentisense.ai/docs/api)
|
|
489
|
+
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
|
523
490
|
|
|
524
|
-
|
|
491
|
+
SentiSense provides research data for informational and educational purposes, not investment advice.
|
|
525
492
|
|
|
526
493
|
## License
|
|
527
494
|
|