sentisense 0.45.0 → 0.46.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 +136 -184
- package/dist/cli.cjs +16 -9
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- 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
|
|
92
|
-
|
|
93
|
-
If you set `SENTISENSE_AGENT_NAME` (what your agent is called) and `SENTISENSE_SKILL` (the
|
|
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
|
-
```
|
|
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.
|
|
104
97
|
|
|
105
|
-
|
|
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=stock-analysis
|
|
121
|
+
npx -y sentisense@latest quote NVDA
|
|
122
|
+
# User-Agent: sentisense-node/{version} sentisense-cli/{version} (stock-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 stock-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,32 @@ 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
|
|
209
214
|
client.stocks.getAISummary("AAPL", { depth: "deep" }) // AI report (PRO)
|
|
210
215
|
```
|
|
211
216
|
|
|
212
|
-
|
|
217
|
+
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.
|
|
218
|
+
|
|
219
|
+
### Documents & news
|
|
213
220
|
|
|
214
221
|
```typescript
|
|
215
222
|
client.documents.getByTicker("AAPL", { source: "news", days: 3 })
|
|
@@ -218,7 +225,7 @@ client.documents.getStories({ limit: 10 })
|
|
|
218
225
|
client.documents.getStoryDetail("cluster_abc123")
|
|
219
226
|
```
|
|
220
227
|
|
|
221
|
-
### Institutional
|
|
228
|
+
### Institutional flows (13F)
|
|
222
229
|
|
|
223
230
|
```typescript
|
|
224
231
|
client.institutional.getQuarters()
|
|
@@ -227,12 +234,7 @@ client.institutional.getHolders("AAPL", "2025-02-14")
|
|
|
227
234
|
client.institutional.getActivists("2025-02-14")
|
|
228
235
|
```
|
|
229
236
|
|
|
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.
|
|
237
|
+
**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
238
|
|
|
237
239
|
| Option | Values |
|
|
238
240
|
|--------|--------|
|
|
@@ -241,14 +243,9 @@ keeps working.
|
|
|
241
243
|
| `sortBy` | `"shares"` (server default), `"valueUsd"`, or `"sharesChangePct"`. Requires `limit`. |
|
|
242
244
|
| `sortDir` | `"desc"` (server default) or `"asc"`. Requires `limit`. |
|
|
243
245
|
|
|
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.
|
|
246
|
+
`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
247
|
|
|
247
248
|
```typescript
|
|
248
|
-
import SentiSense from "sentisense";
|
|
249
|
-
|
|
250
|
-
const client = new SentiSense({ apiKey: process.env.SENTISENSE_API_KEY });
|
|
251
|
-
|
|
252
249
|
// Top 10 holders by position value, largest first
|
|
253
250
|
const top = await client.institutional.getHolders("AAPL", "2026-03-31", {
|
|
254
251
|
limit: 10,
|
|
@@ -267,39 +264,19 @@ const page = await client.institutional.getHolders("AAPL", "2026-03-31", {
|
|
|
267
264
|
console.log(`${page.data.holders.length} rows of ${page.data.holderCount}`);
|
|
268
265
|
```
|
|
269
266
|
|
|
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.
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
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.
|
|
267
|
+
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.
|
|
287
268
|
|
|
288
|
-
### Congressional
|
|
269
|
+
### Congressional trading
|
|
289
270
|
|
|
290
271
|
```typescript
|
|
291
272
|
client.politicians.getActivity({ lookbackDays: 90 }) // Market-wide STOCK Act feed
|
|
292
273
|
client.politicians.getFilings("NVDA") // Trades in one stock
|
|
293
274
|
client.politicians.getMembers() // Tracked members + trade stats
|
|
294
275
|
client.politicians.getMember("nancy-pelosi") // One member's profile and trades
|
|
276
|
+
client.politicians.getDirectory({ q: "tex" }) // Discover slugs, including former members
|
|
295
277
|
```
|
|
296
278
|
|
|
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`.
|
|
279
|
+
**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
280
|
|
|
304
281
|
| Option | Values |
|
|
305
282
|
|--------|--------|
|
|
@@ -319,63 +296,37 @@ for (let offset = 100; offset < (first.totalCount ?? 0); offset += 100) {
|
|
|
319
296
|
}
|
|
320
297
|
```
|
|
321
298
|
|
|
322
|
-
###
|
|
299
|
+
### Insider trading (Form 4)
|
|
323
300
|
|
|
324
301
|
```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" })
|
|
302
|
+
client.insider.getActivity({ lookbackDays: 30 }) // Market-wide buys and sells by ticker
|
|
303
|
+
client.insider.getTrades("NVDA", { lookbackDays: 90 }) // Individual filed transactions
|
|
304
|
+
client.insider.getClusterBuys({ lookbackDays: 90 }) // 3+ distinct insiders buying the same stock
|
|
337
305
|
```
|
|
338
306
|
|
|
339
|
-
|
|
307
|
+
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.
|
|
340
308
|
|
|
341
|
-
###
|
|
309
|
+
### Analyst ratings
|
|
342
310
|
|
|
343
|
-
|
|
344
|
-
client.kb.getPopularEntities()
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### Analyst Ratings
|
|
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.
|
|
311
|
+
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
312
|
|
|
351
313
|
```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 +
|
|
314
|
+
client.analyst.consensus("AAPL") // Price target cone + consensus. Free, full data.
|
|
315
|
+
client.analyst.actions("AAPL", { lookbackDays: 30 }) // Upgrade/downgrade feed. Free: 3 most recent.
|
|
316
|
+
client.analyst.estimates("AAPL") // Forward EPS + surprises. Free: 1 quarter.
|
|
355
317
|
client.analyst.marketActivity({ lookbackDays: 7 }) // Market-wide analyst actions (PRO).
|
|
356
318
|
```
|
|
357
319
|
|
|
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
320
|
### Earnings
|
|
366
321
|
|
|
367
322
|
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
323
|
|
|
369
324
|
```typescript
|
|
370
|
-
client.earnings.getSummaries("AAPL", { limit: 4 }) // Per-quarter analysis
|
|
325
|
+
client.earnings.getSummaries("AAPL", { limit: 4 }) // Per-quarter analysis, newest first. Free: latest quarter, shaped.
|
|
371
326
|
client.earnings.getRecent({ days: 7, limit: 25 }) // Who reported in the last N days. Full window on every key.
|
|
372
327
|
```
|
|
373
328
|
|
|
374
329
|
```typescript
|
|
375
|
-
import SentiSense from "sentisense";
|
|
376
|
-
|
|
377
|
-
const client = new SentiSense({ apiKey: process.env.SENTISENSE_API_KEY });
|
|
378
|
-
|
|
379
330
|
const res = await client.earnings.getSummaries("AAPL", { limit: 1 });
|
|
380
331
|
const quarter = res.data[0];
|
|
381
332
|
|
|
@@ -397,22 +348,49 @@ if (quarter) {
|
|
|
397
348
|
|
|
398
349
|
The forward-looking half of the family is `client.calendar.getEarnings()`, which covers scheduled dates and consensus EPS rather than results.
|
|
399
350
|
|
|
351
|
+
### Company KPIs (PRO)
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
client.stocks.getKpis("AAPL") // Product metrics and segment revenue. Free: metadata only. PRO: full series.
|
|
355
|
+
client.stocks.listKpiCoverage() // All tickers with curated KPI data (free, no quota cost)
|
|
356
|
+
```
|
|
357
|
+
|
|
400
358
|
### ETFs (beta)
|
|
401
359
|
|
|
402
|
-
Composition data is public; the holdings-weighted aggregate views follow the same PRO-with-preview pattern as
|
|
360
|
+
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.
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
client.etfs.list() // Every ETF tracked
|
|
364
|
+
client.etfs.holdings("QQQ") // Full composition + freshness metadata
|
|
365
|
+
client.etfs.analystAggregate("QQQ") // Holdings-weighted analyst consensus
|
|
366
|
+
client.etfs.insiderAggregate("ARKK", { lookbackDays: 90 }) // Holdings-weighted Form 4 net flow
|
|
367
|
+
client.etfs.sentimentAggregate("QQQ") // Constituent-weighted vs direct Score
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Entity metrics
|
|
403
371
|
|
|
404
372
|
```typescript
|
|
405
|
-
|
|
406
|
-
client.
|
|
407
|
-
client.
|
|
408
|
-
|
|
409
|
-
|
|
373
|
+
// Time-series metrics (v2 API)
|
|
374
|
+
client.entityMetrics.getMetrics("AAPL", { metricType: "sentiment" })
|
|
375
|
+
client.entityMetrics.getMetrics("AAPL", {
|
|
376
|
+
metricType: "mentions",
|
|
377
|
+
startTime: Date.now() - 7 * 86400000,
|
|
378
|
+
endTime: Date.now(),
|
|
379
|
+
maxDataPoints: 100,
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
// Distribution by source
|
|
383
|
+
client.entityMetrics.getDistribution("AAPL", "sentiment")
|
|
384
|
+
client.entityMetrics.getDistribution("AAPL", "mentions", { dimension: "source" })
|
|
410
385
|
```
|
|
411
386
|
|
|
412
|
-
|
|
387
|
+
Available metric types: `mentions`, `sentiment`, `sentisense`, `social_dominance`, `creators`.
|
|
388
|
+
|
|
389
|
+
### Market mood & knowledge base
|
|
413
390
|
|
|
414
391
|
```typescript
|
|
415
|
-
client.marketMood.get()
|
|
392
|
+
client.marketMood.get() // Composite market sentiment with sub-signals
|
|
393
|
+
client.kb.getPopularEntities() // Most-tracked entities
|
|
416
394
|
```
|
|
417
395
|
|
|
418
396
|
### Screener
|
|
@@ -456,15 +434,15 @@ Three field semantics are worth stating outright, because guessing them wrong pr
|
|
|
456
434
|
|
|
457
435
|
- **`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
436
|
- **`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
|
|
437
|
+
- **`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
438
|
|
|
461
439
|
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
440
|
|
|
463
441
|
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
442
|
|
|
465
|
-
Screens read a snapshot that refreshes every 20 minutes, so this is not a quote feed. Use `client.stocks.getQuote()` for
|
|
443
|
+
Screens read a snapshot that refreshes every 20 minutes, so this is not a quote feed. Use `client.stocks.getQuote()` for current quotes.
|
|
466
444
|
|
|
467
|
-
## Error
|
|
445
|
+
## Error handling
|
|
468
446
|
|
|
469
447
|
```typescript
|
|
470
448
|
import SentiSense, { AuthenticationError, RateLimitError } from "sentisense";
|
|
@@ -480,7 +458,7 @@ try {
|
|
|
480
458
|
}
|
|
481
459
|
```
|
|
482
460
|
|
|
483
|
-
| Error
|
|
461
|
+
| Error class | HTTP status | When |
|
|
484
462
|
|------------|-------------|------|
|
|
485
463
|
| `AuthenticationError` | 401, 403 | Invalid API key or insufficient tier |
|
|
486
464
|
| `NotFoundError` | 404 | Resource not found |
|
|
@@ -489,39 +467,13 @@ try {
|
|
|
489
467
|
|
|
490
468
|
All errors extend `SentiSenseError` and include `status`, `code`, and `message` properties.
|
|
491
469
|
|
|
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
|
|
470
|
+
## Links
|
|
521
471
|
|
|
522
|
-
|
|
472
|
+
- Get a free API key: [app.sentisense.ai/get-api-key](https://app.sentisense.ai/get-api-key)
|
|
473
|
+
- API documentation: [sentisense.ai/docs/api](https://sentisense.ai/docs/api)
|
|
474
|
+
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
|
523
475
|
|
|
524
|
-
|
|
476
|
+
SentiSense provides research data for informational and educational purposes, not investment advice.
|
|
525
477
|
|
|
526
478
|
## License
|
|
527
479
|
|
package/dist/cli.cjs
CHANGED
|
@@ -1038,7 +1038,7 @@ var flowsCommand = {
|
|
|
1038
1038
|
};
|
|
1039
1039
|
|
|
1040
1040
|
// src/version.ts
|
|
1041
|
-
var VERSION = "0.
|
|
1041
|
+
var VERSION = "0.46.0";
|
|
1042
1042
|
|
|
1043
1043
|
// src/resources/analyst.ts
|
|
1044
1044
|
var Analyst = class {
|
|
@@ -2498,6 +2498,9 @@ var insidersCommand = {
|
|
|
2498
2498
|
],
|
|
2499
2499
|
notes: [
|
|
2500
2500
|
"Rows are individual filed transactions, newest first, not a net total.",
|
|
2501
|
+
"The bought/sold figures count open-market rows only. Form 4 code F rows (shares withheld",
|
|
2502
|
+
"to cover taxes on vesting) arrive typed SELL but are mechanical withholding, not a decision",
|
|
2503
|
+
"to sell, so they are excluded from sold and shown separately as withheld.",
|
|
2501
2504
|
"The plan column says whether the trade was under a confirmed pre-arranged 10b5-1 plan,",
|
|
2502
2505
|
"which is the difference between a scheduled sale and a discretionary one.",
|
|
2503
2506
|
"A free key sees the top few transactions; a PRO key sees the window you asked for.",
|
|
@@ -2522,18 +2525,22 @@ var insidersCommand = {
|
|
|
2522
2525
|
if (note) notes.push(note);
|
|
2523
2526
|
}
|
|
2524
2527
|
const shown = full ? trades : trades.slice(0, 15);
|
|
2528
|
+
const isWithholding = (row) => row.transactionCode === "F";
|
|
2525
2529
|
const buys = trades.filter((trade) => trade.transactionType === "BUY");
|
|
2526
|
-
const sells = trades.filter((trade) => trade.transactionType === "SELL");
|
|
2530
|
+
const sells = trades.filter((trade) => trade.transactionType === "SELL" && !isWithholding(trade));
|
|
2531
|
+
const withheld = trades.filter(isWithholding);
|
|
2527
2532
|
const sum = (rows) => rows.reduce((total, row) => total + (row.totalValue || 0), 0);
|
|
2533
|
+
const headline = [
|
|
2534
|
+
field("trades", String(trades.length)),
|
|
2535
|
+
field("bought", humanize(sum(buys)), buys.length > 0 ? "up" : void 0),
|
|
2536
|
+
field("sold", humanize(sum(sells)), sells.length > 0 ? "down" : void 0)
|
|
2537
|
+
];
|
|
2538
|
+
if (withheld.length > 0) headline.push(field("withheld", humanize(sum(withheld))));
|
|
2528
2539
|
const blocks = [
|
|
2529
2540
|
{
|
|
2530
2541
|
kind: "head",
|
|
2531
2542
|
title: field("ticker", ticker),
|
|
2532
|
-
right: fields(
|
|
2533
|
-
field("trades", String(trades.length)),
|
|
2534
|
-
field("bought", humanize(sum(buys)), buys.length > 0 ? "up" : void 0),
|
|
2535
|
-
field("sold", humanize(sum(sells)), sells.length > 0 ? "down" : void 0)
|
|
2536
|
-
)
|
|
2543
|
+
right: fields(...headline)
|
|
2537
2544
|
}
|
|
2538
2545
|
];
|
|
2539
2546
|
if (shown.length === 0) {
|
|
@@ -2548,8 +2555,8 @@ var insidersCommand = {
|
|
|
2548
2555
|
cell(truncate(trade.insiderName, full ? 40 : 22)),
|
|
2549
2556
|
cell(truncate(trade.insiderTitle ?? "", full ? 40 : 18)),
|
|
2550
2557
|
cell(
|
|
2551
|
-
trade.transactionType,
|
|
2552
|
-
trade.transactionType === "BUY" ? "up" : trade.transactionType === "SELL" ? "down" : void 0
|
|
2558
|
+
isWithholding(trade) ? "TAX-W" : trade.transactionType,
|
|
2559
|
+
!isWithholding(trade) && trade.transactionType === "BUY" ? "up" : !isWithholding(trade) && trade.transactionType === "SELL" ? "down" : void 0
|
|
2553
2560
|
),
|
|
2554
2561
|
cell(humanize(trade.sharesTransacted, 1)),
|
|
2555
2562
|
cell(humanize(trade.totalValue)),
|