88API88API
使用指南AI 應用API 文件幫助支援
影片(Video)

影片 API 完整調用規範

88API 統一影片生成介面。

本頁是 88API 對客戶開放的統一影片生成規範,適用於 88API 提供的各類影片模型。客戶只需從自己的 API Key 可見的模型清單取得 model ID,再使用本頁的統一 API 即可。

目前所有影片任務共用「查詢模型 → 提交 → 輪詢 → 下載」流程。不同帳號看到的模型 ID 可能不同,各模型支援的時長、比例、解析度與參考素材數量也可能不同;請勿把某個模型的能力直接套用到其他模型。

更新於 2026-10-10。各品牌模型的專屬參數與範例已拆分為獨立頁面,請見下方各品牌呼叫規範。建議流程:先呼叫 GET /v1/models 取得目前 Key 可用的模型 ID;已有可用的 HTTPS 素材可直接引用;本機素材先上傳 → 提交生成 → 輪詢取得結果 URL → 播放、下載或作為下一輪的參考素材。上傳成功不代表模型一定支援該素材類型;模型能力與儲存能力是兩回事。

統一串接規則:先以 GET /v1/models 回傳的精確 id 填入 model,再用 POST /v1/videos 建立任務,保存回應中的 id,並透過 GET /v1/videos/{id} 輪詢。新串接請勿使用舊的 /v1/video/generations,不要依賴 task_id,也不要把 callback_url 當成所有模型都支援的功能;需要穩定相容時,一律採用輪詢。

1. 統一串接的最低規則

如果同一套用戶端需要串接多個影片模型,只要固定下列流程:

  1. 以目前的 API Key 請求 GET /v1/models。
  2. 從回傳結果中選擇客戶實際具備權限的模型 id;不要把模型廣場名稱、舊名稱或其他帳號看到的名稱寫死成全域設定。
  3. 以同一套 POST /v1/videos 請求格式提交任務。
  4. 只用回應中的公開 id 查詢任務;不要解析或拼接任何其他任務 ID。
  5. 透過完成回應中的 url、video_url 或 result_url 取得影片。

建議使用的通用欄位如下:

用途建議欄位說明
模型model必須是目前 Key 從 /v1/models 取得的精確模型 ID
文字prompt純文字生成影片通常為必填;能否留空取決於模型
時長duration 或 seconds二擇一,單位為秒;優先使用 duration
比例ratio 或 aspect_ratio建議用於設定畫面比例;實際可用值以目標模型的能力為準
參考圖片images建議使用公開的 HTTPS 網址或已上傳的 assets.88api.ai URL
參考影片metadata.referenceVideos僅在目標模型支援時使用
參考音訊metadata.referenceAudios僅在目標模型支援時使用
首尾影格metadata.firstFrame、metadata.lastFrame必須同時遵守目標模型的首尾影格與素材互斥規則

上表是多數模型通用的寫法。部分品牌的欄位不同(例如萬相的首影格、尾影格和參考素材建議使用 media 陣列,無法識別的素材欄位會回傳 400),請以各品牌呼叫規範為準。

size 是部分模型與舊版用戶端使用的相容欄位。新用戶端應優先使用 ratio 或 aspect_ratio,並依目標模型的能力說明選擇實際的值。

本頁後續範例中的 <MODEL_ID>,都代表依上述步驟從 /v1/models 取得的真實模型 ID,並不是要求所有客戶使用同一個固定字串。

2. API 速查

用途方法與路徑說明
取得模型GET https://api.88api.ai/v1/models回傳目前 API Key 可見的模型 ID;以回傳結果為準
提交生成POST https://api.88api.ai/v1/videos提交文字與參考素材,回傳任務 id
查詢任務GET https://api.88api.ai/v1/videos/{id}查詢狀態、進度與錯誤
申請素材上傳POST https://api.88api.ai/v1/media/uploads傳送檔案摘要、位元組數與 MIME,取得短效上傳憑證
上傳素材二進位檔PUT {upload_url}使用回傳的專用上傳標頭,不要傳送 API Key
取得影片GET {url}優先使用完成回應中的結果 URL,不要自行拼接路徑
舊內容入口GET https://api.88api.ai/v1/videos/{id}/content已封存影片的相容重新導向;不是所有結果通用的下載方式
終態回呼提交時設定 callback_url僅限支援回呼的型號;其餘請使用輪詢

請求標頭

Authorization: Bearer sk-你的API金鑰
Content-Type: application/json
Accept: application/json

API 金鑰必須保存在伺服器端,不得寫進瀏覽器、小程式或 App 的前端套件。

操作驗證方式
建立、查詢任務;申請上傳憑證Authorization: Bearer ...,只傳給 api.88api.ai API
上傳檔案原樣使用 headers 中的 X-Media-Upload-Token 與 Content-Type
讀取公開媒體assets.88api.ai 網址不需要 Bearer、Cookie 或登入

可匿名讀取不等於可匿名上傳,也不會公開任務詳情。取得公開媒體 URL 的人可以在有效期限內讀取檔案;請勿把 URL 當成嚴格的隱私隔離機制。不要把自己的 API Key 轉送給媒體網址、官方結果網址或重新導向的目標。

先取得目前帳號可用的模型 ID

