cryptoetf 0.0.0-stage → 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cryptoetf.today
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,104 @@
1
- # Temporary Holding Version
1
+ # CryptoETF — TypeScript SDK for spot crypto ETF flows
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/cryptoetf?color=cb3837&logo=npm)](https://www.npmjs.com/package/cryptoetf)
4
+
5
+ The official client for the [CryptoETF API](https://docs.cryptoetf.today): daily net
6
+ flows of every US-listed spot crypto ETF, fund by fund, plus fund AUM, the CEFI
7
+ sentiment index and coin prices. Data by [cryptoetf.today](https://cryptoetf.today).
8
+
9
+ Sixteen assets: **BTC, ETH, SOL, XRP, HYPE, DOGE, LINK, AVAX, HBAR, LTC, BNB,
10
+ DOT, SUI, NEAR, TRX, ZEC**. Zero dependencies, fully typed, Node 18+, Deno, Bun
11
+ and modern browsers.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install cryptoetf
17
+ ```
18
+
19
+ ## Get an API key
20
+
21
+ Sign in at [cryptoetf.today](https://cryptoetf.today/en/profile) and create a key
22
+ in **Profile → For developers**. It is free.
23
+
24
+ ## Quick start
25
+
26
+ ```ts
27
+ import { CryptoETF } from "cryptoetf";
28
+
29
+ const etf = new CryptoETF({ apiKey: process.env.CRYPTOETF_API_KEY });
30
+
31
+ // Latest daily net flow for every asset, US$ millions
32
+ const { assets } = await etf.flows.summary();
33
+ for (const a of assets) console.log(a.symbol, a.date, a.netFlowUsdM);
34
+
35
+ // Bitcoin ETFs fund by fund
36
+ const btc = await etf.flows.funds("btc");
37
+ const lastDay = btc.days.at(-1);
38
+ console.log(lastDay?.funds.map((f) => `${f.ticker} ${f.netFlowUsdM}`));
39
+ ```
40
+
41
+ If `apiKey` is omitted, the client reads `CRYPTOETF_API_KEY` from the environment.
42
+
43
+ ## Methods
44
+
45
+ | Method | Endpoint | Returns |
46
+ |---|---|---|
47
+ | `flows.summary()` | `GET /v1/flows/summary` | Latest net flow per asset |
48
+ | `flows.asset(asset)` | `GET /v1/flows/{asset}` | Daily net flows of one asset |
49
+ | `flows.funds(asset)` | `GET /v1/flows/{asset}/funds` | Daily net flows of one asset, per fund |
50
+ | `funds(asset?)` | `GET /v1/funds` | Fund directory with AUM, fees, coins held |
51
+ | `cefi()` | `GET /v1/index/cefi` | CEFI sentiment index (0–100) and its history |
52
+ | `tickers()` | `GET /v1/tickers` | Tracked assets |
53
+ | `prices()` | `GET /v1/prices` | Spot prices of the 16 coins |
54
+ | `weekly()` | `GET /v1/analytics/weekly` | Seven-day totals per asset |
55
+
56
+ `asset` is a code such as `btc`, `eth`, `sol`, `hype` or a full name such as
57
+ `bitcoin`. All values are net US$ millions: positive = inflow, negative = outflow.
58
+ Full field reference: [docs.cryptoetf.today](https://docs.cryptoetf.today).
59
+
60
+ ## Errors and limits
61
+
62
+ Every failed call throws `CryptoETFError` with `status`, an optional `code` and,
63
+ on HTTP 429, `retryAfter` in seconds:
64
+
65
+ ```ts
66
+ import { CryptoETFError } from "cryptoetf";
67
+
68
+ try {
69
+ await etf.prices();
70
+ } catch (e) {
71
+ if (e instanceof CryptoETFError && e.status === 429) {
72
+ console.log(`rate limited, retry in ${e.retryAfter}s`);
73
+ }
74
+ }
75
+ ```
76
+
77
+ - **Rate limit:** 120 requests per minute per key. The latest limit headers are
78
+ available as `etf.rateLimit`.
79
+ - **History:** the free tier covers the **last 30 days**. Full history will be
80
+ available on a paid plan.
81
+ - **Freshness:** a trading day appears the next morning (UTC), before the US
82
+ open. The newest day can be revised once later that day. SUI funds arrive a
83
+ day later.
84
+
85
+ ## Options
86
+
87
+ ```ts
88
+ new CryptoETF({
89
+ apiKey: "…", // default: process.env.CRYPTOETF_API_KEY
90
+ timeoutMs: 15000, // per request
91
+ baseUrl: "https://api.cryptoetf.today/api/v1",
92
+ fetch: customFetch,
93
+ });
94
+ ```
95
+
96
+ ## Related
97
+
98
+ - **Python SDK:** [cryptoetf on PyPI](https://pypi.org/project/cryptoetf/), same methods.
99
+ - **No-code access for AI agents:** the hosted MCP server at
100
+ `https://mcp.cryptoetf.today/api/mcp`
101
+ ([@zerosix-studio/cryptoetf-mcp](https://www.npmjs.com/package/@zerosix-studio/cryptoetf-mcp)).
102
+ - **Embeddable widgets:** [cryptoetf.today/en/crypto-etf-widgets](https://cryptoetf.today/en/crypto-etf-widgets)
103
+
104
+ Not investment advice. MIT licensed.
@@ -0,0 +1,62 @@
1
+ import type { AssetCode, AssetFlows, AssetFundFlows, CefiIndex, Funds, FlowsSummary, Prices, RateLimit, Ticker, WeeklyAnalytics } from "./types.js";
2
+ export type * from "./types.js";
3
+ export declare const DEFAULT_BASE_URL = "https://api.cryptoetf.today/api/v1";
4
+ export interface CryptoETFOptions {
5
+ /** API key from https://cryptoetf.today/en/profile. Defaults to the `CRYPTOETF_API_KEY` environment variable. */
6
+ apiKey?: string;
7
+ /** Override the API root, e.g. for a proxy. */
8
+ baseUrl?: string;
9
+ /** Request timeout in milliseconds. Default 15 000. */
10
+ timeoutMs?: number;
11
+ /** Custom fetch implementation (tests, proxies, older runtimes). */
12
+ fetch?: typeof fetch;
13
+ }
14
+ /** Error returned by the API (4xx/5xx) or raised by the client (timeout, network). */
15
+ export declare class CryptoETFError extends Error {
16
+ /** HTTP status; 0 for network errors and timeouts. */
17
+ readonly status: number;
18
+ /** Machine-readable code when the API sends one, e.g. `api_key_suspended`. */
19
+ readonly code?: string;
20
+ /** Seconds to wait before retrying, on 429. */
21
+ readonly retryAfter?: number;
22
+ constructor(message: string, status: number, code?: string, retryAfter?: number);
23
+ }
24
+ /**
25
+ * Client for the CryptoETF public API: daily net flows of US spot crypto ETFs
26
+ * for 16 assets, fund by fund, plus fund AUM, the CEFI sentiment index and coin prices.
27
+ *
28
+ * ```ts
29
+ * const etf = new CryptoETF({ apiKey: "…" });
30
+ * const { assets } = await etf.flows.summary();
31
+ * ```
32
+ */
33
+ export declare class CryptoETF {
34
+ /** Rate-limit headers of the most recent response, or null before the first call. */
35
+ rateLimit: RateLimit | null;
36
+ private readonly apiKey;
37
+ private readonly baseUrl;
38
+ private readonly timeoutMs;
39
+ private readonly fetchImpl;
40
+ constructor(options?: CryptoETFOptions);
41
+ /** Daily net flows. */
42
+ readonly flows: {
43
+ /** Latest daily net flow for every asset. */
44
+ summary: () => Promise<FlowsSummary>;
45
+ /** Daily net flows of one asset over the history window (30 days on the free tier). */
46
+ asset: (asset: AssetCode | (string & {})) => Promise<AssetFlows>;
47
+ /** Daily net flows of one asset, fund by fund. */
48
+ funds: (asset: AssetCode | (string & {})) => Promise<AssetFundFlows>;
49
+ };
50
+ /** Fund directory with assets under management; all 16 assets unless `asset` is given. */
51
+ funds(asset?: AssetCode | (string & {})): Promise<Funds>;
52
+ /** CEFI — Crypto ETF Flow Index, a 0–100 sentiment gauge, with its recent history. */
53
+ cefi(): Promise<CefiIndex>;
54
+ /** Assets the API tracks. */
55
+ tickers(): Promise<Ticker[]>;
56
+ /** Spot prices of the 16 coins. */
57
+ prices(): Promise<Prices>;
58
+ /** Seven-day flow totals per asset. */
59
+ weekly(): Promise<WeeklyAnalytics>;
60
+ private get;
61
+ }
62
+ export default CryptoETF;
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ export const DEFAULT_BASE_URL = "https://api.cryptoetf.today/api/v1";
2
+ /** Error returned by the API (4xx/5xx) or raised by the client (timeout, network). */
3
+ export class CryptoETFError extends Error {
4
+ /** HTTP status; 0 for network errors and timeouts. */
5
+ status;
6
+ /** Machine-readable code when the API sends one, e.g. `api_key_suspended`. */
7
+ code;
8
+ /** Seconds to wait before retrying, on 429. */
9
+ retryAfter;
10
+ constructor(message, status, code, retryAfter) {
11
+ super(message);
12
+ this.name = "CryptoETFError";
13
+ this.status = status;
14
+ this.code = code;
15
+ this.retryAfter = retryAfter;
16
+ }
17
+ }
18
+ function envKey() {
19
+ const p = globalThis.process;
20
+ return p?.env?.CRYPTOETF_API_KEY;
21
+ }
22
+ /**
23
+ * Client for the CryptoETF public API: daily net flows of US spot crypto ETFs
24
+ * for 16 assets, fund by fund, plus fund AUM, the CEFI sentiment index and coin prices.
25
+ *
26
+ * ```ts
27
+ * const etf = new CryptoETF({ apiKey: "…" });
28
+ * const { assets } = await etf.flows.summary();
29
+ * ```
30
+ */
31
+ export class CryptoETF {
32
+ /** Rate-limit headers of the most recent response, or null before the first call. */
33
+ rateLimit = null;
34
+ apiKey;
35
+ baseUrl;
36
+ timeoutMs;
37
+ fetchImpl;
38
+ constructor(options = {}) {
39
+ const key = options.apiKey ?? envKey();
40
+ if (!key) {
41
+ throw new CryptoETFError("API key required. Get a free key at https://cryptoetf.today/en/profile and pass it as { apiKey } or set CRYPTOETF_API_KEY.", 401);
42
+ }
43
+ this.apiKey = key;
44
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
45
+ this.timeoutMs = options.timeoutMs ?? 15_000;
46
+ const f = options.fetch ?? globalThis.fetch;
47
+ if (!f)
48
+ throw new CryptoETFError("No fetch available: use Node 18+ or pass { fetch }.", 0);
49
+ this.fetchImpl = f.bind(globalThis);
50
+ }
51
+ /** Daily net flows. */
52
+ flows = {
53
+ /** Latest daily net flow for every asset. */
54
+ summary: () => this.get("/flows/summary"),
55
+ /** Daily net flows of one asset over the history window (30 days on the free tier). */
56
+ asset: (asset) => this.get(`/flows/${encodeURIComponent(asset)}`),
57
+ /** Daily net flows of one asset, fund by fund. */
58
+ funds: (asset) => this.get(`/flows/${encodeURIComponent(asset)}/funds`),
59
+ };
60
+ /** Fund directory with assets under management; all 16 assets unless `asset` is given. */
61
+ funds(asset) {
62
+ return this.get("/funds", asset ? { asset } : undefined);
63
+ }
64
+ /** CEFI — Crypto ETF Flow Index, a 0–100 sentiment gauge, with its recent history. */
65
+ cefi() {
66
+ return this.get("/index/cefi");
67
+ }
68
+ /** Assets the API tracks. */
69
+ tickers() {
70
+ return this.get("/tickers");
71
+ }
72
+ /** Spot prices of the 16 coins. */
73
+ prices() {
74
+ return this.get("/prices");
75
+ }
76
+ /** Seven-day flow totals per asset. */
77
+ weekly() {
78
+ return this.get("/analytics/weekly");
79
+ }
80
+ async get(path, query) {
81
+ const url = new URL(this.baseUrl + path);
82
+ if (query)
83
+ for (const [k, v] of Object.entries(query))
84
+ url.searchParams.set(k, v);
85
+ const ctrl = new AbortController();
86
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
87
+ let res;
88
+ try {
89
+ res = await this.fetchImpl(url, {
90
+ headers: { Authorization: `Bearer ${this.apiKey}`, Accept: "application/json" },
91
+ signal: ctrl.signal,
92
+ });
93
+ }
94
+ catch (err) {
95
+ const timedOut = err?.name === "AbortError";
96
+ throw new CryptoETFError(timedOut ? `Request timed out after ${this.timeoutMs} ms` : `Network error: ${err?.message ?? err}`, 0);
97
+ }
98
+ finally {
99
+ clearTimeout(timer);
100
+ }
101
+ const limit = Number(res.headers.get("x-ratelimit-limit"));
102
+ if (limit) {
103
+ this.rateLimit = {
104
+ limit,
105
+ remaining: Number(res.headers.get("x-ratelimit-remaining")),
106
+ reset: Number(res.headers.get("x-ratelimit-reset")),
107
+ };
108
+ }
109
+ const body = await res.json().catch(() => null);
110
+ if (!res.ok) {
111
+ const b = (body ?? {});
112
+ const message = Array.isArray(b.message) ? b.message.join("; ") : typeof b.message === "string" ? b.message : res.statusText || `HTTP ${res.status}`;
113
+ const retry = Number(res.headers.get("retry-after"));
114
+ throw new CryptoETFError(message, res.status, typeof b.code === "string" ? b.code : undefined, retry || undefined);
115
+ }
116
+ return body;
117
+ }
118
+ }
119
+ export default CryptoETF;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Response shapes of the CryptoETF public API v1 (https://docs.cryptoetf.today).
3
+ * Flow values are net US$ millions, rounded to one decimal: positive = inflow,
4
+ * negative = outflow. Dates are `YYYY-MM-DD` (UTC trading days), timestamps ISO 8601.
5
+ */
6
+ /** Asset accepted by the per-asset endpoints. Case-insensitive; full names (`bitcoin`, `solana`, …) and `hype` also work. */
7
+ export type AssetCode = "btc" | "eth" | "sol" | "xrp" | "hyp" | "doge" | "link" | "avax" | "hbar" | "ltc" | "bnb" | "dot" | "sui" | "near" | "trx" | "zec";
8
+ export interface FlowsSummaryItem {
9
+ /** Uppercase ticker of the coin, e.g. `BTC`. */
10
+ symbol: string;
11
+ /** Internal asset id, e.g. `bitcoin`, `hyp`. */
12
+ asset: string;
13
+ /** Latest trading day with data, or null if the asset has none yet. */
14
+ date: string | null;
15
+ netFlowUsdM: number;
16
+ }
17
+ export interface FlowsSummary {
18
+ assets: FlowsSummaryItem[];
19
+ updatedAt: string;
20
+ }
21
+ export interface FlowPoint {
22
+ date: string;
23
+ netFlowUsdM: number;
24
+ }
25
+ export interface AssetFlows {
26
+ symbol: string;
27
+ asset: string;
28
+ /** History window in days (30 on the free tier). */
29
+ windowDays: number;
30
+ days: FlowPoint[];
31
+ updatedAt: string;
32
+ }
33
+ export interface FundFlow {
34
+ ticker: string;
35
+ issuer: string;
36
+ /** Null when the fund has not reported this day yet. */
37
+ netFlowUsdM: number | null;
38
+ }
39
+ export interface FundDay {
40
+ date: string;
41
+ totalUsdM: number;
42
+ funds: FundFlow[];
43
+ }
44
+ export interface AssetFundFlows {
45
+ symbol: string;
46
+ asset: string;
47
+ windowDays: number;
48
+ days: FundDay[];
49
+ updatedAt: string;
50
+ }
51
+ export interface AssetNetAssets {
52
+ symbol: string;
53
+ asset: string;
54
+ totalNetAssetsUsdM: number | null;
55
+ date: string | null;
56
+ }
57
+ export interface Fund {
58
+ ticker: string;
59
+ name: string;
60
+ issuer: string;
61
+ symbol: string;
62
+ asset: string;
63
+ exchange: string;
64
+ listedAt: string;
65
+ /** Sponsor fee, percent per year. */
66
+ sponsorFeePct: number | null;
67
+ netAssetsUsdM: number | null;
68
+ coinsHeld: number | null;
69
+ coinsHeldDate: string | null;
70
+ netAssetsDate: string | null;
71
+ netAssetsBasis: "reported" | "repriced" | "estimated" | null;
72
+ }
73
+ export interface Funds {
74
+ assets: AssetNetAssets[];
75
+ funds: Fund[];
76
+ updatedAt: string;
77
+ }
78
+ export interface CefiComponents {
79
+ flow: number;
80
+ trend: number;
81
+ breadth: number;
82
+ }
83
+ export interface IndexPoint {
84
+ date: string;
85
+ value: number;
86
+ }
87
+ export interface CefiIndex {
88
+ index: string;
89
+ /** 0–100: below 25 extreme fear, above 75 extreme greed. */
90
+ current: number;
91
+ components: CefiComponents | null;
92
+ date: string;
93
+ windowDays: number;
94
+ history: IndexPoint[];
95
+ }
96
+ export interface Ticker {
97
+ symbol: string;
98
+ asset: string;
99
+ name: string;
100
+ }
101
+ export interface Price {
102
+ symbol: string;
103
+ priceUsd: number;
104
+ change24hPct: number;
105
+ }
106
+ export interface Prices {
107
+ prices: Price[];
108
+ updatedAt: string;
109
+ }
110
+ export interface WeeklyAsset {
111
+ symbol: string;
112
+ asset: string;
113
+ netFlowUsdM: number;
114
+ avgDailyUsdM: number;
115
+ positiveDays: number;
116
+ negativeDays: number;
117
+ }
118
+ export interface WeeklyAnalytics {
119
+ /** First day of the seven-day window. */
120
+ from: string;
121
+ /** Last day of the window (today, UTC). */
122
+ to: string;
123
+ assets: WeeklyAsset[];
124
+ }
125
+ /** Rate-limit state taken from the headers of the latest response. */
126
+ export interface RateLimit {
127
+ limit: number;
128
+ remaining: number;
129
+ /** Unix time (seconds) when the current window resets. */
130
+ reset: number;
131
+ }
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Response shapes of the CryptoETF public API v1 (https://docs.cryptoetf.today).
3
+ * Flow values are net US$ millions, rounded to one decimal: positive = inflow,
4
+ * negative = outflow. Dates are `YYYY-MM-DD` (UTC trading days), timestamps ISO 8601.
5
+ */
6
+ export {};
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "cryptoetf",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "Official TypeScript client for the CryptoETF API: daily net flows of US spot crypto ETFs for 16 assets (BTC, ETH, SOL, XRP, HYPE, DOGE, LINK, AVAX, HBAR, LTC, BNB, DOT, SUI, NEAR, TRX, ZEC), fund by fund, plus fund AUM, the CEFI sentiment index and coin prices. Zero dependencies.",
5
+ "keywords": [
6
+ "crypto",
7
+ "etf",
8
+ "bitcoin-etf",
9
+ "ethereum-etf",
10
+ "etf-flows",
11
+ "spot-etf",
12
+ "ibit",
13
+ "api",
14
+ "sdk"
15
+ ],
16
+ "homepage": "https://cryptoetf.today/en/api",
17
+ "bugs": {
18
+ "url": "https://github.com/zerosix-studio/cryptoetf-sdk/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/zerosix-studio/cryptoetf-sdk.git",
23
+ "directory": "typescript"
24
+ },
25
+ "license": "MIT",
26
+ "author": "cryptoetf.today",
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "sideEffects": false,
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "scripts": {
46
+ "build": "tsc -p tsconfig.json",
47
+ "test": "npm run build && node --test test/*.test.mjs",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "devDependencies": {
51
+ "typescript": "^5.9.3"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }