Nodaro Docs
DocumentationNode ReferenceModelsTemplatesAI Agents (MCP)DevelopersSelf-hostingResearchCompare
REST API

Trends

Browse the Nodaro trend catalog, fill a trend's slots with your own pictures, quote the run for free and start it over REST, the SDK, the CLI or MCP.

Available on Nodaro Cloud

The Trends API runs ready-made Cast Swap recipes. A trend is a short clip with slots: the people, pets, products and place you can replace. Fill the slots with your own pictures and run the trend to get the clip back with them in it.

Trends are part of Nodaro Cloud. On a self-hosted install, every route answers 404. The routes take a bearer token. See Authentication.

Only published trends exist here. A draft, an unpublished or an unknown slug is a 404 on every route.

Endpoints

MethodPathWhat it does
GET/v1/trendsThe catalog, in its own order: { data: TrendCard[], next }. Query: q (search), tag, favorites=1 (only yours), locale, cursor, limit (1 to 60, default 30).
GET/v1/trends/:slugOne trend: { data: TrendDetail }, with its slots, examples, preview length and resolutions. Query: locale.
POST/v1/trends/:slug/estimateQuote a run. Free. Answers { data: { expectedCredits, holdCredits, breakdown } }.
POST/v1/trends/:slug/runsStart a run. Answers 202 { data: { jobId } }. Follow the job with GET /v1/jobs/:id.
GET/v1/trends/runsYour trend runs, newest first: { data: TrendRun[], next }. Query: trend (a slug), cursor, limit (1 to 60, default 30).
PUT/v1/trends/:slug/favoriteAdd the trend to your favorites. Answers 204.
DELETE/v1/trends/:slug/favoriteRemove the trend from your favorites. Answers 204.

Names, descriptions, slot labels and hints come in the language of ?locale= when it is a platform language. Otherwise they come in your profile's language, and then in English.

A run stays readable in GET /v1/trends/runs after its trend moves to a new version or is unpublished.

What a trend holds

GET /v1/trends/:slug returns:

  • slots: each slot's key, label, kind (person, pet, product or location), required, an optional hint and its default chips.
  • examples: each example's result video and the pictures every slot used.
  • previewSec: 4 when the trend offers a 4-second preview run, else null.
  • resolutions: the resolutions the trend can run at.

A catalog card in GET /v1/trends carries the slug, name, tags, needs (how many slots of each kind), the length in seconds and isFavorite.

The run body

POST /v1/trends/:slug/estimate and POST /v1/trends/:slug/runs take the same body: { slots, example?, details?, preview?, resolution?, draft?, sessionId?, projectId? }.

FieldWhat it does
slotsRequired. Slot key → { images?, …chips }, with 1 to 3 images of one subject per slot. Every required slot needs images.
exampleOptional. An example number, from 1. Its pictures fill the optional slots you leave out. The default is 1.
detailsOptional. Your own words, up to 1,000 characters. Mention a slot as {slot:<key>}.
previewOptional. true runs 4 seconds, when the trend's previewSec is 4.
resolutionOptional. "480p", the default, "720p" or "1080p".
draftOptional. The default is true. A draft is available at 480p only.
sessionIdOptional. Accepted and checked, but not applied yet: trend runs are not filed into sessions yet.
projectIdOptional. Accepted, but not used by the run today.

The chips are the slot kind's own traits. Each chip is "new", made from your pictures, or "original", kept as in the clip:

Slot kindChips
personbody, outfit, hair
petbody, coat
productshape, finish
locationNone
{
  "slots": {
    "dancer": { "images": ["https://example.com/me.jpg"], "outfit": "original" }
  },
  "preview": true,
  "resolution": "480p"
}

The quote

The estimate answers expectedCredits, what the run is expected to be charged, and holdCredits, what is reserved when the run starts. breakdown lists the parts of the quote.

Repeats

POST /v1/trends/:slug/runs takes an Idempotency-Key header, 8 to 200 characters. A repeat with the same key answers with the first run's job. Nothing new is started or charged.

Permissions

An OAuth app needs:

  • jobs:read to read the catalog, a trend and your runs;
  • workflows:execute to estimate or start a run, plus assets:write to pass a sessionId;
  • assets:write for favorites.

Errors

The body of a refusal is { error: { code, message } }. The slot errors also carry the slot's key as slot. The 400 refusals of the estimate and run routes come before anything is charged.

StatuscodeWhen
400validation_errorThe body or a query is not valid, the cursor is not one this list gave, or the Idempotency-Key is not 8 to 200 characters.
400missing_slotA required slot has no images, or an optional slot is left empty and the chosen example has no pictures for it.
400unknown_slotslots names a key the trend does not have.
400invalid_choiceA chip that the slot's kind does not take, for example a coat on a person or any chip on a location.
400example_out_of_rangeexample names an example the trend does not have, or an optional slot is left empty and the trend has no example to fill it.
400preview_unavailablepreview: true on a trend that offers no preview.
400invalid_slot_mentiondetails mentions {slot:<key>} with a key the trend does not have.
400prompt_too_longdetails is too long for this trend. Shorten it.
401unauthorizedNo authenticated user.
404not_foundThe trend is not published, or sessionId names a session you cannot use.
502engine_errorThe video engine answered in an unexpected way.
503engine_unsupportedThis platform version cannot run trends yet.
503trend_unavailableThe trend cannot be run right now.
503not_availableThis server has no trend catalog yet. Every route except GET /v1/trends/runs.