模型 ID 並非所有帳號都相同。用戶端首次串接或定期更新模型清單時,請先執行:

curl 'https://api.88api.ai/v1/models' \
  --header 'Authorization: Bearer sk-xxxx'

從回應中的 data[].id 選擇模型,並把該值原樣填入後續 POST /v1/videos 的 model 欄位。不要依模型顯示名稱自行拼出 ID,也不要以為其他帳號或舊文件中的模型名稱在目前帳號一定可用。

{
  "object": "list",
  "data": [
    {
      "id": "<MODEL_ID>",
      "object": "model",
      "owned_by": "88api"
    }
  ]
}

3. 標準提交請求

最簡文字生成影片請求

curl --request POST 'https://api.88api.ai/v1/videos' \
  --header 'Authorization: Bearer sk-xxxx' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "model": "<MODEL_ID>",
    "prompt": "雨夜城市中的銀色跑車,低角度跟拍,霓虹倒影,電影感",
    "duration": 8,
    "ratio": "16:9"
  }'

duration 也可以改用字串形式的 seconds:

{
  "model": "<MODEL_ID>",
  "prompt": "日出時的海岸空拍,雲層緩緩移動",
  "seconds": "8",
  "ratio": "16:9"
}

通用請求欄位

欄位型別必填說明
modelstring是目前 API Key 透過 GET /v1/models 取得的精確模型 ID
promptstring視情況文字提示詞;僅部分模型在提供參考素材時允許留空
negative_promptstring否多參考模型的負面提示詞;Veo 也可寫成 metadata.negativePrompt
durationinteger否影片時長,單位為秒;與 seconds 二擇一
secondsstring否字串形式的影片時長,例如 "8"
ratiostring否建議使用的畫面比例欄位,例如 16:9、9:16
aspect_ratiostring否ratio 的相容別名;實際可用值以目標模型的能力為準
sizestring否相容欄位;新串接不要依賴它傳遞比例
resolutionstring否僅在目標模型支援時使用,例如 480p、720p、1080p、4k
imagestring否單張參考圖片 URL
imagesstring[]否參考圖片 URL 陣列,建議用法
videostring否單段參考影片 URL;Gemini Omni 必須使用這個頂層欄位
videosstring[]否參考影片陣列;其他多參考模型使用 metadata.referenceVideos
seedinteger否隨機種子
generateAudio / generate_audioboolean否在支援的模型上同步生成音訊;僅在模型能力允許時使用
camera_controlobject否鏡頭控制;僅在對應模型明確支援時使用
callback_urlstring否僅在目標模型明確支援回呼時使用;新串接預設不要傳送
metadataobject否參考影片、音訊、首尾影格等擴充欄位

如何指定解析度

建立任務的 API 都相同,但不同模型選擇解析度的方式可能不同。請先以 GET /v1/models 回傳的模型 ID 與模型能力說明為準。

固定解析度模型:以模型 ID 選擇解析度

部分模型已在模型名稱或模型 ID 中固定輸出規格。客戶應先從 GET /v1/models 取得目前帳號實際可用的 ID,再直接選擇對應規格的模型:

{
  "model": "<FIXED_RESOLUTION_MODEL_ID>",
  "prompt": "城市夜景中,汽車緩緩駛過街道",
  "duration": 8,
  "ratio": "16:9"
}

對固定解析度模型而言,resolution 無法臨時改變輸出規格。即使用戶端帶上 resolution,也不要把它當成覆寫模型能力的方法。

可選解析度模型:使用 resolution 欄位

部分模型可能支援多種解析度,此時請在請求中傳入小寫的 resolution:

{
  "model": "<MULTI_RESOLUTION_MODEL_ID>",
  "prompt": "城市夜景中,汽車緩緩駛過街道",
  "duration": 8,
  "resolution": "720p",
  "ratio": "16:9"
}

實際可用的解析度與取值,仍以目前 API Key 的 /v1/models、模型說明及伺服器端驗證為準。若用戶端需要相容多個模型,可以保留 resolution 欄位:支援此欄位的模型會用它選擇輸出規格,固定規格的模型則以 model 為準。請勿使用 quality、vquality 或自行修改模型名稱來繞過模型能力限制;不支援的組合會回傳 400。

4. 參考圖片、影片與音訊

3.1 參考圖片

多張圖片一律使用頂層 images 陣列:

{
  "model": "<MODEL_ID>",
  "prompt": "保持人物的臉部、服裝與配色一致,在海邊緩步行走,電影級柔光",
  "duration": 10,
  "ratio": "16:9",
  "images": [
    "https://cdn.example.com/character-front.jpg",
    "https://cdn.example.com/character-side.jpg"
  ]
}

單張圖片也可以使用 image 或 input_reference,但新串接建議一律使用 images 陣列。

3.2 參考影片

能力表中支援多段參考影片的模型,請放在 metadata.referenceVideos:

{
  "model": "<MODEL_ID>",
  "prompt": "參考鏡頭節奏與運鏡,但保持主體外觀不變",
  "duration": 8,
  "ratio": "16:9",
  "images": ["https://cdn.example.com/subject.jpg"],
  "metadata": {
    "referenceVideos": [
      "https://cdn.example.com/camera-motion.mp4"
    ]
  }
}

