sentisense 0.33.0 → 0.35.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 +105 -6
- package/dist/index.cjs +104 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +350 -27
- package/dist/index.d.ts +350 -27
- package/dist/index.mjs +104 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -6
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install sentisense
|
|
|
16
16
|
```typescript
|
|
17
17
|
import SentiSense from "sentisense";
|
|
18
18
|
|
|
19
|
-
const client = new SentiSense({ apiKey:
|
|
19
|
+
const client = new SentiSense({ apiKey: process.env.SENTISENSE_API_KEY });
|
|
20
20
|
|
|
21
21
|
const price = await client.stocks.getPrice("AAPL");
|
|
22
22
|
console.log(price.currentPrice);
|
|
@@ -40,8 +40,11 @@ Most methods resolve to the payload directly, but two families wrap it. The retu
|
|
|
40
40
|
describe the wrapper, so `.data` / `.documents` type-check natively, no cast.
|
|
41
41
|
|
|
42
42
|
**1. Tier-gated endpoints return a preview envelope.** The payload is in `data`, and
|
|
43
|
-
`isPreview` tells you whether it was truncated for your tier.
|
|
44
|
-
|
|
43
|
+
`isPreview` tells you whether it was truncated for your tier. `totalCount` carries the
|
|
44
|
+
untruncated size whenever the server knows it: on a truncated response, so you can render
|
|
45
|
+
"showing N of M", and on a paged endpoint such as `politicians.getActivity`, where it is
|
|
46
|
+
the full match count on every tier including PRO. A missing `totalCount` means "count
|
|
47
|
+
`data` yourself", never "zero results".
|
|
45
48
|
|
|
46
49
|
Affected: `institutional.getFlows` / `getHolders` / `getActivists`, and all five
|
|
47
50
|
`insights` methods.
|
|
@@ -125,6 +128,98 @@ client.institutional.getHolders("AAPL", "2025-02-14")
|
|
|
125
128
|
client.institutional.getActivists("2025-02-14")
|
|
126
129
|
```
|
|
127
130
|
|
|
131
|
+
#### Paging the holder list
|
|
132
|
+
|
|
133
|
+
A widely held ticker returns thousands of rows: a megacap quarter is roughly 6,000
|
|
134
|
+
holders and 1.5 MB on the wire. Pass `limit` unless you really want the whole list.
|
|
135
|
+
Omitting the options object sends the original unbounded request, so existing code
|
|
136
|
+
keeps working.
|
|
137
|
+
|
|
138
|
+
| Option | Values |
|
|
139
|
+
|--------|--------|
|
|
140
|
+
| `limit` | Maximum rows to return. Must be >= 1; values above 1000 are capped server-side. Omit for the full list. |
|
|
141
|
+
| `offset` | Row offset to start from. Server default is 0. Requires `limit`. |
|
|
142
|
+
| `sortBy` | `"shares"` (server default), `"valueUsd"`, or `"sharesChangePct"`. Requires `limit`. |
|
|
143
|
+
| `sortDir` | `"desc"` (server default) or `"asc"`. Requires `limit`. |
|
|
144
|
+
|
|
145
|
+
`limit` is the switch for the whole set. Send `offset`, `sortBy`, or `sortDir` without it
|
|
146
|
+
and the server ignores them, returning the full unsorted list with a 200 and no warning.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import SentiSense from "sentisense";
|
|
150
|
+
|
|
151
|
+
const client = new SentiSense({ apiKey: process.env.SENTISENSE_API_KEY });
|
|
152
|
+
|
|
153
|
+
// Top 10 holders by position value, largest first
|
|
154
|
+
const top = await client.institutional.getHolders("AAPL", "2026-03-31", {
|
|
155
|
+
limit: 10,
|
|
156
|
+
sortBy: "valueUsd",
|
|
157
|
+
sortDir: "desc",
|
|
158
|
+
});
|
|
159
|
+
for (const holder of top.data.holders) {
|
|
160
|
+
console.log(holder.filerName, holder.valueUsd);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Walk the list a page at a time
|
|
164
|
+
const page = await client.institutional.getHolders("AAPL", "2026-03-31", {
|
|
165
|
+
limit: 100,
|
|
166
|
+
offset: 100,
|
|
167
|
+
});
|
|
168
|
+
console.log(`${page.data.holders.length} rows of ${page.data.holderCount}`);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
A response to a request carrying `limit` also has three fields the unbounded response does
|
|
172
|
+
not: `returnedCount` (rows on this page, smaller than your `limit` on the last one),
|
|
173
|
+
`offset` (echoed back), and `notableChanges`, a ticker-wide summary of the quarter's biggest
|
|
174
|
+
position moves so you do not have to scan every page to find them.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const page = await client.institutional.getHolders("AAPL", "2026-03-31", { limit: 100 });
|
|
178
|
+
console.log(`${page.data.returnedCount} of ${page.data.holderCount} holders`);
|
|
179
|
+
for (const mover of page.data.notableChanges?.top ?? []) {
|
|
180
|
+
console.log(mover.filerName, mover.changeType, mover.sharesChangePct);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Each holder row also carries `entitySlug`, which you can hand straight to
|
|
185
|
+
`institutional.getInstitutionDetail()`, and `cikCount` when the row rolls up several SEC
|
|
186
|
+
filers under one manager. Both are null for filers we have not matched to an institution
|
|
187
|
+
page, so check before building a link.
|
|
188
|
+
|
|
189
|
+
### Congressional Trading
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
client.politicians.getActivity({ lookbackDays: 90 }) // Market-wide STOCK Act feed
|
|
193
|
+
client.politicians.getFilings("NVDA") // Trades in one stock
|
|
194
|
+
client.politicians.getMembers() // Tracked members + trade stats
|
|
195
|
+
client.politicians.getMember("nancy-pelosi") // One member's profile and trades
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Paging the activity feed
|
|
199
|
+
|
|
200
|
+
A 90-day window is routinely well over a thousand disclosures, and without `limit` the
|
|
201
|
+
server returns the first 200 with nothing in the payload to say it stopped. `totalCount` on
|
|
202
|
+
the envelope is the real size on every tier, so size the walk from that rather than from
|
|
203
|
+
`data.length`.
|
|
204
|
+
|
|
205
|
+
| Option | Values |
|
|
206
|
+
|--------|--------|
|
|
207
|
+
| `lookbackDays` | Days to look back (1-365). Defaults to 90. |
|
|
208
|
+
| `limit` | Rows to return. Must be >= 1; anything above 500 is capped at 500. Omit for the default 200. |
|
|
209
|
+
| `offset` | Row offset to start from. Defaults to 0. Works with or without `limit`. |
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const first = await client.politicians.getActivity({ limit: 100 });
|
|
213
|
+
console.log(`${first.data.length} of ${first.totalCount} disclosures`);
|
|
214
|
+
|
|
215
|
+
for (let offset = 100; offset < (first.totalCount ?? 0); offset += 100) {
|
|
216
|
+
const page = await client.politicians.getActivity({ limit: 100, offset });
|
|
217
|
+
for (const trade of page.data) {
|
|
218
|
+
console.log(trade.politicianName, trade.ticker, trade.transactionType);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
128
223
|
### Entity Metrics
|
|
129
224
|
|
|
130
225
|
```typescript
|
|
@@ -215,12 +310,16 @@ All errors extend `SentiSenseError` and include `status`, `code`, and `message`
|
|
|
215
310
|
|
|
216
311
|
```typescript
|
|
217
312
|
const client = new SentiSense({
|
|
218
|
-
apiKey:
|
|
219
|
-
baseUrl: "https://...",
|
|
220
|
-
timeout: 30000,
|
|
313
|
+
apiKey: process.env.SENTISENSE_API_KEY, // Get yours at app.sentisense.ai/settings/developer
|
|
314
|
+
baseUrl: "https://...", // Default: https://app.sentisense.ai
|
|
315
|
+
timeout: 30000, // Default: 30s (in milliseconds)
|
|
316
|
+
maxRetries: 3, // Default: 3
|
|
221
317
|
});
|
|
222
318
|
```
|
|
223
319
|
|
|
320
|
+
Keep the key in the environment rather than in source. Committing a literal key leaks it
|
|
321
|
+
into git history and into every registry security scan that reads your repo.
|
|
322
|
+
|
|
224
323
|
## Get an API Key
|
|
225
324
|
|
|
226
325
|
Generate your API key from the [Developer Console](https://app.sentisense.ai/settings/developer).
|
package/dist/index.cjs
CHANGED
|
@@ -322,6 +322,19 @@ var Politicians = class {
|
|
|
322
322
|
*
|
|
323
323
|
* PRO-gated. Free/unauthenticated users receive a preview (top 5 trades)
|
|
324
324
|
* with `isPreview: true` in the response.
|
|
325
|
+
*
|
|
326
|
+
* The feed is longer than one response: a default 90-day window is routinely well over a
|
|
327
|
+
* thousand disclosures, and without `limit` the server sends the first 200 with no marker
|
|
328
|
+
* that it stopped. `totalCount` on the envelope is the real size on every tier, so page
|
|
329
|
+
* with `limit` and `offset` rather than reading `data.length` as the total.
|
|
330
|
+
*
|
|
331
|
+
* ```typescript
|
|
332
|
+
* const first = await client.politicians.getActivity({ limit: 100 });
|
|
333
|
+
* for (let offset = 100; offset < first.totalCount!; offset += 100) {
|
|
334
|
+
* const page = await client.politicians.getActivity({ limit: 100, offset });
|
|
335
|
+
* // ... page.data
|
|
336
|
+
* }
|
|
337
|
+
* ```
|
|
325
338
|
*/
|
|
326
339
|
async getActivity(options) {
|
|
327
340
|
return this.client.get("/api/v1/politicians/activity", options);
|
|
@@ -462,11 +475,20 @@ var Institutional = class {
|
|
|
462
475
|
* are two levels down: `(await getHolders(t, d)).data.holders`, alongside ticker-level
|
|
463
476
|
* totals like `holderCount`. Free callers get a truncated `holders` array with
|
|
464
477
|
* `isPreview: true`.
|
|
478
|
+
*
|
|
479
|
+
* A widely held ticker returns thousands of rows: a megacap quarter is about
|
|
480
|
+
* 6,000 holders and 1.5 MB. Pass `limit` unless you really want all of them.
|
|
481
|
+
* Omitting `options` sends the original unbounded request.
|
|
482
|
+
*
|
|
483
|
+
* `limit` is the switch for the whole option set. With it, the response also carries
|
|
484
|
+
* `returnedCount`, `offset`, and a `notableChanges` summary, so you can walk the list
|
|
485
|
+
* without re-counting it. Without it, `offset` / `sortBy` / `sortDir` are ignored by the
|
|
486
|
+
* server and you get the full unsorted list back with a 200.
|
|
465
487
|
*/
|
|
466
|
-
async getHolders(ticker, reportDate) {
|
|
488
|
+
async getHolders(ticker, reportDate, options) {
|
|
467
489
|
return this.client.get(
|
|
468
490
|
`/api/v1/institutional/holders/${encodeURIComponent(ticker)}`,
|
|
469
|
-
{ reportDate }
|
|
491
|
+
{ reportDate, ...options }
|
|
470
492
|
);
|
|
471
493
|
}
|
|
472
494
|
/**
|
|
@@ -531,7 +553,11 @@ var MarketMoodResource = class {
|
|
|
531
553
|
return this.client.get("/api/v2/market-mood");
|
|
532
554
|
}
|
|
533
555
|
// TODO: accept a `days` param to control history length (the endpoint supports ?days=N).
|
|
534
|
-
//
|
|
556
|
+
//
|
|
557
|
+
// Market Mood is also reachable through `client.indexes`, which serves it in the shared
|
|
558
|
+
// index envelope alongside fed-sentiment and ai-sentiment. Use this resource when you want
|
|
559
|
+
// the phase band, weekly change, per-signal breakdown and per-sector map; use `indexes`
|
|
560
|
+
// when you want every index to answer the same shape. Both report the same headline number.
|
|
535
561
|
};
|
|
536
562
|
|
|
537
563
|
// src/resources/marketSummary.ts
|
|
@@ -626,9 +652,17 @@ var Stocks = class {
|
|
|
626
652
|
async getEntities(ticker) {
|
|
627
653
|
return this.client.get(`/api/v1/stocks/${encodeURIComponent(ticker)}/entities`);
|
|
628
654
|
}
|
|
629
|
-
/**
|
|
655
|
+
/**
|
|
656
|
+
* Get AI-generated stock analysis report. Requires PRO tier.
|
|
657
|
+
*
|
|
658
|
+
* `depth: "deep"` returns the full curated report and consumes one report view on
|
|
659
|
+
* metered tiers; the default `"basic"` returns the one-paragraph summary.
|
|
660
|
+
*
|
|
661
|
+
* The deprecated `forceRefresh` option is accepted and discarded, not forwarded.
|
|
662
|
+
*/
|
|
630
663
|
async getAISummary(ticker, options) {
|
|
631
|
-
|
|
664
|
+
const { forceRefresh: _forceRefresh, ...params } = options ?? {};
|
|
665
|
+
return this.client.get(`/api/v1/stocks/${encodeURIComponent(ticker)}/ai-summary`, params);
|
|
632
666
|
}
|
|
633
667
|
/** Get sentiment/mention metrics breakdown by entity. */
|
|
634
668
|
async getMetricsBreakdown(ticker, metricType, options) {
|
|
@@ -720,8 +754,8 @@ var Stocks = class {
|
|
|
720
754
|
return this.client.get("/api/v1/stocks/with-kpis");
|
|
721
755
|
}
|
|
722
756
|
/**
|
|
723
|
-
* List the KPI metadata tuples available for a ticker
|
|
724
|
-
* chartType`
|
|
757
|
+
* List the KPI metadata tuples available for a ticker (`id, name, category,
|
|
758
|
+
* chartType`) without paying the cost of the full series payload. Mirrors
|
|
725
759
|
* the `/api/v1/insights/stock/{ticker}/types` precedent.
|
|
726
760
|
*
|
|
727
761
|
* Auth: API key required, no quota cost. 404 if the ticker has no curated KPIs.
|
|
@@ -733,13 +767,60 @@ var Stocks = class {
|
|
|
733
767
|
}
|
|
734
768
|
};
|
|
735
769
|
|
|
770
|
+
// src/resources/indexes.ts
|
|
771
|
+
var Indexes = class {
|
|
772
|
+
constructor(client) {
|
|
773
|
+
this.client = client;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* List every index the platform publishes: id, display name, one-line
|
|
777
|
+
* description, the scale it lives on, its access tier, and where its richest
|
|
778
|
+
* view lives.
|
|
779
|
+
*
|
|
780
|
+
* Iterate this rather than hardcoding ids. Every `indexId` it advertises
|
|
781
|
+
* resolves on {@link get} and {@link history}.
|
|
782
|
+
*/
|
|
783
|
+
async list() {
|
|
784
|
+
return this.client.get("/api/v1/indexes");
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Latest reading for one index.
|
|
788
|
+
*
|
|
789
|
+
* Check `constituents` for `null` before iterating: it is `null` on a
|
|
790
|
+
* composite index like `market-mood`, which has no constituents by
|
|
791
|
+
* construction. For Market Mood this is the narrowed view; the phase band,
|
|
792
|
+
* weekly change, per-signal breakdown and per-sector map live on
|
|
793
|
+
* `client.marketMood.get()`, and both report the same headline number.
|
|
794
|
+
*
|
|
795
|
+
* @param indexId slug from {@link list}, e.g. `"fed-sentiment"`.
|
|
796
|
+
*/
|
|
797
|
+
async get(indexId) {
|
|
798
|
+
return this.client.get(`/api/v1/indexes/${indexId}`);
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Historical scalar series for one index, for charting.
|
|
802
|
+
*
|
|
803
|
+
* Thin or low-coverage buckets are withheld, so the series can be shorter
|
|
804
|
+
* than `days` and can contain gaps. Plot against each point's `date`.
|
|
805
|
+
*
|
|
806
|
+
* @param indexId slug from {@link list}.
|
|
807
|
+
* @param days days of history to return. Defaults to the API's own 180.
|
|
808
|
+
*/
|
|
809
|
+
async history(indexId, days) {
|
|
810
|
+
return this.client.get(
|
|
811
|
+
`/api/v1/indexes/${indexId}/history`,
|
|
812
|
+
days === void 0 ? void 0 : { days }
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
|
|
736
817
|
// src/resources/trackers.ts
|
|
737
818
|
var Trackers = class {
|
|
738
819
|
constructor(client) {
|
|
739
820
|
this.client = client;
|
|
740
821
|
}
|
|
741
822
|
/**
|
|
742
|
-
* List every publicly-visible tracker
|
|
823
|
+
* List every publicly-visible tracker: id, display name, category,
|
|
743
824
|
* one-line description, and the methodology anchor to link out to.
|
|
744
825
|
*/
|
|
745
826
|
async list() {
|
|
@@ -753,8 +834,8 @@ var Trackers = class {
|
|
|
753
834
|
* `"choropleth"` they live at `data.geo[]`; etc. Dispatch on `viewType`
|
|
754
835
|
* in your renderer.
|
|
755
836
|
*
|
|
756
|
-
* @param trackerId
|
|
757
|
-
* @param params
|
|
837
|
+
* @param trackerId slug from {@link list}, e.g. `"institution-concentration"`.
|
|
838
|
+
* @param params provider-specific query params (e.g. `{ scope: "us" }` for
|
|
758
839
|
* geographically-scoped trackers like hantavirus). Unknown keys are ignored.
|
|
759
840
|
*/
|
|
760
841
|
async get(trackerId, params) {
|
|
@@ -763,7 +844,7 @@ var Trackers = class {
|
|
|
763
844
|
};
|
|
764
845
|
|
|
765
846
|
// src/version.ts
|
|
766
|
-
var VERSION = "0.
|
|
847
|
+
var VERSION = "0.35.0";
|
|
767
848
|
|
|
768
849
|
// src/client.ts
|
|
769
850
|
var DEFAULT_BASE_URL = "https://app.sentisense.ai";
|
|
@@ -775,12 +856,15 @@ var DEEP_HISTORY_FALLBACK_WAIT_S = 3;
|
|
|
775
856
|
var MAX_DEEP_HISTORY_WAIT_S = 30;
|
|
776
857
|
var MAX_RATE_LIMIT_WAIT_S = 120;
|
|
777
858
|
var RATE_LIMIT_FALLBACK_WAIT_S = 60;
|
|
778
|
-
function
|
|
779
|
-
if (!raw) return
|
|
859
|
+
function clampRetryAfter(raw, maxWaitS) {
|
|
860
|
+
if (!raw) return void 0;
|
|
780
861
|
const parsed = Number(raw);
|
|
781
|
-
if (!Number.isFinite(parsed)) return
|
|
862
|
+
if (!Number.isFinite(parsed)) return void 0;
|
|
782
863
|
return Math.min(Math.max(0.5, parsed), maxWaitS);
|
|
783
864
|
}
|
|
865
|
+
function retryAfterSeconds(raw, defaultS, maxWaitS) {
|
|
866
|
+
return clampRetryAfter(raw, maxWaitS) ?? defaultS;
|
|
867
|
+
}
|
|
784
868
|
function sleep(ms) {
|
|
785
869
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
786
870
|
}
|
|
@@ -802,6 +886,7 @@ var SentiSense = class {
|
|
|
802
886
|
this.marketMood = new MarketMoodResource(this);
|
|
803
887
|
this.marketSummary = new MarketSummaryResource(this);
|
|
804
888
|
this.kb = new KB(this);
|
|
889
|
+
this.indexes = new Indexes(this);
|
|
805
890
|
this.trackers = new Trackers(this);
|
|
806
891
|
this.calendar = new Calendar(this);
|
|
807
892
|
}
|
|
@@ -949,8 +1034,11 @@ var SentiSense = class {
|
|
|
949
1034
|
case 404:
|
|
950
1035
|
throw new NotFoundError(message, code);
|
|
951
1036
|
case 429: {
|
|
952
|
-
const
|
|
953
|
-
|
|
1037
|
+
const retryAfter = clampRetryAfter(
|
|
1038
|
+
response.headers.get("Retry-After"),
|
|
1039
|
+
MAX_RATE_LIMIT_WAIT_S
|
|
1040
|
+
);
|
|
1041
|
+
throw new RateLimitError(message, code, retryAfter);
|
|
954
1042
|
}
|
|
955
1043
|
default:
|
|
956
1044
|
throw new APIError(message, response.status, code);
|