The short version
約10分でREST連携を組み、レンダリングは永続状態を持つ非同期ジョブとして扱います。
公開URLから30秒、16:9のモーショングラフィックスジョブを作ります。upload、create、status、downloadの連携は短時間で書けますが、レンダリングは10分保証ではありません。実測は長くかかったため、ID保存とネットワーク再試行を含めます。
Review TapVid API and MCP access
01
このチュートリアルのtext-to-video API
Text to videoにはprompt clip、image animation、avatarなどがあります。TapVidは既存資料から複数シーンの構造化された情報動画を作ります。公開ページとbriefが事実、対象、長さ、禁止事項を定義します。残りを保ったまま一つのシーンだけ再生成できます。
「10分」は4つのREST操作の実装時間で、render保証ではありません。Createは`202 Accepted`を返し、statusやexportは続きます。IDを保存し、接続を失ってもduplicate createをしないことが重要です。
埋め込み動画は別の検証済みワークフローで完成したTapVidの例です。RESTテストは終端状態を確認する前にpollerの接続が切れたため、その結果としては扱いません。
02
コード前に契約を確認する
Gatewayは`https://api.tapvid.ai/api/public/v1`です。API KeysでBearer keyを作り、serverで`process.env.TAPVID_API_KEY`から読みます。frontendやrepositoryへ入れません。
- HTTPS REST baseを使う。
- keyをserverの`process.env.TAPVID_API_KEY`から読む。
- URLかfileの一方を送る。
- `materialId`と202後の`videoId`を保存する。
- `pollAfterSeconds`と再開可能timeoutを使う。
| 項目 | 制限 | 注意 |
|---|---|---|
| File | 100 MB | multipart |
| URL | HTTPS, 4,096文字 | internal host不可 |
| Materials | 30 | ID保存 |
| Prompt | 12,000文字 | source grounded |
| Ratio | `16:9` or `9:16` | enum exact |
| Duration | `30s` to `5m` | enum exact |
03
手順1: URLまたはファイルをupload
URLはHTTPS JSON、fileは100 MBまでのmultipartです。URLは4,096文字までです。返る`materialId`をprivate stateとして保存します。
export TAPVID_API_KEY="replace-with-your-local-secret"
curl -X POST https://api.tapvid.ai/api/public/v1/materials \
-H "Authorization: Bearer ${TAPVID_API_KEY}" \
-H "Content-Type: application/json" \
-d '{ "url": "https://tapvid.ai/api-mcp" }'cURLはローカル説明用です。CIではsecret storeを使います。`payload_too_large`はmaterialを直し、URL拒否はHTTPS、長さ、到達性を確認します。
04
手順2: 非同期video jobをcreate
Upload成功後だけcreateします。対象、source、30秒、16:9、英語、順序、禁止claimを指定します。HTTP 202と`videoId`は受付であり完成ではありません。
curl -X POST https://api.tapvid.ai/api/public/v1/video/create \
-H "Authorization: Bearer ${TAPVID_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"materialIds": ["MATERIAL_ID_FROM_UPLOAD"],
"userPrompt": "Create a concise 30-second English explainer for developers. Stay faithful to the supplied source and do not invent claims.",
"title": "TapVid API developer explainer",
"aspectRatio": "16:9",
"duration": "30s",
"language": "en"
}'
次の通信より前に`videoId`を永続化します。processが落ちても再開でき、二重createを防ぎます。responseは`videoId`、queryは`video_id`です。
05
手順3: 保存、poll、download
`pollAfterSeconds`に従ってstatusを確認します。progressは残り時間ではありません。completed後にdownloadを要求し、約1時間で期限切れのsigned URLを必要時に更新します。
import { writeFile } from 'node:fs/promises'
const apiKey = process.env.TAPVID_API_KEY
if (!apiKey) throw new Error('TAPVID_API_KEY is required')
const base = 'https://api.tapvid.ai/api/public/v1'
const headers = { Authorization: `Bearer ${apiKey}` }
async function request(path, init = {}, { attempts = 1 } = {}) {
let lastError
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
const response = await fetch(`${base}${path}`, {
...init,
headers: { ...headers, ...init.headers },
})
const data = await response.json()
if (response.ok) return { response, data }
if (response.status !== 429 && response.status < 500) {
const error = new Error(`HTTP ${response.status}: ${data.code ?? 'unknown'}`)
error.retryable = false
throw error
}
lastError = new Error(`retryable HTTP ${response.status}`)
} catch (error) {
if (error?.retryable === false) throw error
lastError = error
}
if (attempt === attempts) break
await new Promise((resolve) => setTimeout(resolve, attempt * 1000))
}
throw lastError
}
const { data: material } = await request('/materials', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://tapvid.ai/api-mcp' }),
})
const { response: createResponse, data: video } = await request('/video/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
materialIds: [material.materialId],
userPrompt: 'Create a concise 30-second English explainer for developers. Stay faithful to the source.',
title: 'TapVid API developer explainer',
aspectRatio: '16:9',
duration: '30s',
language: 'en',
}),
})
if (createResponse.status !== 202) throw new Error('Expected 202 Accepted')
// Persist before the next network call. A lost poll must not cause a duplicate create.
await writeFile('.tapvid-job.json', JSON.stringify({ videoId: video.videoId }))
let status
for (let poll = 0; poll < 180; poll += 1) {
const result = await request(
`/video/status?video_id=${encodeURIComponent(video.videoId)}`,
{},
{ attempts: 4 },
)
status = result.data
if (status.status === 'completed' || status.status === 'failed') break
await new Promise((resolve) =>
setTimeout(resolve, Math.max(status.pollAfterSeconds ?? 5, 5) * 1000),
)
}
if (!status) throw new Error('No status received')
if (status.status === 'failed') throw new Error(status.error?.code ?? 'generation_failed')
if (status.status !== 'completed') throw new Error('Polling timeout; resume with the saved videoId')
const query = new URLSearchParams({
video_id: video.videoId,
resolution: '1080P',
watermark: 'true',
subtitle: 'false',
})
const { data: download } = await request(`/video/download?${query}`, {}, { attempts: 4 })
console.log({ status: download.status, expiresAt: download.expiresAt })Node例はIDを保存し、安全なreadだけ制限付きretryします。本番ではJSON fileをdatabaseにします。400と401は修正し、429、server、networkはbackoffします。
06
実際のRESTテスト結果
テストはupload約1.4秒、create約0.3秒でHTTP 202でした。81秒でrunning 50%、331秒でも同じでした。その後pollerがTLS前の`ECONNRESET`で終了しました。