3.3 參考音訊

能力表中支援參考音訊的模型,請放在 metadata.referenceAudios:

{
  "model": "<MODEL_ID>",
  "prompt": "人物依照參考音訊的節奏演唱,口型自然",
  "duration": 10,
  "ratio": "16:9",
  "images": ["https://cdn.example.com/singer.jpg"],
  "metadata": {
    "referenceAudios": [
      "https://cdn.example.com/voice-reference.mp3"
    ]
  }
}

MiniMax H3 的參考圖片不要用頂層 images,請依 MiniMax 影片 API 規範使用 metadata.referenceImages。

3.4 多參考模型的混合素材

{
  "model": "<MODEL_ID>",
  "prompt": "使用參考圖的產品外觀、參考影片的鏡頭節奏與參考音訊的聲音氛圍,製作科技廣告",
  "duration": 12,
  "ratio": "16:9",
  "images": [
    "https://cdn.example.com/product-front.jpg",
    "https://cdn.example.com/product-detail.jpg"
  ],
  "metadata": {
    "referenceVideos": [
      "https://cdn.example.com/motion-reference.mp4"
    ],
    "referenceAudios": [
      "https://cdn.example.com/sound-reference.mp3"
    ]
  }
}

同一類素材請勿同時使用多個別名欄位。例如不要同時提交頂層 images 與 metadata.referenceImages;多個相容欄位同時存在時,閘道只會採用其中一組,不保證合併。

3.5 素材 URL 要求

  1. 參考影片與音訊請使用可匿名下載的公開 HTTPS URL。blob:、本機路徑、data:video/... 都不是通用的參考影片協定;本機檔案請依下一節先上傳。
  2. URL 不能依賴 Cookie、瀏覽器登入狀態、Referer 或自訂請求標頭。
  3. 不能使用 localhost、私有 IP、Docker 服務名稱或只有你的內部網路才能存取的位址。
  4. URL 在整個任務生成期間都必須有效,建議有效期限不少於 60 分鐘。
  5. 圖片建議使用 JPG、PNG 或 WebP;影片建議 MP4/MOV;音訊建議 MP3/WAV。
  6. 影片建議每段不超過 50 MB,音訊建議每段不超過 15 MB;素材過大可能在下載或審核階段失敗。
  7. 引用第三方素材前,請確認已取得必要的著作權、肖像權與使用授權。

素材頁面打得開,不代表影片位元組可以下載:目標必須回傳實際的媒體檔案,而不是 HTML 分享頁。URL 可用也不代表其編碼、時長或尺寸符合目標模型的要求。Google 影片模型的圖片還可以使用 Base64 或檔案上傳,請見 Google 影片 API 規範;參考影片仍只接受 URL。

3.6 本機素材上傳:先申請,再 PUT

這個 API 提供暫存媒體空間,不會生成影片、轉碼、壓縮或自動擷取影格。

第一步:在伺服器端申請上傳憑證。

POST https://api.88api.ai/v1/media/uploads
Authorization: Bearer sk-xxxx
Content-Type: application/json
{
  "size": 1234567,
  "mime_type": "video/mp4",
  "sha256": "<檔案原始位元組的SHA-256,以不含padding的Base64URL編碼>"
}
欄位規則
size原始檔案的位元組數,整數,1–100,000,000;不是 Base64 字串長度
mime_type必須與實際檔案格式及上傳時的 Content-Type 一致
sha256將 SHA-256 的 32 位元組摘要做 Base64URL 編碼,不帶 =;不是 64 位的十六進位字串

可上傳的類型:video/mp4、video/webm、video/quicktime;audio/mpeg、audio/wav、audio/ogg、audio/mp4、audio/flac;image/png、image/jpeg、image/webp、image/gif。上傳上限不等於模型的參考素材上限,仍須符合目標模型更嚴格的限制。

HTTP 200 回應範例(尖括號為預留位置,不是可直接使用的真實值):

{
  "url": "https://assets.88api.ai/media/reference-media/<object-id>.mp4",
  "upload_url": "https://assets.88api.ai/media/reference-media/<object-id>.mp4",
  "method": "PUT",
  "headers": {
    "Content-Type": "video/mp4",
    "X-Media-Upload-Token": "<scoped-upload-token>"
  },
  "expires_at": 1789373400
}

這裡的 expires_at 是 上傳憑證的到期時間(Unix 秒,約 10 分鐘),不是檔案的保存期限;此時的 url 也不代表檔案已上傳完成。憑證只允許寫入指定的檔案,無法存取整個儲存貯體。

第二步:在憑證到期前上傳原始二進位檔。 原樣使用回應中的 upload_url、method 與 headers:

# 以下變數取自申請憑證的回應;請勿把 API_KEY 傳給上傳網址。
curl --request PUT "$UPLOAD_URL" \
  --header 'Content-Type: video/mp4' \
  --header "X-Media-Upload-Token: $UPLOAD_TOKEN" \
  --data-binary @reference.mp4

這不是 multipart/form-data,不使用 -F,也不是 JSON Base64。必須傳送與申請時一致的 Content-Length、MIME 與檔案位元組;瀏覽器可以直接傳送 File/Blob,由瀏覽器產生 Content-Length。伺服器端的串流用戶端也必須提供正確的長度,不要使用長度未知的 chunked 上傳。