When the video engine refuses an estimate or a run, the trend routes answer with their own codes and fixed messages:

StatuscodeWhen
400trend_run_rejectedThe engine refused these inputs, for a reason not listed below.
400clip_out_of_range, preview_out_of_range, video_unreadableThe trend's own clip cannot be run as it is.
400unsafe_urlA picture is not a public http or https address.
400raw_reference_tokendetails contains {image:, {video: or {audio:.
402insufficient_creditsYour credits cannot cover the run. In a workspace, the limit that refused it can answer instead: budget_exceeded, member_cap_exceeded or user_allowance_exceeded.
413storage_limit_exceededYour storage is full.
422job_blockedThe deployment's job policy blocked the run.
429rate_limitedToo many requests. Try again shortly.
503engine_unsupported, estimate_unavailableThe run cannot be run or quoted right now. Try again shortly.
502engine_errorAnything else.

Runs that did not finish

A run in GET /v1/trends/runs that did not finish has error: { code, message }. The message is always the same fixed sentence, so branch on the code:

codeMeaning
safety-block, policy-blockThe job was blocked.
creditsThe run ran out of credits partway. It was refunded. Top up, then run it again.
cancelledThe run was cancelled.
failedAny other failure.

A run that did not fail and was not cancelled has error: null. A completed run carries videoUrl, posterUrl and creditsCharged. Each slot of the run says whether its pictures came from the example (fromExample).

From the SDK

client.trends wraps the routes. It is also importable as @nodaro/sdk/trends.

list(params?: ListTrendsParams): Promise<TrendPage<TrendCard>>
get(slug: string, params?: { locale?: string }): Promise<TrendDetail>
estimate(slug: string, request: TrendRunRequest): Promise<TrendEstimate>
run(slug: string, request: TrendRunRequest, options?: { idempotencyKey?: string }): Promise<{ jobId: string }>
runs(params?: ListTrendRunsParams): Promise<TrendPage<TrendRun>>
favorite(slug: string): Promise<void>
unfavorite(slug: string): Promise<void>

list() and runs() resolve to the page as it arrives, { data, next }. Pass next back as cursor; it is null on the last page. get(), estimate() and run() resolve to the payload, without the { data } envelope. "runs" is never a slug: the methods that take a slug throw a TypeError for it before any request.

import type { TrendRunRequest } from "@nodaro/sdk"

const { data: cards } = await client.trends.list({ q: "dance" })
const trend = await client.trends.get(cards[0].slug)

const request: TrendRunRequest = {
  // Every required slot needs 1–3 pictures; optional slots left out take the example's.
  slots: Object.fromEntries(
    trend.slots.filter((s) => s.required).map((s) => [s.key, { images: [myPhotoUrl] }]),
  ),
}
const quote = await client.trends.estimate(trend.slug, request)
console.log(`about ${quote.expectedCredits} credits, ${quote.holdCredits} held while it runs`)

const { jobId } = await client.trends.run(trend.slug, request, { idempotencyKey: crypto.randomUUID() })
const job = await client.jobs.get(jobId)

From the CLI

nodaro trends list --search dance
nodaro trends get <slug>
nodaro trends estimate <slug> --slot dancer=https://example.com/me.jpg --choice dancer.outfit=original
nodaro trends run <slug> --slot dancer=https://example.com/me.jpg --idempotency-key <key>
nodaro trends runs --trend <slug>
nodaro trends favorite <slug>

--slot repeats, with 1 to 3 pictures per slot. trends run spends credits, so run trends estimate first. See CLI commands.

From MCP

Four MCP tools cover trends: list_trends, get_trend, estimate_trend and run_trend. See MCP › Video. run_trend without confirm returns the quote and charges nothing. With confirm: true, after the user accepts the price, it starts the run and returns a job id.

Frequently asked questions

What is a trend in Nodaro?

A trend is a ready-made Cast Swap recipe. It is a short clip with slots for the people, pets, products and place you can replace. You fill the slots with your own pictures and get the clip back with them in it.

How do I know what a trend run costs?

Call POST /v1/trends/:slug/estimate with the same body you will run. It is free. It returns expectedCredits, what the run is expected to be charged, and holdCredits, what is reserved while it runs.

Do I have to fill every slot?

No. Every required slot needs 1 to 3 pictures. An optional slot that you leave out takes the pictures of the trend's example, the first one by default.

Can I retry a trend run safely?

Yes. Send the same Idempotency-Key header, 8 to 200 characters. A repeat with the same key answers with the first run's job, and nothing new is started or charged.

Do trends work on a self-hosted install?

No. Trends are part of Nodaro Cloud. On a self-hosted install, every trend route answers 404.

Last updated on

On this page