最初のscriptはIDをmemoryだけに持ち再開できませんでした。duplicateは作りませんでした。MCPとREST合計は180 credits、各create 90でした。10分完成とは主張しません。
07
二重課金なしでエラー処理
unauthorizedはkey、invalid_requestはbodyを直し、insufficient_creditsは停止、rate_limitedはRetry-After、networkはsafe readをretryします。lost response後にcreateし直しません。
| 条件 | Retry | 対応 |
|---|---|---|
| unauthorized | No | key修正 |
| invalid_request | No | body修正 |
| insufficient_credits | No | 停止 |
| rate_limited | Yes | Retry-After |
| network/server | bounded | read retry |
| create後response loss | recreateしない | ID再開 |
08
本番チェックリスト
本番ではserver secret、atomic ID、別々のlatency、再開可能timeout、安全なlog、更新可能URL、人による事実、長さ、権利、brand確認が必要です。
- keyはserverのみ。
- IDをatomic保存。
- latencyを分ける。
- intervalとtimeout。
- read/writeを区別。
- signed URLを更新。
- secretなしでcredits記録。
- 人が確認と公開。
09
ランダムなclipではなく完成explainerに使う
承認済みcontentから一貫したexplainerを作る用途です。対話はClaude MCP tutorial、永続制御はRESTとAPI/MCP overviewを使います。
10
Frequently asked questions
必ず10分で完成しますか?
いいえ。実装は短時間ですがrenderとexportは非同期です。MCP実測はcompletedまで約28分でした。
HTTP 202とは?
Job受付です。videoIdを保存してstatusをpollします。
poll間隔は?
pollAfterSecondsと制限retry、再開可能timeoutを使います。
download URLの期限は?
現在の文書では約1時間です。
watermarkは外せますか?
defaultはonで、除去にはactive subscriptionが必要です。
Turn them into a clear, publishable video
Keep reading
Related stories

ClaudeとTapVid MCPでモーショングラフィックスを作る方法
公開URL、実際の5ツール、30秒の指示、正直な本番テストを使った実践ガイドです。
Aug 7, 2026

Claude動画生成:Seedance 2.5とTapVidの使い分け
Claudeだけでは動画を書き出せません。Seedance 2.5とTapVidの使い分けを、実測結果と再利用できるプロンプト付きで解説します。
Aug 8, 2026

テキストアニメーター実践術:視覚ノイズを抑えて速く動かす
読みやすくインパクトのある動画メッセージを求めるチームのための、実践的なテキストアニメーターのフレームワーク。
Apr 16, 2026

