1つのAPIで複数のAIモデルをn8nに接続するには?
AIモデルをプロバイダーごとに個別接続する方法はプロトタイプでは機能しますが、利用が拡大するにつれて脆弱になります。各プロバイダーは別々の認証情報、エンドポイント、リクエスト形式、レート制限、請求、レスポンス構造を持ち込みます。n8nでは、これが重複するHTTPノードやプロバイダー別の分岐を生みやすく、モデルの追加やフォールバック経路の変更時にワークフローの複数箇所を編集する必要が出てきます。
n8nとCometAPIは、この問題の異なるレイヤーを解決します。n8nは実行タイミング、入力検証、同期/非同期タスクのルーティング、再試行、結果保存を制御します。CometAPIはモデルへのアクセスを1つのAPIキーと1つのベースURLの背後に集約します。両者を組み合わせることで、オーケストレーション層からプロバイダーの変更を切り離せます。キュー、ポーリング、ストレージ、モニタリングのロジックをそのまま保ちつつ、モデルIDの切り替えだけで済みます。
この組み合わせは、スプレッドシートや社内ツールからの画像/動画混在ジョブに特に有効です。ワークフローはn8n上で可視的かつ監査可能なまま、認証情報、モデルの可用性、利用コストの管理は1つのAPIレイヤーで容易になります。
複数のAIプロバイダーを1つのアプリに統合する最も簡単な方法は、オーケストレーションとモデルアクセスを分離することです。トリガー、分岐、再試行、ストレージはn8nに任せ、CometAPIで各分岐に1つのAPIキーと1つのベースURLを付与します。モデルIDは、個別のプロバイダーアカウント、SDK、請求設定ではなく、各ジョブのフィールドになります。
このガイドでは、Google Sheetsから画像・動画ジョブを読み込み、CometAPI経由でOpenAIとByteDanceのモデルに送信し、非同期の動画タスクIDを保存して完了までポーリングし、最終結果をn8n Data Tableにアップサートする、実働するローコードのパイプラインを構築します。
作成するもの
完成するワークフローの流れは次のとおりです。
Google Sheets Trigger → ジョブの正規化 → メディア種別で分岐 → CometAPI 画像または動画リクエスト → 動画タスクを待機・ポーリング → 出力をアップロードまたは参照 → Data Table へアップサート。
ソースシートには次の列を用意します:
job_id | media_type | model | prompt | size | seconds | status
典型的な画像行では image、gpt-image-2、1024x1024 を使用します。動画行では video、seedance-2-5、1280x720、および4〜30秒の継続時間を使用します。
始める前に
n8nインスタンス、Google Sheet、CometAPIのAPIキー、そして ai_jobs という名前のn8n Data Tableが必要です。Data Tableには次の列を作成してください: job_id、media_type、model、status、task_id、result_url、error、updated_at。
セルフホストのn8nでは、n8nプロセスが使用する環境に次の値を追加します:
COMETAPI_BASE_URL=https://api.cometapi.com/v1COMETAPI_KEY=your_cometapi_key
環境を変更したらn8nを再起動してください。n8n Cloud、またはノード式に環境変数を露出させたくない場合は、CometAPI Bearer という名前のHTTP Header Auth認証情報を作成します。ヘッダー名を Authorization、値を Bearer your_cometapi_key に設定します。以下の例ではこの認証情報と、OpenAI互換の固定ベースURL https://api.cometapi.com/v1. を使用します。
現行のモデルIDを使用
| ジョブ | プロバイダーとモデル | リクエスト | 結果 |
|---|---|---|---|
| 画像 | OpenAI · gpt-image-2 | POST /v1/images/generations | 同期のbase64画像 |
| 動画 | ByteDance · seedance-2-5 | POST /v1/videos | 非同期タスク、その後ポーリング |
両方のIDと機能は、2026年8月11日時点のライブCometAPIモデルディレクトリAPIで確認できました。画像モデルはテキストから画像の生成をサポートします。Seedance 2.5はテキストから動画、画像から動画の生成、4〜30秒のクリップ、そしてドキュメントにある480pと720pのサイズをサポートします。
価格(2026年8月11日時点): GPT Image 2のモデルページには、入力トークン100万あたり$4、出力トークン100万あたり$24と記載されています。Seedance 2.5のモデルページには、480pで1秒あたり$0.103、720pで1秒あたり$0.231と記載されています。価格は変動するため、実行時の正確な情報源としてライブのモデルディレクトリまたはモデルページを使用してください。
重要なアーキテクチャ上の違いは、画像生成はリクエスト-レスポンスの操作として扱える一方で、動画生成は状態を持つジョブとして扱うべき点です。ポーリング前に動画のタスクIDを永続化しておくことで、n8nの実行が再起動してもジョブを失わずに済みます。
n8nでワークフローを構築する
1. Google Sheetsから新規ジョブをトリガー
Google Sheets Trigger ノードを追加し、Row added or updated を選択します。ジョブキューを含むワークシートを指定します。トリガーの直後にIFノードを追加し、status が空または queued の場合のみ続行します。これにより、シート変更時に完了済み行が再送信されるのを防ぎます。
2. 各行を正規化して検証
Normalize Job という名前の Code ノードを追加します。このノードは安全なデフォルトを適用し、許可されたモデルIDに制限し、両方の分岐で同じフィールドを出力します。
const row = $json;const allowedModels = { image: new Set(['gpt-image-2']), video: new Set(['seedance-2-5']),};const mediaType = String(row.media_type || '').trim().toLowerCase();if (!allowedModels[mediaType]) { throw new Error(`media_type must be image or video; received: ${row.media_type}`);}const defaultModel = mediaType === 'image' ? 'gpt-image-2' : 'seedance-2-5';const model = String(row.model || defaultModel).trim();if (!allowedModels[mediaType].has(model)) { throw new Error(`Model ${model} is not allowed for ${mediaType} jobs`);}const prompt = String(row.prompt || '').trim();if (!prompt) throw new Error('prompt is required');const seconds = mediaType === 'video' ? Number(row.seconds || 4) : null;if (mediaType === 'video' && (!Number.isInteger(seconds) || seconds < 4 || seconds > 30)) { throw new Error('Seedance 2.5 seconds must be an integer from 4 to 30');}return [{ json: { job_id: String(row.job_id || $execution.id), media_type: mediaType, model, prompt, size: String(row.size || (mediaType === 'image' ? '1024x1024' : '1280x720')), seconds, status: 'processing', updated_at: new Date().toISOString(), },}];
Normalize Job の後に Switch ノードを追加します。image を画像分岐、video を動画分岐にルーティングします。
3. 1つのエンドポイントで画像を生成
Create Image という名前の HTTP Request ノードを追加し、次の設定にします:
- Method:
POST - URL:
https://api.cometapi.com/v1/images/generations - Authentication:
CometAPI BearerHeader Auth認証情報 - Body Content Type: JSON
{ "model": "={{ $('Normalize Job').item.json.model }}", "prompt": "={{ $('Normalize Job').item.json.prompt }}", "size": "={{ $('Normalize Job').item.json.size }}"}
GPT Image 2はbase64の画像データを返します。これをn8nのバイナリアイテムに変換するため、Prepare Image File という名前のCodeノードを追加します:
const job = $('Normalize Job').item.json;const b64 = $json.data?.[0]?.b64_json;if (!b64) throw new Error('CometAPI returned no image data');return [{ json: { ...job, status: 'completed', task_id: '', result_url: '', error: '', updated_at: new Date().toISOString(), }, binary: { media: { data: b64, mimeType: 'image/png', fileName: `${job.job_id}.png`, }, },}];
このノードをS3やGoogle Driveなどの任意のオブジェクトストレージノードに接続します。返却されたファイルURLを result_url に保存し、行を ai_jobs にアップサートします。大きなbase64ペイロードはData Tableに保存しないようにします。
4. 非同期の動画タスクを作成
Create Video という名前の HTTP Request ノードを追加します:
- Method:
POST - URL:
https://api.cometapi.com/v1/videos - Authentication:
CometAPI Bearer - Body Content Type: Form-Data
フォームフィールドを4つ追加します: model、prompt、seconds、size。値は Normalize Job からマッピングします。
続いて、Save Video Task という名前のCodeノードを追加します:
const job = $('Normalize Job').item.json;const taskId = $json.id || $json.task_id;if (!taskId) throw new Error('Video task ID missing from create response');return [{ json: { ...job, task_id: taskId, status: $json.status || 'queued', result_url: '', error: '', updated_at: new Date().toISOString(), },}];
ポーリングの前に、このアイテムを ai_jobs にアップサートします。タスクIDを即座に保存することで、再起動やタイムアウトが起きてもジョブを失いません。
5. 待機・ポーリングして動画URLを保存
Wait ノードを15秒に設定して追加します。続いて Get Video という名前の HTTP Request ノードを追加します:
- Method:
GET - URL:
=https://api.cometapi.com/v1/videos/{{ $json.task_id }} - Authentication:
CometAPI Bearer
リクエスト後、status に基づくSwitchノードを追加します:
queuedまたはin_progress: Waitノードに戻ります。completed:Finalize Videoに進みます。failedまたはerror: エラーをai_jobsに書き込み、停止します。
完了分岐には次のCodeノードを追加します:
const prior = $('Save Video Task').item.json;const resultUrl = $json.video_url || $json.url || $json.data?.video_url;if (!resultUrl) throw new Error('Completed video response has no video URL');return [{ json: { ...prior, status: 'completed', result_url: resultUrl, error: '', updated_at: new Date().toISOString(), },}];
job_id で最終アイテムを ai_jobs にアップサートします。CometAPIの動画URLは署名付きで一時的な場合があるため、本番ワークフローではファイルをダウンロードして再ホストし、永続的なURLを保存してください。アプリが受信リクエストを受けられる場合は、選択したモデルがコールバックをサポートしていればポーリングの代わりにWebhookを使用します。
ノード全体マップ
以下のノードでフルワークフローを組み立てられます:
- Google Sheets Trigger — Row added or updated
- IF — Process only new or queued rows
- Code — Normalize Job
- Switch — Image or video
- 画像分岐: HTTP Request → Prepare Image File → Object Storage → Data Table Upsert
- 動画分岐: HTTP Request → Save Video Task → Data Table Upsert → Wait → HTTP Request → Status Switch
- 完了動画: Finalize Video → Object Storageまたは永続URL → Data Table Upsert
- 失敗動画: Set Error → Data Table Upsert
失敗分岐では、Edit Fieldsノードに次の式を使用します:
{ "job_id": "={{ $('Save Video Task').item.json.job_id }}", "status": "failed", "task_id": "={{ $('Save Video Task').item.json.task_id }}", "result_url": "", "error": "={{ $json.error?.message || $json.message || 'Video generation failed' }}", "updated_at": "={{ $now.toISO() }}"}
ワークフローをテスト
ソースシートに次の2行を追加します:
img-001 | image | gpt-image-2 | A cinematic product photo of a glass robot on a dark desk | 1024x1024 | | queuedvid-001 | video | seedance-2-5 | A paper airplane flies through a sunlit studio, smooth tracking shot | 1280x720 | 4 | queued
画像リクエストは次のような構造を返すはずです:
{ "created": 1786400000, "data": [ { "b64_json": "iVBORw0KGgoAAA..." } ]}
動画作成リクエストは次のようなタスク構造を返すはずです:
{ "id": "video_task_abc123", "object": "video", "status": "queued", "progress": 0}
ポーリング後、完了したレスポンスには同じタスクID、status: completed、および video_url が含まれます。任意フィールドはモデルにより異なり得るため、正規化コードはプロバイダーのレスポンス全体をデータベースにコピーするのではなく、安定したタスクステータスと結果URLを読み取ります。
よくあるエラーと対処
| エラー | 対処 |
|---|---|
| 401 Unauthorized | Header Authの値がBearerで始まり、キーが有効であることを確認します。 |
| 404 model or task not found | ライブモデルディレクトリを確認し、保存済みのタスクIDを GET /v1/videos/{id} に使用しているか確認。 |
| 400 invalid size or seconds | サポートされるサイズを使用し、Seedance 2.5の継続時間を4〜30秒に保ちます。 |
| 429 rate limited | n8nの同時実行数を下げ、指数バックオフとジッターで再試行します。 |
| Polling never ends | 試行回数を永続化し、定義したタイムアウトで停止します。failed と error は終端扱いにします。 |
| Image payload is too large | base64をバイナリに変換してアップロードし、永続URLのみを保存します。 |
本番運用チェックリスト
- 認証情報を保護する。APIキーはn8nの認証情報またはサーバー側の環境変数に保存。スプレッドシートやブラウザーへは渡さない。
- すべてのジョブを冪等に。
job_idをData Tableのアップサートキーに使用。新規タスク作成前に、すでにprocessingまたはcompletedの行はスキップ。 - ポーリングと同時実行を制御。動画ジョブは10〜20秒間隔でポーリング、試行回数に上限を設定し、同時実行を制限。429、500、503にはバックオフで対応し、重複タスク作成を避ける。
- リクエスト前にモデルポリシーを検証。メディア種別ごとの許可リストを維持。モデルの可用性と価格はライブディレクトリから定期更新するが、スプレッドシート利用者が任意IDを送れないよう変更はレビュー経由でデプロイ。
- ジョブごとのコストを追跡。モデル、解像度、継続時間、使用量を結果と一緒に保存。720pの4秒Seedance 2.5は2026年8月11日時点の掲載レートで約$0.924、同じ4秒の480pは約$0.412。送信前に最大継続時間と解像度を強制。
- 生成メディアを再ホスト。プロバイダーの署名付きURLは配信リンクとみなし、永続ストレージとは別にする。完了したメディアをダウンロードして管理バケットへアップロードし、耐久URLとチェックサムを保存。
- 監査証跡を保持。リクエストのモデル、サニタイズしたパラメータ、タスクID、ステータスの遷移、再試行回数、応答時間、最終アセットの場所を保存。APIキーや完全なプライベートプロンプトは記録しない。
このパターンがスケールする理由
各新規プロバイダーやモデルの追加は、アカウント統合ではなくルーティングの判断に留まるため、ワークフローはシンプルに保たれます。スプレッドシートはジョブキューのまま、n8nはオーケストレーション層のまま、CometAPIは単一のアクセスレイヤーのままです。許可リストと分岐設定を拡張するだけでモデルを追加でき、トリガー、タスク永続化、ポーリング、ストレージ、モニタリングのロジックは不変です。
これが実践的なマルチプロバイダーAI統合の答えです。単一の制御されたエンドポイントとキー、明示的なモデルルーティング、同期/非同期パスの分離、各ジョブの耐久レコードです。
FAQs
n8nは1つのAPI経由で複数のAIプロバイダーを呼び出せますか?
はい。CometAPIのような統一APIレイヤーを用いれば、n8nはプロバイダーの認証情報とHTTP統合を一元化したまま、対応する異なるモデルにリクエストを送れます。
n8nのHTTP RequestノードでCometAPIを使えますか?
はい。HTTP RequestノードでCometAPIのAPIエンドポイントに対し、必要な認証とモデル固有のパラメータを付けてリクエストを送信できます。
モデルが失敗したときに、n8nは自動で別のAIモデルに切り替えられますか?
はい。APIリクエスト後にIF/Switch分岐を使い、再試行可能なエラーやモデル固有の失敗をフォールバックモデルへルーティングできます。フォールバックは同じモダリティと必要な機能をサポートしている必要があります。
