@node-data/sdk
The official client for the Node Data API. It's also what this portal runs on — the playground and the signature tool import it from source, so if it breaks here, it's broken.
Install
A real npm package — ESM and CommonJS entry points, bundled type declarations. It just isn't on the public registry yet, so you install it from a URL instead of by name.
npm install https://www.nodedata.dev/node-data-sdk-latest.tgzThat installs under the real package name, so imports read @node-data/sdk and switching to the registry later is a one-line change. The tarball is rebuilt by every deploy, so it always matches what this site runs on.
Module formats
ESM + CJS
Types
bundled .d.ts
Dependencies
none
Both module systems, one package
import resolves to the ESM build and require() to the CommonJS one, via the package's exports map. Type declarations are emitted for each, so moduleResolution of bundler, node16, or nodenext all resolve correctly — each of those is checked in CI.import { NodeData } from "@node-data/sdk"; const nd = new NodeData();Prefer to read it first? The full source is in this portal's repo under sdk/ — about 900 lines across eight files, no build step needed if you're already compiling TypeScript.
First call
ping() is one round trip that confirms the key works and reports what it's allowed to do.
1import { NodeData } from "@node-data/sdk";2 3// apiKey defaults to process.env.NODE_DATA_API_KEY4const nd = new NodeData();5 6console.log(await nd.ping());7// { ok: true, latencyMs: 141, account: "casper-white-2", scopes: ["models:read"] }8 9console.log(nd.mode); // "live" — read from the key prefix, no network call10console.log(nd.maskedKey); // "nd_live_a1b2••••••••••••••••••••" — safe to logWhat it does for you
Zero dependencies
Nothing but the platform. Runs on Node 18+, Bun, Deno, Cloudflare Workers, and Vercel Functions with the same code.
Typed against the live API
Wire types mirror the JSON exactly — snake_case and all — so there's no hidden mapping layer to drift out of sync.
Pagination as iteration
A Page is both the first page and an async iterable over every page. for await, and you're done.
Errors you can branch on
Typed subclasses per status, each carrying the API's stable error code and the x-request-id for support.
Retries that behave
429/5xx and network failures retry with full-jitter exponential backoff, honouring Retry-After. Streams never do.
Refuses to leak your key
Constructing a client with a live key in a browser throws, because minification is not encryption.
The four things you'll actually do
Read the marketplace, publish to it, react to events, and run inference.
// One pageconst page = await nd.models.list({ q: "grasping", type: "policy", limit: 20 });page.items; // Asset[]page.hasMore; // booleanpage.nextCursor; // string | null // Every page, lazily — memory stays flatfor await (const asset of page) { console.log(asset.slug, asset.downloads);} // With a ceilingconst first200 = await page.all(200); // One listing, by id or slugconst asset = await nd.models.retrieve("bin-picking-policy-v2");Errors
Catch the case you can handle, re-throw the rest. Every error carries status, code, and requestId.
import { PermissionError, RateLimitError, PaymentRequiredError, ApiError,} from "@node-data/sdk"; try { await nd.payouts.retrieve();} catch (err) { if (err instanceof PermissionError) { console.error(`Key needs the ${err.requiredScope} scope`); } else if (err instanceof RateLimitError) { await sleep((err.retryAfter ?? 5) * 1000); } else if (err instanceof PaymentRequiredError) { console.error("Premium key not activated"); } else if (err instanceof ApiError) { report(err.code, err.status, err.requestId); } else { throw err; }}Full table of codes on the errors page.
Configuration
const nd = new NodeData({ apiKey: process.env.NODE_DATA_API_KEY, // or NODE_DATA_API_KEY implicitly baseUrl: "https://www.nodedata.ai/api/v1", // or NODE_DATA_BASE_URL timeout: 60_000, maxRetries: 2, headers: { "x-my-service": "ledger-sync" }, fetch: instrumentedFetch, // tests, proxies, tracing onRequest: ({ method, url, attempt }) => log(method, url, attempt),}); // Per-call overrides, including cancellationconst controller = new AbortController();await nd.models.list({ limit: 100 }, { timeout: 5_000, maxRetries: 0, signal: controller.signal,}); // Escape hatch for endpoints the SDK doesn't model yet —// same auth, retries, and error mapping.const data = await nd.request("GET", "/some/new/endpoint", { query: { limit: 10 },});Other languages
The API is plain HTTP + JSON, so any language works. Two clients exist today.
@node-data/sdk
v0.4.0TypeScript and JavaScript. Complete: every endpoint, streaming, webhook verification. ESM + CJS with bundled types, installable today from the tarball above.
Reference →nodedata
v0.1.0Python, earlier stage. Lives in packages/sdk-python in the Node Data repo and is not on PyPI yet — check its README for what's covered before relying on it.
SDK docs on nodedata.ai ↗Everything the client can do, one page.