HTTP 201 代表上傳完成,回傳 url、size、mime_type、expires_at。這次的 expires_at 才是素材的到期時間(Unix 秒,自該物件上傳起算 30 天)。確認成功後,再把 url 填入目標模型要求的參考欄位。瀏覽器應由自家後端申請憑證後直接上傳,不能在前端暴露長期有效的 API Key。

同一個上傳網址不允許覆寫:重複 PUT 會回傳 409,請重複使用已上傳成功的網址;遇到不確定結果的網路中斷時,先以匿名 HEAD 檢查該 URL,不要馬上重新申請新物件。上傳與引用都不會產生新的生成任務。

5. 支援模型的首尾影格模式

首影格與尾影格放在 metadata.firstFrame 與 metadata.lastFrame:

下例適用於支援這些欄位的多參考模型。Veo 與 Gemini Omni 同樣接受 metadata.firstFrame 與 metadata.lastFrame,其他寫法請見 Google 影片 API 規範。

{
  "model": "<MODEL_ID>",
  "prompt": "從清晨空曠的廣場平順轉場到夜晚燈火亮起的同一構圖",
  "duration": 10,
  "ratio": "16:9",
  "metadata": {
    "firstFrame": "https://cdn.example.com/start.jpg",
    "lastFrame": "https://cdn.example.com/end.jpg"
  }
}

規則:

  • lastFrame 不能單獨使用,必須同時提供 firstFrame。
  • 大多數支援首尾影格的模型,不允許首尾影格與一般參考圖片/影片/音訊混用。
  • 模型能力表標示為「互斥」時,請在首尾影格與一般參考素材之間擇一。

6. 目前影片模型能力表

下列表格說明已整理的公開模型能力,並非客戶請求時的固定模型 ID。實際請求必須使用目前 API Key 從 /v1/models 取得的 id。不同帳號可能看到不同的模型 ID;只要使用統一的 /v1/videos 協定,用戶端無須了解伺服器端如何選擇模型。

各品牌呼叫規範

不同品牌的參數寫法與素材欄位不完全相同,串接前請先閱讀對應的品牌頁:

品牌頁模型
Seedance 官方版seedance-2.5官方版、seedance-2.0官方版、seedance-2.0-fast官方版、seedance-2.0-mini官方版
SD 雲廠商版SD2.5、SD2.0
Seedance 官轉Seedance 2.0 Mini(多解析度)、Seedance 2.0(多解析度)、Seedance 2.0 Fast(多解析度)、Seedance 2.5(多解析度)
Googleveo-3.1、veo-3.1-fast、gemini-omni-flash、gemini-omni-flash-1.1
可靈 Klingkling-3.0-turbo
MiniMaxminimax-h3(同 minimax_h3)
萬相 Wanwan3.0-video、wan3.0-video-prime
Grokgrok-imagine-video、grok-imagine-video-1.5

模型能力總表

序號模型能力/公開名稱時長比例輸出圖/影片/音訊上限首尾影格音訊控制
1seedance-2.5官方版4–30 秒,預設 4 秒16:9 9:16 1:1 4:3 3:4 21:9720P30 / 10 / 10支援,與一般素材互斥無法設定
2seedance-2.0官方版4–15 秒,預設 4 秒同上720P9 / 3 / 3支援,與一般素材互斥無法設定
3seedance-2.0-fast官方版4–15 秒,預設 4 秒同上720P9 / 3 / 3支援,與一般素材互斥無法設定
4seedance-2.0-mini官方版4–15 秒,預設 5 秒同上480P/720P,預設 480P9 / 3 / 3模型說明未列出首尾影格模式不提供生成音訊開關
5SD2.54–30 秒必須指定:16:9 9:16 1:1 4:3 3:4 21:9480P/720P/1080P,預設 480P30 / 10 / 10支援,與一般素材互斥支援 generate_audio
6SD2.04–15 秒1:1(預設) 21:9 16:9 9:16 3:4 4:3480P/720P/1080P/4K,預設 480P9 / 3 / 3,三類合計最多 12支援,與一般素材互斥無法設定
7Seedance 2.0 Mini(多解析度)4–15 秒;支援 -1,預設 5 秒adaptive(預設) 16:9 4:3 1:1 3:4 9:16 21:9480P/720P9 / 3 / 3支援,與一般素材互斥預設帶音訊,可關閉
8Seedance 2.0(多解析度)4–15 秒;支援 -1,預設 5 秒同上480P/720P/1080P/4K9 / 3 / 3支援,與一般素材互斥預設帶音訊,可關閉
9Seedance 2.0 Fast(多解析度)4–15 秒;支援 -1,預設 5 秒同上480P/720P9 / 3 / 3支援,與一般素材互斥預設帶音訊,可關閉
10Seedance 2.5(多解析度)4–30 秒;支援 -1,預設 -1(自動)同上480P/720P/1080P30 / 10 / 10支援,與一般素材互斥預設帶音訊,可關閉
11veo-3.1-fast4/6/8 秒,參考圖固定 8 秒,延長每次增加 7 秒16:9 9:16720P/1080P/4K3 / 0 / 0支援首影格、首尾影格與參考圖原生音訊,可用 generate_audio 關閉
12veo-3.14/6/8 秒,參考圖固定 8 秒,延長每次增加 7 秒16:9 9:16720P/1080P/4K3 / 0 / 0支援首影格、首尾影格與參考圖原生音訊,可用 generate_audio 關閉
13gemini-omni-flash3–10 秒16:9 9:16720P10 / 1 / 0支援首影格、首尾影格不支援
14gemini-omni-flash-1.13–10 秒,延長每次增加約 10 秒16:9 9:16720P/1080P/4K10 / 3 / 0支援首影格、首尾影格不支援
15kling-3.0-turbo4–15 秒16:9 9:16 1:1720P/1080P/2K/4K,預設 720P30(含首尾影格)/ 10 / 0支援,可與參考圖並用支援 generate_audio
16minimax-h34–15 秒,預設 5 秒16:9(預設) 9:16 1:1 4:3 3:4 21:9768P/1080P/2K(超解析度),預設 768P9 / – / 3支援,與一般素材互斥成品自帶音軌,無法設定
17wan3.0-video2–30 秒,預設 5 秒;支援 -1adaptive(預設) 16:9 9:16 1:1 4:3 3:4 21:9480P/720P/1080P,預設 1080P10 / 5 / 5(使用 media 陣列)支援,與一般素材互斥無法設定
18wan3.0-video-prime同上同上同上同上同上無法設定
19grok-imagine-video1–15 秒,預設 8 秒16:9 9:16 1:1 4:3 3:4 3:2 2:3(使用 aspect_ratio)480P/720P1 / 0 / 0不支援不支援
20grok-imagine-video-1.51–15 秒,預設 8 秒同上480P/720P/1080P1 / 0 / 0不支援不支援

