# Trends

> Nodaro のトレンドのカタログを閲覧し、トレンドのスロットを自分の写真で埋めて、REST、SDK、CLI、MCP で無料の見積もりを出し、実行を開始します。

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

**Trends API** は、既製の [Cast Swap](https://nodaro.ai/docs/developers/api/cast-swap) のレシピを実行します。トレンドは、スロットを持つ短いクリップです。スロットは、置き換えられる人物、ペット、商品、場所です。スロットを自分の写真で埋めてトレンドを実行すると、それらが入ったクリップが返されます。

トレンドは Nodaro Cloud の機能です。セルフホスティング環境では、どのルートも `404` を返します。これらのルートは Bearer トークンを受け取ります。[認証](https://nodaro.ai/docs/developers/api/authentication)を参照してください。

ここにあるのは、**公開済み**のトレンドだけです。ドラフト、非公開、または不明なスラッグは、どのルートでも `404` になります。

## エンドポイント
| メソッド | パス | 内容 |
| --- | --- | --- |
| `GET` | `/v1/trends` | カタログを、その並び順で返します：`{ data: TrendCard[], next }`。クエリ：`q`（検索）、`tag`、`favorites=1`（自分のお気に入りのみ）、`locale`、`cursor`、`limit`（1〜60、デフォルトは 30）。 |
| `GET` | `/v1/trends/:slug` | 1 つのトレンドを返します：`{ data: TrendDetail }`。スロット、例、プレビューの長さ、解像度が含まれます。クエリ：`locale`。 |
| `POST` | `/v1/trends/:slug/estimate` | 実行の見積もりを出します。無料です。`{ data: { expectedCredits, holdCredits, breakdown } }` を返します。 |
| `POST` | `/v1/trends/:slug/runs` | 実行を開始します。`202 { data: { jobId } }` を返します。`GET /v1/jobs/:id` でジョブを追跡します。 |
| `GET` | `/v1/trends/runs` | 自分のトレンドの実行を、新しい順に返します：`{ data: TrendRun[], next }`。クエリ：`trend`（スラッグ）、`cursor`、`limit`（1〜60、デフォルトは 30）。 |
| `PUT` | `/v1/trends/:slug/favorite` | トレンドをお気に入りに追加します。`204` を返します。 |
| `DELETE` | `/v1/trends/:slug/favorite` | トレンドをお気に入りから外します。`204` を返します。 |

名前、説明、スロットのラベル、ヒントは、`?locale=` がプラットフォームの言語の場合はその言語で返されます。それ以外の場合は、プロフィールの言語、次に英語で返されます。

トレンドが新しいバージョンに移ったり、非公開になったりした後も、実行は `GET /v1/trends/runs` で読み取れます。

## トレンドの内容
`GET /v1/trends/:slug` は次のものを返します。

- `slots`：各スロットの `key`、`label`、`kind`（`person`、`pet`、`product`、`location` のいずれか）、`required`、任意の `hint`、デフォルトのチップです。
- `examples`：各例の結果の動画と、各スロットで使われた写真です。
- `previewSec`：トレンドが 4 秒のプレビュー実行に対応している場合は `4`、それ以外は `null` です。
- `resolutions`：トレンドを実行できる解像度です。

`GET /v1/trends` のカタログのカードには、スラッグ、名前、タグ、`needs`（種類ごとのスロットの数）、秒単位の長さ、`isFavorite` が含まれます。

## 実行のボディ
`POST /v1/trends/:slug/estimate` と `POST /v1/trends/:slug/runs` は、同じボディを受け取ります：`{ slots, example?, details?, preview?, resolution?, draft?, sessionId?, projectId? }`。

| フィールド | 内容 |
| --- | --- |
| `slots` | 必須。スロットのキー → `{ images?, …chips }` で、スロットごとに 1 つの対象の画像を 1〜3 枚指定します。必須のスロットには、すべて画像が必要です。 |
| `example` | 任意。例の番号で、1 から数えます。省略した任意のスロットは、その例の写真で埋められます。デフォルトは 1 です。 |
| `details` | 任意。あなた自身の言葉で、最大 1,000 文字です。スロットは `{slot:<key>}` で指定します。 |
| `preview` | 任意。トレンドの `previewSec` が 4 の場合、`true` にすると 4 秒を実行します。 |
| `resolution` | 任意。`"480p"`（デフォルト）、`"720p"` または `"1080p"` です。 |
| `draft` | 任意。デフォルトは `true` です。ドラフトは 480p でのみ使えます。 |
| `sessionId` | 任意。受け付けられ、確認もされますが、まだ適用されません。トレンドの実行は、まだセッションに保存されません。 |
| `projectId` | 任意。受け付けられますが、現在の実行では使われません。 |

チップは、スロットの種類ごとの特徴です。各チップは、自分の写真から作る `"new"` か、クリップのまま残す `"original"` です。

| スロットの種類 | チップ |
| --- | --- |
| `person` | `body`、`outfit`、`hair` |
| `pet` | `body`、`coat` |
| `product` | `shape`、`finish` |
| `location` | なし |

```json
{
"slots": {
"dancer": { "images": ["https://example.com/me.jpg"], "outfit": "original" }
},
"preview": true,
"resolution": "480p"
}
```

## 見積もり
見積もりは、実行に課金される見込みの `expectedCredits` と、実行の開始時に確保される `holdCredits` を返します。`breakdown` には、見積もりの内訳が示されます。

## 繰り返し
`POST /v1/trends/:slug/runs` は、`Idempotency-Key` ヘッダー（8〜200 文字）を受け取ります。同じキーでの繰り返しには、最初の実行のジョブが返されます。新しく開始や課金されるものはありません。

## 権限
OAuth アプリには、次のものが必要です。

- カタログ、トレンド、自分の実行を読み取るには `jobs:read`。
- 実行の見積もりや開始には `workflows:execute`。`sessionId` を渡すには、さらに `assets:write`。
- お気に入りには `assets:write`。

## エラー
拒否のボディは `{ error: { code, message } }` です。スロットのエラーには、スロットのキーも `slot` として含まれます。見積もりと実行のルートの `400` の拒否は、何かが課金される前に行われます。

| ステータス | `code` | 条件 |
| --- | --- | --- |
| `400` | `validation_error` | ボディかクエリが有効ではない、`cursor` がこの一覧が返したものではない、または `Idempotency-Key` が 8〜200 文字ではありません。 |
| `400` | `missing_slot` | 必須のスロットに画像がないか、任意のスロットが空のままで、選んだ例にそのスロットの写真がありません。 |
| `400` | `unknown_slot` | `slots` が、トレンドにないキーを指定しています。 |
| `400` | `invalid_choice` | スロットの種類が受け付けないチップです。たとえば、人物の `coat` や、場所のチップすべてです。 |
| `400` | `example_out_of_range` | `example` がトレンドにない例を指定しているか、任意のスロットが空のままで、それを埋める例がトレンドにありません。 |
| `400` | `preview_unavailable` | プレビューに対応していないトレンドで `preview: true` を指定しています。 |
| `400` | `invalid_slot_mention` | `details` が、トレンドにないキーで `{slot:<key>}` を指定しています。 |
| `400` | `prompt_too_long` | `details` がこのトレンドには長すぎます。短くしてください。 |
| `401` | `unauthorized` | 認証されたユーザーがいません。 |
| `404` | `not_found` | トレンドが公開されていないか、`sessionId` が、あなたが使えないセッションを指しています。 |
| `502` | `engine_error` | 動画エンジンが予期しない応答を返しました。 |
| `503` | `engine_unsupported` | このプラットフォームのバージョンでは、まだトレンドを実行できません。 |
| `503` | `trend_unavailable` | 現在、このトレンドを実行できません。 |
| `503` | `not_available` | このサーバーには、まだトレンドのカタログがありません。`GET /v1/trends/runs` 以外のすべてのルートです。 |

動画エンジンが見積もりや実行を拒否した場合、トレンドのルートは独自のコードと固定のメッセージを返します。

| ステータス | `code` | 条件 |
| --- | --- | --- |
| `400` | `trend_run_rejected` | 下に挙げていない理由で、エンジンがこれらの入力を拒否しました。 |
| `400` | `clip_out_of_range`、`preview_out_of_range`、`video_unreadable` | トレンド自体のクリップを、そのままでは実行できません。 |
| `400` | `unsafe_url` | 写真が、公開された http または https のアドレスではありません。 |
| `400` | `raw_reference_token` | `details` に `{image:`、`{video:` または `{audio:` が含まれています。 |
| `402` | `insufficient_credits` | クレジットが実行の料金に足りません。ワークスペースでは、代わりに拒否した上限が返ることがあります：`budget_exceeded`、`member_cap_exceeded` または `user_allowance_exceeded`。 |
| `413` | `storage_limit_exceeded` | ストレージが満杯です。 |
| `422` | `job_blocked` | デプロイ環境のジョブポリシーが実行をブロックしました。 |
| `429` | `rate_limited` | リクエストが多すぎます。少し後でもう一度試してください。 |
| `503` | `engine_unsupported`、`estimate_unavailable` | 現在、実行も見積もりもできません。少し後でもう一度試してください。 |
| `502` | `engine_error` | それ以外のすべてです。 |

## 完了しなかった実行
`GET /v1/trends/runs` の実行のうち、完了しなかったものには `error: { code, message }` が含まれます。メッセージは常に同じ固定の文なので、コードで処理を分岐してください。

| `code` | 意味 |
| --- | --- |
| `safety-block`、`policy-block` | ジョブがブロックされました。 |
| `credits` | 実行が途中でクレジット不足になりました。クレジットは返還されています。クレジットを追加してから、もう一度実行してください。 |
| `cancelled` | 実行はキャンセルされました。 |
| `failed` | それ以外の失敗です。 |

失敗もキャンセルもされなかった実行は `error: null` です。完了した実行には、`videoUrl`、`posterUrl`、`creditsCharged` が含まれます。実行の各スロットには、その写真が例から取られたかどうか（`fromExample`）が示されます。

## SDK から
`client.trends` はこれらのルートをラップします。`@nodaro/sdk/trends` としてインポートすることもできます。

```ts
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()` と `runs()` は、届いたままのページ `{ data, next }` に解決されます。`next` を `cursor` として渡し直してください。最後のページでは `null` です。`get()`、`estimate()`、`run()` は、`{ data }` のエンベロープを除いたペイロードに解決されます。`"runs"` がスラッグになることはありません。スラッグを受け取るメソッドは、それに対してリクエストの前に `TypeError` をスローします。

```ts

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)
```

## CLI から
```bash
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` は繰り返して指定し、スロットごとに 1〜3 枚の写真を渡します。`trends run` はクレジットを使うので、先に `trends estimate` を実行してください。[CLI コマンド](https://nodaro.ai/docs/developers/cli/commands)を参照してください。

## MCP から
トレンドを扱う MCP ツールは、`list_trends`、`get_trend`、`estimate_trend`、`run_trend` の 4 つです。[MCP › 動画](https://nodaro.ai/docs/mcp/tools/video#trends)を参照してください。`confirm` を指定しない `run_trend` は、見積もりを返し、何も課金しません。ユーザーが料金を承認した後に `confirm: true` で呼び出すと、実行を開始してジョブ ID を返します。

## よくある質問

### Nodaro のトレンドとは何ですか？

トレンドは、既製の Cast Swap のレシピです。置き換えられる人物、ペット、商品、場所のスロットを持つ短いクリップです。スロットを自分の写真で埋めると、それらが入ったクリップが返されます。

### トレンドの実行にかかる料金は、どうすればわかりますか？

実行するのと同じボディで POST /v1/trends/:slug/estimate を呼び出します。この呼び出しは無料です。実行に課金される見込みの expectedCredits と、実行中に確保される holdCredits が返されます。

### すべてのスロットを埋める必要がありますか？

いいえ。必須のスロットには、それぞれ 1〜3 枚の写真が必要です。省略した任意のスロットには、トレンドの例の写真が使われます。デフォルトは最初の例です。

### トレンドの実行は、安全に再試行できますか？

はい。同じ Idempotency-Key ヘッダー（8〜200 文字）を送ります。同じキーでの繰り返しには最初の実行のジョブが返され、新しく開始や課金されるものはありません。

### トレンドは、セルフホスティング環境で使えますか？

いいえ。トレンドは Nodaro Cloud の機能です。セルフホスティング環境では、トレンドのどのルートも 404 を返します。
