Build on the marketplace for physical AI.
A real TypeScript SDK, honest API tooling, and a gamified course on modern web development that costs nothing and always will. Everything you need to ship against Node Data — and to become the kind of developer who ships.
41
API endpoints
0
SDK dependencies
38
Free lessons
190
Exercises
The SDK
One install. Every endpoint. Zero dependencies.
Typed against the live API — cursor pagination, retries with jittered backoff, streaming inference, and webhook verification that runs on Node, Bun, Deno, Workers, and Vercel Functions.
- Cursor pagination as a plain async iterable
- Typed error classes carrying the API's stable error codes
- Automatic retries on 429/5xx that honour Retry-After
- Web Crypto webhook verification — no Node built-ins
- Refuses to run a live key in a browser
import { NodeData } from "@node-data/sdk"; const nd = new NodeData({ apiKey: process.env.NODE_DATA_API_KEY }); // Cursor pagination, handled for youconst page = await nd.models.list({ q: "manipulation", limit: 20 }); for await (const asset of page) { console.log(asset.slug, asset.price.amount / 100, asset.downloads);}What's here
Four things, all of them real
No placeholder dashboards, no coming-soon tiles. Every tool on this site either talks to the live API or runs real code in your browser.
SDK
The TypeScript client, dogfooded by this portal — the playground and webhook tools run on it. Read the source, install it, or copy the parts you like.
API explorer
Every v1 endpoint with its required scope, parameters, response shape, and a copyable request. Links through to the canonical reference.
Tools
Live API playground, webhook signature signer and verifier, key inspector, and an inference cost calculator. All of them actually work.
Dev program
Learn modern web development the way you'd learn a language: two-minute lessons, streaks, XP, real code execution. No paywall, ever.
The Node Data Developer Program
Duolingo, but it teaches you to ship.
Most coding courses are forty-hour video walls you never finish. This is the opposite: two-minute lessons, one idea each, immediate feedback, and a streak you won't want to break.
2-minute lessons
One concept, five or six exercises, done before your coffee cools.
Streaks & XP
Daily goals, level-ups, and ranks named after rendering strategies.
Hearts
Five a session. Wrong answers cost one. They refill free — this is not a paywall.
Real code
JavaScript exercises execute in your browser, graded against real assertions.
The path
8 units · 38 lessons- 1The Web, Actually4 lessons · requests, responses, and what a browser really does→
- 2HTML & CSS That Holds Up5 lessons · semantics, accessibility, and modern layout→
- 3JavaScript for Real6 lessons · values, functions, async, and the parts that bite→
- 4TypeScript, Lightly4 lessons · just enough types to stop shipping the obvious bugs→
- 5React, the Mental Model5 lessons · state, rendering, and when not to use an effect→
- 6Next.js & Shipping5 lessons · server components, routing, caching, deploy→
- 7APIs, Auth & Webhooks5 lessons · integrate a real platform without getting burned→
- 8Ship It4 lessons · git, performance, debugging, security→
No account. No email. Progress lives in your browser — export it any time from /learn/progress.
API surface
Everything the platform exposes
Scoped bearer keys, cursor pagination, stable error codes, and an OpenAI-compatible inference endpoint.
| Method | Path | Scope | SDK |
|---|---|---|---|
| GET | /me | — | nd.account.retrieve() |
| GET | /models | models:read | nd.models.list() |
| GET | /models/{idOrSlug} | models:read | nd.models.retrieve(idOrSlug) |
| POST | /models | models:upload | nd.models.create(params) |
| PATCH | /models/{idOrSlug} | listings:write | nd.models.update(idOrSlug, params) |
| DELETE | /models/{idOrSlug} | listings:write | nd.models.del(idOrSlug) |
| GET | /payouts | payouts:read | nd.payouts.retrieve() |
| GET | /usage | — | nd.account.usage() |
| GET | /webhook_endpoints | webhooks:write | nd.webhookEndpoints.list() |
| POST | /webhook_endpoints | webhooks:write | nd.webhookEndpoints.create(params) |
| GET | /webhook_endpoints/{id} | webhooks:write | nd.webhookEndpoints.retrieve(id) |
| PATCH | /webhook_endpoints/{id} | webhooks:write | nd.webhookEndpoints.update(id, params) |
| DELETE | /webhook_endpoints/{id} | webhooks:write | nd.webhookEndpoints.del(id) |
| GET | /webhook_endpoints/{id}/events | webhooks:write | nd.webhookEndpoints.deliveries(id) |
| POST | /uploads | models:upload | nd.uploads.create() |
| GET | /uploads | models:upload | nd.uploads.limits() |
| POST | /models/{idOrSlug}/download | models:read | nd.models.download(idOrSlug) |
| GET | /datasets | datasets:read | nd.datasets.list() |
| POST | /datasets | datasets:upload | nd.datasets.create(params) |
| GET | /datasets/{idOrSlug} | datasets:read | nd.datasets.retrieve(idOrSlug) |
| POST | /datasets/{idOrSlug}/publish | listings:write | nd.datasets.publish(idOrSlug) |
| GET | /datasets/{idOrSlug}/shards | datasets:read | nd.datasets.shards(idOrSlug) |
| POST | /datasets/{idOrSlug}/shards | datasets:upload | nd.datasets.createShard(idOrSlug, params) |
| GET | /datasets/{idOrSlug}/shards/{name} | datasets:read | nd.datasets.shard(idOrSlug, name) |
| GET | /listings | listings:read | nd.listings.list() |
| POST | /listings | listings:write | nd.listings.create(params) |
| PATCH | /listings/{idOrSlug} | listings:write | nd.listings.update(idOrSlug, params) |
| POST | /listings/{idOrSlug}/publish | listings:write | nd.listings.publish(idOrSlug) |
| POST | /listings/{idOrSlug}/unpublish | listings:write | nd.listings.unpublish(idOrSlug) |
| GET | /purchases | listings:read | nd.purchases.list() |
| POST | /purchases | listings:read | nd.purchases.create(params) |
| GET | /balance_transactions | payouts:read | nd.balanceTransactions.list() |
| GET | /factory/jobs | factory:run | nd.factory.list() |
| POST | /factory/jobs | factory:run | nd.factory.create(params) |
| GET | /factory/jobs/{id} | factory:run | nd.factory.retrieve(id) |
| POST | /factory/jobs/{id}/approve | factory:run | nd.factory.approve(id) |
| POST | /factory/jobs/{id}/clarify | factory:run | nd.factory.clarify(id, answers) |
| POST | /factory/jobs/{id}/publish | factory:run | nd.factory.publish(id, params) |
| POST | /factory/jobs/{id}/regenerate | factory:run | nd.factory.regenerate(id, stage) |
| GET | /inference/models | — | nd.inference.models() |
| POST | /chat/completions | inference:run | nd.inference.create() / nd.inference.stream() |
Two commands and you're building.
Create a key on nodedata.ai, wire up the client, make your first request. The rest of this site is here for when you get stuck — or bored.
psst — press ⌘K. or the up arrow. twice.