# REST API overview

> The Nodaro REST API runs workflows and single nodes, polls jobs, uploads media and manages assets over HTTPS with JSON and a bearer token.

Source: https://nodaro.ai/docs/developers/api

The **Nodaro REST API** is the HTTP interface to Nodaro: it runs workflows and single nodes, reports the status of their jobs, stores your media, and manages characters, presets and workspaces. Requests and responses are JSON over HTTPS, and every request carries a bearer token. The same API serves Nodaro Cloud and self-hosted installs, and the [TypeScript SDK](https://nodaro.ai/docs/developers/sdk) and the [CLI](https://nodaro.ai/docs/developers/cli) are thin clients of it.

## Base URL

| Where Nodaro runs | Base URL |
| --- | --- |
| Nodaro Cloud | `https://app.nodaro.ai` |
| A self-hosted install | The install's own address, for example `http://localhost:3000` for a default Community Edition install |

Every path starts with `/v1/`, for example `https://app.nodaro.ai/v1/nodes`. Some endpoints exist only on Nodaro Cloud, such as credits and organizations, and answer `404` on other editions. Each page says when an endpoint is limited.

## Authentication

Send `Authorization: Bearer <token>` on every request. Use a personal API token (`ndr_…`) from **Settings › API Tokens** for your own account, an OAuth access token (`ndr_app_…`) when your product acts for other Nodaro users, or your session JWT on a Community Edition install. A handful of discovery endpoints, such as `GET /v1/nodes` and `GET /v1/models`, need no token at all. Read [Authentication](https://nodaro.ai/docs/developers/api/authentication) to create a token.

## Your first call

This example generates an image with one node and reads the result. Generation is asynchronous: the first call returns a job id, and you poll the job until it is complete.

**curl**

```bash
export NODARO_API_KEY="ndr_..."

# 1. Start the job
curl -s -X POST https://app.nodaro.ai/v1/generate-image \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
"prompt": "a snow leopard on a mountain ridge at dawn",
"provider": "nano-banana-pro",
"aspectRatio": "16:9"
}'
# {"jobId":"0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10"}

# 2. Poll the job until its status is "completed"
curl -s https://app.nodaro.ai/v1/jobs/0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10/status \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

```json
{
"data": {
"id": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10",
"status": "completed",
"progress": 100,
"output_data": { "imageUrl": "https://…/0f1a9c2e.png" },
"error_message": null
}
}
```

**TypeScript SDK**

```ts

const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

// Starts the job and polls it until it finishes.
const output = await client.nodes.runAndWait('generate-image', {
prompt: 'a snow leopard on a mountain ridge at dawn',
provider: 'nano-banana-pro',
aspectRatio: '16:9',
})
console.log(output.imageUrl)
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a snow leopard on a mountain ridge at dawn" \
  --param provider=nano-banana-pro \
  --param aspectRatio=16:9 \
  --watch --json | jq -r '.output_data.imageUrl'
```

The same pattern runs every generation node: `POST /v1/<node-type>` with the node's settings, then poll the job. See [Run a single node](https://nodaro.ai/docs/developers/api/nodes).

## Request and response conventions

- **JSON in, JSON out.** Send bodies as JSON with `Content-Type: application/json`. File uploads are the exception: they use `multipart/form-data`.
- **Ids are UUIDs.** A malformed id answers `400 validation_error`.
- **Most responses are wrapped in `data`.** A read returns `{ "data": … }`, and a delete or a cancel returns `{ "success": true }`. The legacy workflow endpoints under `/v1/api/` return their payload directly.
- **Generations return a job id.** `POST /v1/<node-type>` answers with `{ "jobId": "…" }`, sometimes with `adjustments` or `warnings` beside it.
- **Lists use cursors.** A list returns a cursor, usually `nextCursor` (`next` on `GET /v1/jobs`). Pass it back as `?cursor=` for the next page. A `null` cursor means there are no more rows. Cursors are opaque: never parse or build one.
- **Two field styles.** Job objects use snake_case, such as `output_data` and `created_at`. Workflows, executions and most other resources use camelCase.
- **Responses grow.** New fields appear over time. Ignore the fields you do not know instead of failing on them.
- **Errors share one shape.** A failed call returns an HTTP status and `{ "error": { "code": "…", "message": "…" } }`. Dispatch on `code`. See [Errors](https://nodaro.ai/docs/developers/api/errors).

Two optional headers change how a request is handled:

| Header | What it does |
| --- | --- |
| `X-Nodaro-Workspace` | Acts in one workspace of an organization: which workspace a list reads and where a create lands. See [Workspaces](https://nodaro.ai/docs/developers/api/workspaces). |
| `X-Nodaro-Client` | Records which client created a job: `sdk/<version>`, `cli/<version>` or `extension/<name>`. See [Identify your client](https://nodaro.ai/docs/developers/api/authentication#identify-your-client). |

## Sync or async

Most work in Nodaro takes seconds to minutes, so the API is asynchronous. A generation route answers at once with a `jobId`, and a workflow run answers `202 Accepted` with an `executionId`. You then poll the [job](https://nodaro.ai/docs/developers/api/jobs) or the [execution](https://nodaro.ai/docs/developers/api/executions) every 2 to 5 seconds until it reaches a final status. For a workflow you expect to finish in under a minute, `POST /v1/api/run?wait=true` holds the connection for up to 600 seconds and returns the result. A few routes answer synchronously, such as inline text nodes, free media processing and the structured LLM call. Read [Sync or async](https://nodaro.ai/docs/developers/api/workflows#sync-or-async) for the details.

## Endpoints by area

### Run things

| Page | What it covers | Main endpoints |
| --- | --- | --- |
| [Workflows](https://nodaro.ai/docs/developers/api/workflows) | Run a saved workflow, with or without inputs, and manage workflows | `POST /v1/workflows/:id/run`, `POST /v1/api/run`, `GET /v1/api/schema` |
| [Nodes](https://nodaro.ai/docs/developers/api/nodes) | Run one node without a workflow, and discover nodes, models and pickers | `POST /v1/<node-type>`, `GET /v1/nodes`, `GET /v1/models` |
| [Jobs](https://nodaro.ai/docs/developers/api/jobs) | Job status and results, batch polling, cancelling, Video Pro run control | `GET /v1/jobs/:id/status`, `POST /v1/jobs/batch-status` |
| [Executions](https://nodaro.ai/docs/developers/api/executions) | The status and history of workflow runs | `GET /v1/workflow-executions/:id`, `GET /v1/workflows/:id/executions` |
| [Uploads](https://nodaro.ai/docs/developers/api/uploads) | Upload images, video and audio, or copy a URL into storage | `POST /v1/upload`, `POST /v1/save-to-storage` |
| [Webhooks](https://nodaro.ai/docs/developers/api/webhooks) | Start a workflow from an HTTP call or a schedule, and send results out | `POST /v1/webhooks/:token`, `POST /v1/workflow-triggers` |

### Resources

| Page | What it covers | Main endpoints |
| --- | --- | --- |
| [Characters](https://nodaro.ai/docs/developers/api/characters) | Characters, portrait candidates, expressions, poses and motion | `/v1/characters`, `POST /v1/generate-character` |
| [Objects](https://nodaro.ai/docs/developers/api/objects) | Props, products and vehicles with their main image and variants | `/v1/objects`, `POST /v1/generate-object` |
| [Locations](https://nodaro.ai/docs/developers/api/locations) | Places with their main image and variants | `/v1/locations`, `POST /v1/generate-location` |
| [Creatures](https://nodaro.ai/docs/developers/api/creatures) | Creatures with their main image and variants | `/v1/creatures` |
| [Presets](https://nodaro.ai/docs/developers/api/presets) | Your node presets and the built-in catalog, read-only | `GET /v1/node-presets`, `GET /v1/node-presets/factory` |
| [Community](https://nodaro.ai/docs/developers/api/community) | Browse and clone shared characters, locations and objects | `GET /v1/community/browse` |
| [Pipelines](https://nodaro.ai/docs/developers/api/pipelines) | Story-to-Video pipelines | `POST /v1/pipelines/:id/branch` |
| [Prompt Wizard](https://nodaro.ai/docs/developers/api/prompt-wizard) | Improve a prompt for a generation node | `POST /v1/prompt-helper/wizard` |
| [Recast](https://nodaro.ai/docs/developers/api/recast) | Regenerate an analyzed video with your own cast | `POST /v1/recast` |
| [Studio productions](https://nodaro.ai/docs/developers/api/studio-productions) | Shot-by-shot productions | `/v1/studio/productions` |
| [Voice and media](https://nodaro.ai/docs/developers/api/voice-and-media) | Voices, voice changing, dubbing, media import and audio tools | `/v1/voices`, `/v1/download-video`, `/v1/transcribe` |
| [Character training](https://nodaro.ai/docs/developers/api/character-training) | Train a model on one character | `POST /v1/characters/:id/train` |
| [3D scenes](https://nodaro.ai/docs/developers/api/3d-scenes) | Editable 3D scenes and 3D Render Pro | `POST /v1/3d-scene/generate`, `POST /v1/pro-3d-render` |

### Account

| Page | What it covers | Main endpoints |
| --- | --- | --- |
| [Workspaces and organizations](https://nodaro.ai/docs/developers/api/workspaces) | Acting in a workspace, organizations, members, invitations and usage | `/v1/orgs`, `/v1/workspaces` |
| [Credits](https://nodaro.ai/docs/developers/api/credits) | Balance, transactions and cost lookups | `GET /v1/credits/balance`, `GET /v1/credits/transactions` |

### Reference

| Page | What it covers |
| --- | --- |
| [Errors](https://nodaro.ai/docs/developers/api/errors) | The error envelope, every error code, and job failure hints |
| [Rate limits](https://nodaro.ai/docs/developers/api/rate-limits) | Per-token limits, per-route limits, and how to handle `429` |
| [OpenAPI spec](https://nodaro.ai/docs/developers/api/openapi) | The machine-readable spec at `GET /v1/openapi.json`, and clients in other languages |

## Limits and errors

A personal API token allows 30 requests per minute by default, up to 120, on the workflow-run and workflow-list endpoints, and a few routes have limits of their own. Status polls do not count against the token's limit. A `429` means slow down and retry with backoff; a `4xx` means fix the request before you retry; a `5xx` is usually temporary. Read [Rate limits](https://nodaro.ai/docs/developers/api/rate-limits) and [Errors](https://nodaro.ai/docs/developers/api/errors).

## Clients for your language

- **TypeScript and JavaScript:** `npm install @nodaro/sdk`. The [SDK](https://nodaro.ai/docs/developers/sdk) wraps these endpoints with types, typed errors and polling helpers.
- **The terminal and CI:** `npm install -g @nodaro/cli`. The [CLI](https://nodaro.ai/docs/developers/cli) runs workflows, apps and single nodes, with `--watch` and `--json`.
- **Any other language:** generate a client from the [OpenAPI spec](https://nodaro.ai/docs/developers/api/openapi), or send plain HTTPS requests.
- **AI assistants:** connect the [MCP server](https://nodaro.ai/docs/mcp) instead of writing code.

## Frequently asked questions

### What is the base URL of the Nodaro API?

On Nodaro Cloud it is https://app.nodaro.ai, and every path starts with /v1/, for example https://app.nodaro.ai/v1/nodes. On a self-hosted install, use your install's own address.

### Is the Nodaro API synchronous or asynchronous?

Mostly asynchronous. A generation returns a job id and a workflow run returns an execution id, and you poll them until they finish. For workflows that finish in under a minute, POST /v1/api/run?wait=true can hold the connection instead.

### Which programming languages can I use with the Nodaro API?

Any language that can send HTTPS requests with JSON. For TypeScript and JavaScript there is the @nodaro/sdk package, and the OpenAPI 3.1 spec generates typed clients for Go, Rust, Python and more.

### Does the Nodaro API cost credits?

Generations do. On Nodaro Cloud, each generation spends credits at the model's price, the same as in the editor. A self-hosted Community Edition install has no credit system, and you pay the model providers directly.

### Do I need a subscription to use the API?

No. On Nodaro Cloud, buying any credit pack activates pay-as-you-go, and pay-as-you-go credits work through the API, the SDK, the CLI and MCP. Using the web editor needs a subscription.