表中序號從 1 開始,只是為了方便閱讀,不是請求參數。表中的公開名稱與能力系列也僅供對照;實際呼叫時必須使用目前 API Key 從 /v1/models 回傳的精確 id,不要自行拼接名稱或推測對應關係。

模型專屬規則

各品牌的欄位差異很大,以下是最容易出錯的幾點,完整規則請見上方各品牌頁:

  • 萬相 wan3.0-video* 的首影格、尾影格和參考素材建議放在 media 陣列;也接受 metadata.firstFrame/lastFrame/referenceImages/referenceVideos/referenceAudios(含底線寫法)、頂層 firstFrame/first_frame/lastFrame/last_frame 和頂層 images(第 1 張首影格、第 2 張尾影格,最多 2 張),都會轉換為 media。無法識別的素材欄位會回傳 400。
  • Grok 的 aspect_ratio 與 ratio 等效,同時填寫多個比例欄位時值必須一致;最多 1 張輸入圖片。
  • minimax-h3 的參考圖片使用 metadata.referenceImages,首尾影格使用 metadata.firstFrame/lastFrame;頂層 images 的含義會隨分配的線路而不同,請勿依賴。480P、4K 已下架。
  • Veo 與 Gemini Omni 的參考影片使用頂層 video/videos,不使用 metadata.referenceVideos。
  • Seedance 官轉也接受單數欄位 image、input_reference、video、audio 作為參考素材,同一素材重複填寫只計一次;size 與 ratio、aspect_ratio 或 resolution 衝突時回傳 400。該組模型還支援虛擬人像庫、2.5 樣片、尾影格、聯網搜尋、取消排隊任務和官方任務 ID,見 Seedance 官轉。
  • SD2.5 必須明確指定比例,不支援 auto。
  • Seedance 官方版、SD2.5、SD2.0 與 kling-3.0-turbo 每個系列只有一個模型名稱,以 resolution 選擇規格並依規格計費;未傳時使用該系列最低規格,不支援的規格回傳 400。先前帶有規格後綴的舊名稱(如 SD2.5 720P、SD2.0 4K)仍可呼叫,但固定為名稱中的規格,已不在模型廣場顯示;可靈帶規格後綴的舊名稱已下架。

7. 提交回應

提交成功後會立即回傳公開的任務 ID:

{
  "id": "task_xxxxxxxxxxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "<MODEL_ID>",
  "status": "queued",
  "progress": 0,
  "created_at": 1788249600
}

請保存 id。標準 API 不保證回傳 task_id,新串接必須使用 id。

HTTP 200 代表「任務已受理」,不代表影片已生成完成。

8. 輪詢任務

curl 'https://api.88api.ai/v1/videos/task_xxxxxxxxxxxxxxxxxxxxxxxx' \
  --header 'Authorization: Bearer sk-xxxx'

統一狀態

status意義用戶端動作
queued已受理,排隊中繼續等待
in_progress生成中繼續等待
completed已完成讀取回傳的 url,播放、下載或再次引用
failed已失敗讀取 error.code 與 error.message
unknown狀態未知短暫重試,若持續出現請聯絡客服

完成回應範例:

{
  "id": "task_xxxxxxxxxxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "<MODEL_ID>",
  "status": "completed",
  "progress": 100,
  "created_at": 1788249600,
  "completed_at": 1788250200,
  "url": "https://assets.88api.ai/media/task-videos/2026/09/<object-key>.mp4",
  "video_url": "https://assets.88api.ai/media/task-videos/2026/09/<object-key>.mp4",
  "result_url": "https://assets.88api.ai/media/task-videos/2026/09/<object-key>.mp4"
}

