@pythfeeds/sdk 1.0.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.
Files changed (4) hide show
  1. package/README.md +57 -0
  2. package/index.d.ts +33 -0
  3. package/index.js +73 -0
  4. package/package.json +20 -0
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @pythfeeds/sdk
2
+
3
+ Official JavaScript/TypeScript client for the [PythFeeds API](https://pythfeeds.com/developers) — real-time Pyth oracle prices, market data and live streaming. Works in browsers and Node 18+.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @pythfeeds/sdk
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import { PythFeeds } from "@pythfeeds/sdk";
15
+
16
+ const pf = new PythFeeds("pf_live_xxx");
17
+
18
+ // Single price
19
+ const btc = await pf.getPrice("BTC");
20
+ console.log(btc.price, btc.confidence);
21
+
22
+ // Batch
23
+ const prices = await pf.getPrices(["BTC", "ETH", "SOL"]);
24
+
25
+ // Market data
26
+ const coins = await pf.getCoins({ page: 1, perPage: 50 });
27
+ const chart = await pf.getChart("bitcoin", 30);
28
+ const global = await pf.getGlobal();
29
+ ```
30
+
31
+ ### Live streaming (SSE)
32
+
33
+ ```js
34
+ const close = pf.stream(["BTC", "ETH"], (update) => {
35
+ console.log("tick", update);
36
+ });
37
+ // later: close();
38
+ ```
39
+
40
+ In Node, provide an `EventSource` first:
41
+
42
+ ```js
43
+ globalThis.EventSource = (await import("eventsource")).default;
44
+ ```
45
+
46
+ ## Errors
47
+
48
+ Failed calls throw `PythFeedsError` with `.status` and a stable `.code` (`missing_key`, `invalid_key`, `rate_limited`, `quota_exceeded`).
49
+
50
+ ```js
51
+ import { PythFeedsError } from "@pythfeeds/sdk";
52
+ try { await pf.getPrice("BTC"); }
53
+ catch (e) { if (e instanceof PythFeedsError && e.code === "rate_limited") { /* back off */ } }
54
+ ```
55
+
56
+ ## Rate limits & quota
57
+ Responses carry `RateLimit-*` (per-minute) and `X-Quota-*` (monthly) headers. See [pythfeeds.com/pricing](https://pythfeeds.com/pricing).
package/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ export interface PythPrice {
2
+ symbol: string;
3
+ price: number;
4
+ confidence: number;
5
+ exponent: number;
6
+ publishTime: number;
7
+ [k: string]: unknown;
8
+ }
9
+
10
+ export interface PythFeedsOptions {
11
+ baseUrl?: string;
12
+ fetch?: typeof fetch;
13
+ }
14
+
15
+ export class PythFeedsError extends Error {
16
+ status?: number;
17
+ code?: string;
18
+ }
19
+
20
+ export class PythFeeds {
21
+ constructor(apiKey: string, options?: PythFeedsOptions);
22
+ getPrice(symbol: string): Promise<PythPrice>;
23
+ getPrices(symbols: string[] | string): Promise<Record<string, PythPrice> | PythPrice[]>;
24
+ getFeeds(query?: string): Promise<unknown>;
25
+ getCoins(opts?: { page?: number; perPage?: number }): Promise<unknown[]>;
26
+ getCoin(id: string): Promise<unknown>;
27
+ getOHLC(id: string, days?: number): Promise<unknown>;
28
+ getChart(id: string, days?: number): Promise<unknown>;
29
+ getGlobal(): Promise<unknown>;
30
+ stream(symbols: string[] | string, onUpdate: (update: unknown) => void, onError?: (err: unknown) => void): () => void;
31
+ }
32
+
33
+ export default PythFeeds;
package/index.js ADDED
@@ -0,0 +1,73 @@
1
+ // PythFeeds official JS/TS SDK — a thin wrapper over the public API.
2
+ // Works in browsers and Node 18+ (global fetch). MIT licensed.
3
+
4
+ const DEFAULT_BASE = "https://pythfeeds.com/api/v1";
5
+
6
+ export class PythFeedsError extends Error {
7
+ constructor(message, status, code) {
8
+ super(message);
9
+ this.name = "PythFeedsError";
10
+ this.status = status;
11
+ this.code = code;
12
+ }
13
+ }
14
+
15
+ export class PythFeeds {
16
+ /**
17
+ * @param {string} apiKey Your API key (create one at pythfeeds.com/developers).
18
+ * @param {{ baseUrl?: string, fetch?: typeof fetch }} [options]
19
+ */
20
+ constructor(apiKey, options = {}) {
21
+ if (!apiKey) throw new PythFeedsError("apiKey is required");
22
+ this.apiKey = apiKey;
23
+ this.baseUrl = (options.baseUrl || DEFAULT_BASE).replace(/\/$/, "");
24
+ this._fetch = options.fetch || globalThis.fetch;
25
+ if (!this._fetch) throw new PythFeedsError("No fetch available — pass options.fetch on older runtimes.");
26
+ }
27
+
28
+ async _get(path, params) {
29
+ const url = new URL(this.baseUrl + path);
30
+ if (params) for (const [k, v] of Object.entries(params)) if (v != null) url.searchParams.set(k, String(v));
31
+ const res = await this._fetch(url.toString(), { headers: { "x-api-key": this.apiKey, accept: "application/json" } });
32
+ let body = {};
33
+ try { body = await res.json(); } catch { /* non-JSON */ }
34
+ if (!res.ok) throw new PythFeedsError(body.error || `HTTP ${res.status}`, res.status, body.code);
35
+ return body;
36
+ }
37
+
38
+ /** Real-time Pyth price for one symbol, e.g. "BTC". */
39
+ getPrice(symbol) { return this._get(`/price/${encodeURIComponent(symbol)}`); }
40
+ /** Real-time prices for many symbols in one call. */
41
+ getPrices(symbols) { return this._get("/prices", { symbols: Array.isArray(symbols) ? symbols.join(",") : symbols }); }
42
+ /** The full Pyth feed catalog (optionally filtered by query). */
43
+ getFeeds(query) { return this._get("/feeds", query ? { query } : undefined); }
44
+ /** Top coins by market cap. */
45
+ getCoins({ page = 1, perPage = 100 } = {}) { return this._get("/coins", { page, per_page: perPage }); }
46
+ /** Full detail for one coin. */
47
+ getCoin(id) { return this._get(`/coins/${encodeURIComponent(id)}`); }
48
+ /** OHLC candles. days ∈ {1,7,14,30,90,180,365}. */
49
+ getOHLC(id, days = 7) { return this._get(`/coins/${encodeURIComponent(id)}/ohlc`, { days }); }
50
+ /** Historical price / market-cap / volume series. */
51
+ getChart(id, days = 7) { return this._get(`/coins/${encodeURIComponent(id)}/chart`, { days }); }
52
+ /** Global market cap, 24h volume and BTC dominance. */
53
+ getGlobal() { return this._get("/global"); }
54
+
55
+ /**
56
+ * Subscribe to the live price stream (Server-Sent Events).
57
+ * Browser-native; in Node install `eventsource` and set `globalThis.EventSource`.
58
+ * @returns {() => void} a function that closes the stream.
59
+ */
60
+ stream(symbols, onUpdate, onError) {
61
+ const syms = Array.isArray(symbols) ? symbols.join(",") : symbols;
62
+ const ES = globalThis.EventSource;
63
+ if (!ES) throw new PythFeedsError("EventSource unavailable — in Node: `globalThis.EventSource = require('eventsource')`.");
64
+ // EventSource can't send headers, so the key goes in the query string.
65
+ const url = `${this.baseUrl}/stream?symbols=${encodeURIComponent(syms)}&api_key=${encodeURIComponent(this.apiKey)}`;
66
+ const es = new ES(url);
67
+ es.onmessage = (e) => { try { onUpdate(JSON.parse(e.data)); } catch { /* skip */ } };
68
+ if (onError) es.onerror = onError;
69
+ return () => es.close();
70
+ }
71
+ }
72
+
73
+ export default PythFeeds;
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@pythfeeds/sdk",
3
+ "version": "1.0.0",
4
+ "description": "Official JavaScript/TypeScript SDK for the PythFeeds API — real-time Pyth oracle prices, market data and live SSE streaming.",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "module": "index.js",
8
+ "types": "index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./index.js"
13
+ }
14
+ },
15
+ "files": ["index.js", "index.d.ts", "README.md"],
16
+ "keywords": ["pyth", "pythfeeds", "crypto", "prices", "oracle", "market-data", "sdk"],
17
+ "homepage": "https://pythfeeds.com/developers",
18
+ "license": "MIT",
19
+ "engines": { "node": ">=18" }
20
+ }