TypeScript SDKv0.4.00 dependencies

@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.

BASH
npm install https://www.nodedata.dev/node-data-sdk-latest.tgz

That 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.
app.ts
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.

hello.ts
1import { NodeData } from "@node-data/sdk";
2
3// apiKey defaults to process.env.NODE_DATA_API_KEY
4const 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 call
10console.log(nd.maskedKey); // "nd_live_a1b2••••••••••••••••••••" — safe to log

What 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.

read.ts
// One page
const page = await nd.models.list({ q: "grasping", type: "policy", limit: 20 });
page.items; // Asset[]
page.hasMore; // boolean
page.nextCursor; // string | null
// Every page, lazily — memory stays flat
for await (const asset of page) {
console.log(asset.slug, asset.downloads);
}
// With a ceiling
const first200 = await page.all(200);
// One listing, by id or slug
const 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.

errors.ts
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

config.ts
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 cancellation
const 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.0

TypeScript and JavaScript. Complete: every endpoint, streaming, webhook verification. ESM + CJS with bundled types, installable today from the tarball above.

Reference →

nodedata

v0.1.0

Python, 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.