失敗回應範例:

{
  "id": "task_xxxxxxxxxxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "<MODEL_ID>",
  "status": "failed",
  "progress": 100,
  "error": {
    "code": "video_generation_failed",
    "message": "影片生成失敗"
  }
}

輪詢策略

  1. 提交後等待 3–5 秒再進行第一次查詢。
  2. 建議每 10–15 秒查詢一次,不要每秒輪詢。
  3. 互動式用戶端可採用 15 分鐘的等待上限,伺服器端可持久保存任務 ID 後繼續查詢或接收回呼。用戶端停止等待,不代表任務失敗、取消或退款。
  4. 任務處於 queued 或 in_progress 期間,請勿重複提交同一任務,否則可能重複計費。
  5. 同時設定回呼時,輪詢可作為備援機制。

單次 HTTP 請求逾時、用戶端總等待時間與伺服器端任務期限是不同的概念。不要把 15 分鐘寫成伺服器端必定終止的保證,也不要只憑 progress=100 判斷成功;請以 status 為準。頁面或網路恢復後,可以繼續查詢原任務。

9. 回呼(Webhook)

本節僅適用於明確支援回呼的型號。Google 影片模型(Veo、Gemini Omni)以及其他未明確支援回呼的模型請使用輪詢,不要設定 callback_url。

提交時設定

{
  "model": "<MODEL_ID>",
  "prompt": "賽博龐克城市中的無人機空拍",
  "duration": 8,
  "size": "16:9",
  "callback_url": "https://api.customer.example/webhooks/88api/video"
}

callback_url 必須:

  • 是可從公開網路存取的 HTTP(S) URL,正式環境強烈建議使用 HTTPS;
  • 不超過 2048 個字元;
  • 不能指向私有 IP、localhost、鏈路本地位址或受限制的連接埠;
  • 能在 15 秒內回傳任一 2xx 回應。

回呼只會在終態時傳送:

  • video.completed
  • video.failed

回呼請求標頭

Content-Type: application/json
User-Agent: NewAPI-Task-Callback/1.0
X-NewAPI-Event: video.completed
X-NewAPI-Delivery: evt_task_xxx_completed
X-NewAPI-Timestamp: 1788250200
X-NewAPI-Signature: sha256=<hex-hmac>

完成事件

{
  "id": "evt_task_xxxxxxxxxxxxxxxxxxxxxxxx_completed",
  "type": "video.completed",
  "created_at": 1788250200,
  "data": {
    "task_id": "task_xxxxxxxxxxxxxxxxxxxxxxxx",
    "model": "<MODEL_ID>",
    "status": "completed",
    "progress": "100%",
    "output_url": "https://assets.88api.ai/media/task-videos/2026/09/<object-key>.mp4",
    "created_at": 1788249600,
    "completed_at": 1788250200
  }
}

失敗事件

{
  "id": "evt_task_xxxxxxxxxxxxxxxxxxxxxxxx_failed",
  "type": "video.failed",
  "created_at": 1788250200,
  "data": {
    "task_id": "task_xxxxxxxxxxxxxxxxxxxxxxxx",
    "model": "<MODEL_ID>",
    "status": "failed",
    "progress": "100%",
    "error": "影片生成失敗",
    "created_at": 1788249600,
    "completed_at": 1788250200
  }
}

驗證回呼簽章

簽章金鑰是提交該任務時使用的完整 API Key(包含 sk- 前綴)。簽章原文為:

X-NewAPI-Timestamp + "." + 原始請求主體位元組

Node.js 驗證範例:

import crypto from "node:crypto";
import express from "express";

const app = express();

// 必須先保留原始請求主體,再解析 JSON。
app.use(
  express.json({
    verify: (req, _res, buffer) => {
      req.rawBody = buffer;
    },
  }),
);

app.post("/webhooks/88api/video", (req, res) => {
  const timestamp = req.header("X-NewAPI-Timestamp") || "";
  const timestampNumber = Number(timestamp);
  if (
    !Number.isFinite(timestampNumber) ||
    Math.abs(Date.now() / 1000 - timestampNumber) > 300
  ) {
    return res.status(401).send("stale timestamp");
  }
  const received = (req.header("X-NewAPI-Signature") || "").replace(
    /^sha256=/,
    "",
  );
  const secret = process.env.API_KEY; // 完整的 sk-xxxx
  const signed = Buffer.concat([
    Buffer.from(`${timestamp}.`, "utf8"),
    req.rawBody,
  ]);
  const expected = crypto
    .createHmac("sha256", secret)
    .update(signed)
    .digest("hex");

  const valid =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
  if (!valid) return res.status(401).send("invalid signature");

  // 以 X-NewAPI-Delivery 或 req.body.id 做冪等去重。
  console.log(req.body.type, req.body.data.task_id);
  return res.sendStatus(204);
});

回呼重試與冪等

  • 回呼逾時、發生網路錯誤或回傳非 2xx 時,88API 會以指數退避重試。
  • 單次請求逾時為 15 秒,最多嘗試 8 次,退避上限為 15 分鐘。
  • 同一事件可能被投遞多次。用戶端必須以 X-NewAPI-Delivery 或事件 id 做冪等去重。
  • 建議拒絕與本機時間相差超過 5 分鐘的回呼,以降低重放攻擊風險。
  • 建議先驗證簽章、保存事件並立即回傳 2xx,耗時的業務邏輯放進自己的非同步佇列。

10. 取得影片檔案

任務進入 completed 後,優先使用查詢回傳的 url;相容用戶端可退而讀取 video_url/result_url,回呼則使用 data.output_url。這些欄位是伺服器端交付的結果,請勿從任務 ID 或內部 ID 自行組出媒體路徑。

# RESULT_URL 原樣取自完成回應,請勿向媒體伺服器傳送 API Key。
curl --fail --location "$RESULT_URL" --output result.mp4

說明:

  • 用戶端應允許 HTTP 3xx 重新導向,curl 需要加上 --location。
  • 本站封存的結果使用 https://assets.88api.ai/media/... 匿名網址;符合安全直連條件的官方結果會保留原始 URL,不會強制改成本站網域。
  • 其他無法直接公開或無法明確對應的產出,可能仍會回傳帶有存取權限參數的相容網址。請完整保留伺服器回傳的 URL 與必要參數,不要自行刪除參數或替換網域;也不要替 assets 網址額外加上查詢參數(會回傳 400)。
  • 舊的 /v1/videos/{id}/content 只為已封存影片提供 307 相容重新導向,不應視為官方直連網址或所有產出的必經入口;回傳 404 時請重新查詢任務取得實際結果網址,不要盲目重新生成。
  • 回呼中的 output_url 是不透明的結果網址,可能有有效期限;請勿解析其網域或路徑結構。
  • 不要拼接、保存或依賴任何其他任務 ID;只使用提交回應回傳的公開 id。
  • 若需要長期保存成品,請在完成後下載到自己的物件儲存空間。

9.1 保存、播放與跨模型引用

  • 本站上傳的素材與封存影片,自各自物件上傳起保存 30 天;重複讀取、查詢舊任務或重複引用都不會延長期限,也不會重新上傳一份。官方結果 URL 依其來源本身的有效期限處理,不保證 30 天。
  • 沒有簽章參數、無須登入,不代表會永久保存;檔案到期後回傳 410,被生命週期規則清除後可能回傳 404。任務記錄仍在,也不代表二進位檔案仍然存在。
  • 公開媒體支援 GET、HEAD、單段 Range 與瀏覽器跨來源讀取。可讀取 X-Media-Expires-At(Unix 秒);Range 成功時通常回傳 206。快取不會延長原本的到期時間,也不保證各地區下載速度相同。
  • Veo 生成的結果可以作為其他支援參考影片的模型的輸入,前提是網址仍然有效,且格式、尺寸、時長符合目標模型的要求。直接把結果 URL 填入該模型的參考欄位即可,無須先下載再上傳;Veo 本身只在延長影片時接受輸入影片,不支援參考影片。
{
  "model": "<MODEL_ID>",
  "prompt": "保持參考影片的主體與動作,延續鏡頭",
  "duration": 4,
  "ratio": "16:9",
  "metadata": {
    "referenceVideos": ["<上一個任務回傳的完整HTTPS結果URL>"]
  }
}

11. JavaScript 完整輪詢範例

需要 Node.js 22 以上;請在伺服器端設定 API_KEY、MODEL_ID 與 REFERENCE_FILE(MP4 檔案路徑)。MODEL_ID 應取自目前 API Key 的 GET /v1/models 回應。設定 TASK_ID 可直接接續舊任務,不會重新上傳或生成。此範例會建立一次付費生成,最後只印出下一輪的請求主體,不會自動發起第二次生成。為方便閱讀,範例會把檔案讀入記憶體;大量處理大型檔案時請限制並行數。

// video-api-example.mjs — Node.js 22+, run with API_KEY, MODEL_ID, and REFERENCE_FILE.
import { readFile, stat } from "node:fs/promises";
import { createHash } from "node:crypto";

const apiKey = process.env.API_KEY;
if (!apiKey) throw new Error("Set API_KEY on your server");
const baseUrl = "https://api.88api.ai";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function apiJSON(path, body) {
  // No automatic POST retry: a timed-out create may already have been accepted.
  const response = await fetch(baseUrl + path, {
    method: "POST",
    headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(120000),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  return response.json();
}

async function uploadMedia(file, mimeType) {
  const info = await stat(file);
  if (!info.isFile() || info.size < 1 || info.size > 100000000) {
    throw new Error("File must contain 1–100000000 bytes");
  }
  const bytes = await readFile(file);
  const permit = await apiJSON("/v1/media/uploads", {
    size: bytes.length, mime_type: mimeType,
    sha256: createHash("sha256").update(bytes).digest("base64url"),
  });
  const target = new URL(permit.upload_url);
  if (target.origin !== "https://assets.88api.ai" || permit.method !== "PUT") {
    throw new Error("Unexpected upload destination; do not send credentials");
  }
  const response = await fetch(target, {
    method: "PUT", headers: permit.headers, body: bytes,
    redirect: "error", signal: AbortSignal.timeout(120000),
  });
  // A 409/ambiguous upload is not automatically retried; HEAD permit.url first.
  if (response.status !== 201) throw new Error(`Upload HTTP ${response.status}`);
  return response.json();
}

async function waitForVideo(id, budgetMs = 15 * 60 * 1000) {
  const deadline = Date.now() + budgetMs;
  let delay = 4000;
  while (Date.now() < deadline) {
    await sleep(Math.min(delay, deadline - Date.now()));
    const remaining = deadline - Date.now();
    if (remaining <= 0) break;
    let response, state;
    try {
      response = await fetch(`${baseUrl}/v1/videos/${encodeURIComponent(id)}`, {
        headers: { Authorization: `Bearer ${apiKey}` },
        signal: AbortSignal.timeout(Math.min(30000, remaining)),
      });
      if (response.ok) state = await response.json();
    } catch {
      delay = Math.min(delay * 2, 60000);
      continue; // Retry a read, never create a replacement task.
    }
    if (response.status === 429 || response.status >= 500) {
      const retryAfter = response.headers.get("Retry-After");
      const seconds = retryAfter && /^\d+$/.test(retryAfter)
        ? Number(retryAfter)
        : (Date.parse(retryAfter || "") - Date.now()) / 1000;
      delay = Number.isFinite(seconds) && seconds > 0
        ? Math.max(1000, seconds * 1000)
        : Math.min(delay * 2, 60000);
      await response.body?.cancel();
      continue;
    }
    if (!response.ok) throw new Error(`Task ${id}: HTTP ${response.status}`);
    if (state.status === "completed") {
      const result = state.url || state.video_url || state.result_url;
      if (typeof result !== "string" || new URL(result).protocol !== "https:") {
        throw new Error(`Task ${id} completed without a usable HTTPS result URL`);
      }
      return result; // Keep official URLs and capability query parameters intact.
    }
    if (state.status === "failed") {
      throw new Error(`Task ${id}: ${state.error?.message || "video generation failed"}`);
    }
    if (!["queued", "in_progress", "unknown"].includes(state.status)) {
      throw new Error(`Task ${id}: unexpected status ${state.status}`);
    }
    delay = 12000;
  }
  throw new Error(`Stopped waiting for ${id}; save this ID and query later. Not cancelled.`);
}

async function main() {
  // Resume a saved task without uploading or creating another paid task.
  let id = process.env.TASK_ID;
  if (!id) {
    if (!process.env.MODEL_ID) throw new Error("Set MODEL_ID from GET /v1/models");
    if (!process.env.REFERENCE_FILE) throw new Error("Set REFERENCE_FILE");
    const media = await uploadMedia(process.env.REFERENCE_FILE, "video/mp4");
    const task = await apiJSON("/v1/videos", {
      model: process.env.MODEL_ID, prompt: "保持參考影片的主體與動作,延續鏡頭",
      duration: 4, ratio: "16:9", metadata: { referenceVideos: [media.url] },
    });
    id = task.id;
    if (!id) throw new Error("Create response did not contain a task ID; do not blindly retry");
    console.log("Save task ID:", id); // Persist to your database in production.
  }
  const resultURL = await waitForVideo(id);
  console.log("Result URL:", resultURL); // This grants media access; do not log publicly.
  // This only shows the NEXT request body; it does not create a second paid task.
  console.log(JSON.stringify({
    model: process.env.MODEL_ID, prompt: "保持參考影片的主體與動作,延續鏡頭",
    duration: 4, ratio: "16:9", metadata: { referenceVideos: [resultURL] },
  }, null, 2));
}

await main();

12. 錯誤處理與重試

情況處理建議
HTTP 400檢查模型名稱、時長、比例、素材數量與欄位位置
HTTP 401API Key 缺少、錯誤或已失效
HTTP 403目前的 Key 沒有該模型或群組的權限
HTTP 404任務不存在、不屬於目前帳號,或成品尚未就緒
上傳 HTTP 409指定物件已存在且不可覆寫;先以 HEAD 確認,再重複使用已成功的網址
媒體 HTTP 410檔案已到期;讀取與重複引用都不會延長期限,清除後也可能回傳 404
上傳 HTTP 415檔案簽章與宣告的 MIME 不符;不能只改副檔名來冒充其他格式
媒體 HTTP 416Range 超出檔案範圍或格式不受支援;請重新以 HEAD 確認檔案大小
HTTP 429請求過於頻繁或觸及額度限制;依 Retry-After 或以指數退避重試
HTTP 5xx暫時性服務錯誤;查詢請求可退避後重試
status=failedHTTP 查詢成功但生成失敗;請讀取 error.message
素材下載失敗確認 URL 可從公開網路存取、尚未過期,且沒有防盜連結或驗證依賴
unsupported_reference_media模型不支援該參考素材類型,上傳或更換網域都無法解決
must be a public HTTPS URL本機檔案請先上傳,不能提交 Blob、Data URL、HTTP 或需要登入的網址
input_download_failed檢查目標服務實際上是否可達、DNS/網路狀況與媒體有效期限,而不只是本機能否開啟
內容安全攔截調整提示詞或參考素材,不要原封不動地無限重試

POST /v1/videos 目前不提供客戶自訂冪等鍵的保證。如果已送出完整請求後遇到網路逾時,請勿盲目立即重送,否則可能生成並計費多個任務。