数字人 V3 合成 API 接入教程
在线效果体验:https://ai.hihookeji.com
提交接口:
https://api.hihookeji.com/api/humanmetav3/index
查询接口:
https://api.hihookeji.com/api/queryclonehumanv3/index
返回格式:
application/json
请求方式:
HTTP POST
一、接口概述
本接口将 原视频 与驱动 音频(wav) 合成数字人开口视频。提交后可通过 notify_url 回调拿结果,也可用 查询接口 按 taskid 轮询状态与成果地址。
| 项目 | 说明 |
|---|---|
| 提交接口 | https://api.hihookeji.com/api/humanmetav3/index |
| 查询接口 | https://api.hihookeji.com/api/queryclonehumanv3/index |
| 认证方式 | 请求体字段 key(你的 API 密钥,与业务参数一并提交) |
| Content-Type | application/json(推荐)或 application/x-www-form-urlencoded |
| 提交业务判断 | 提交响应以 code 为准:示例成功为 1,失败常见为 500 |
| 查询业务判断 | 查询成功时 code 为 200 |
| 计费 | 1 元 / 60 秒,按秒计费 |
二、请求参数
2.1 提交合成(humanmetav3)
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| key | string | 是 | API 密钥,放在请求体中与其它参数一起提交 | 你的密钥 |
| video_url | string | 是 | 原视频链接(公网可访问),无需训练,直接传入即可驱动 | https://abc.com/video.mp4 |
| audio_url | string | 是 | 驱动音频地址,须公网可访问,wav 格式,建议 URL 以 .wav 结尾 | https://abc.com/audio.wav |
| notify_url | string | 是 | 合成结果通知地址(需公网可达) | 见下方「回调测试」 |
| video_name | string | 否 | 视频名称 | 测试视频 |
回调测试:联调异步回调时,建议先打开 https://webhook.site/ 获取自己的唯一 URL,将该地址填入 notify_url。提交任务后可在网页上直接查看回调字段与成果链接,无需先部署自己的回调服务。
2.2 查询结果(queryclonehumanv3)
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| key | string | 是 | API 密钥,放在请求体中与其它参数一起提交 | 你的密钥 |
| taskid | string | 是 | 任务 ID;若查训练结果则传 mode_id | 374 |
| tasktype | string | 是 | huamanclonev3:查询合成结果;huamanclonev1:V1 查询 | huamanclonev3 |
tasktype取值请按接口约定原样传递(含拼写huamanclonev3/huamanclonev1)。新接入合成任务一般用huamanclonev3+ 提交返回的data.taskid。
合成不会马上完成:提交后请 每隔 60 秒 主动调一次本查询接口,直到
status为3(成功)或4(失败),不要提交后立刻只查一次。
三、更新说明
2025-12-03
- 新增
video_url:可直接传原视频链接,无需训练
四、返回结果说明
4.1 任务提交成功
立即返回任务受理结果,合成视频通过 notify_url 回调。
| 字段 | 说明 |
|---|---|
| code | 成功时示例为 1 |
| msg | 说明信息,如 ok |
| time | 时间戳 |
| data.taskid | 视频任务 ID,与回调关联 |
{
"code": 1,
"msg": "ok",
"time": "1722863371",
"data": {
"taskid": 374
}
}
4.2 任务提交失败
{
"code": 500,
"msg": "不能为空"
}
4.3 异步回调 Notify
服务端合成完成后,向 notify_url POST 推送结果(表单字段;不是 JSON Body)。
| 字段 | 说明 |
|---|---|
| errcode | 0 成功;1001 失败(与提交响应的 code 不同,回调按此处判断) |
| taskid | 视频任务 ID |
| target_file | 成果视频链接(如 https://1234.com/video.mp4),请及时下载 |
| video_time | 视频时长,单位:秒 |
| msg | 通知信息 |
本接口回调的
target_file为 视频 URL 字符串,不是文件流上传。收到后请尽快下载保存。
4.4 查询合成结果成功(tasktype=huamanclonev3)
查询成功时 code 为 200。
| data 字段 | 说明 |
|---|---|
| taskid | 任务 ID |
| status | 合成状态:-1 编辑中,1 排队中,2 合成中,3 合成成功,4 合成失败 |
| audio_url | 驱动音频地址 |
| mode_id | 模型 ID(历史字段,新流程以 video_url 提交为主) |
| result_video_url | 成果视频地址(成功时有值) |
| video_time | 视频时长(秒) |
{
"code": 200,
"msg": "任务查询成功",
"data": {
"taskid": 123456,
"status": 3,
"audio_url": "https://oml.img.vodeshop.com/uploads/voice/20250224/20250224114152f79b25015.wav",
"mode_id": 363546,
"result_video_url": "https://opendigman.oss-cn-hangzhou.aliyuncs.com/aivideo/202502/24/20250224_1150261740369026196629.mp4",
"video_time": 11.21
}
}
当 status=3 时,用 data.result_video_url 下载成果视频即可。查询非即时有结果:建议 每 60 秒轮询一次,直到成功或失败。
五、接入流程建议
- 准备公网可访问的原视频(
video_url)与驱动音频(audio_url,标准 wav,URL 建议以.wav结尾)。
- 申请 API Key,作为请求体字段
key与其它参数一起提交。
- 填入
notify_url;联调可用 webhook.site 唯一地址。
- 调用提交接口,成功后记录
data.taskid。
- 结果获取二选一或并用:
- 等待
notify_url回调(errcode=0后下载target_file);
- 或主动轮询查询接口:
taskid+tasktype=huamanclonev3,每隔 60 秒查一次,直到status=3再下载result_video_url(status=4则失败结束)。
- 成果链接请及时下载落盘。
六、Python 完整代码
依赖:requests(pip install requests)
# -*- coding: utf-8 -*-
"""
数字人 V3 合成 API - Python 完整示例
接口: https://api.hihookeji.com/api/humanmetav3/index
"""
import json
import os
import time
import requests
SUBMIT_URL = "https://api.hihookeji.com/api/humanmetav3/index"
QUERY_URL = "https://api.hihookeji.com/api/queryclonehumanv3/index"
API_KEY = "你的密钥" # 替换为真实密钥
def create_human_video(
video_url,
audio_url,
notify_url,
video_name="测试视频",
timeout=60,
):
"""提交数字人合成任务(异步)。"""
payload = {
"key": API_KEY,
"video_url": video_url,
"audio_url": audio_url,
"notify_url": notify_url,
"video_name": video_name,
}
resp = requests.post(
SUBMIT_URL,
json=payload,
headers={"Content-Type": "application/json"},
timeout=timeout,
)
resp.raise_for_status()
return resp.json()
def query_human_video(taskid, tasktype="huamanclonev3", timeout=60):
"""
查询任务结果。
tasktype: huamanclonev3=查询合成结果;huamanclonev1=V1 查询
"""
payload = {
"key": API_KEY,
"taskid": str(taskid),
"tasktype": tasktype,
}
resp = requests.post(
QUERY_URL,
json=payload,
headers={"Content-Type": "application/json"},
timeout=timeout,
)
resp.raise_for_status()
return resp.json()
def wait_human_video(taskid, interval_sec=60, max_tries=60, tasktype="huamanclonev3"):
"""
每隔 interval_sec 秒查询一次,直到成功(status=3)、失败(status=4) 或超过次数。
合成不会马上有结果,默认间隔 60 秒。
"""
last = None
for i in range(max_tries):
last = query_human_video(taskid, tasktype=tasktype)
data = last.get("data") or {}
status = data.get("status")
print(f"第 {i + 1} 次查询 status={status}:", json.dumps(last, ensure_ascii=False))
if int(last.get("code") or 0) == 200 and status is not None:
status = int(status)
if status == 3:
return last
if status == 4:
raise RuntimeError(f"合成失败: {last.get('msg')}")
time.sleep(interval_sec)
raise TimeoutError(f"超过轮询次数仍未完成,最后结果: {last}")
def download_result_video(video_url, save_path):
"""根据回调 target_file 或查询 result_video_url 下载成果视频。"""
r = requests.get(video_url, timeout=120)
r.raise_for_status()
parent = os.path.dirname(save_path)
if parent:
os.makedirs(parent, exist_ok=True)
with open(save_path, "wb") as f:
f.write(r.content)
return save_path
# ---------------------------------------------------------------------------
# Flask 异步回调接收示例(可选)
# 安装: pip install flask
# ---------------------------------------------------------------------------
def create_notify_app(save_dir="./callback_video"):
from flask import Flask, request, jsonify
app = Flask(__name__)
os.makedirs(save_dir, exist_ok=True)
@app.route("/Notify", methods=["POST"])
def notify():
# 回调为表单字段,不是 JSON Body
taskid = request.form.get("taskid")
msg = request.form.get("msg")
errcode = request.form.get("errcode") # 成功 0,失败 1001
target_file = request.form.get("target_file") or ""
video_time = request.form.get("video_time")
if str(errcode) == "0" and target_file:
path = os.path.join(save_dir, f"{taskid}.mp4")
download_result_video(target_file, path)
return jsonify({
"ok": True,
"taskid": taskid,
"saved": path,
"video_time": video_time,
"errcode": errcode,
})
return jsonify({
"ok": False,
"taskid": taskid,
"msg": msg,
"errcode": errcode,
})
return app
if __name__ == "__main__":
result = create_human_video(
video_url="https://abc.com/video.mp4",
audio_url="https://abc.com/audio.wav",
notify_url="https://webhook.site/你的唯一ID",
video_name="测试视频",
)
print("任务提交:", json.dumps(result, ensure_ascii=False, indent=2))
# 成功时 code==1,记录 data.taskid
taskid = (result.get("data") or {}).get("taskid")
if taskid:
# 不会马上有结果:默认每 60 秒查一次,直到 status=3/4
query = wait_human_video(taskid, interval_sec=60)
url = (query.get("data") or {}).get("result_video_url")
if url:
path = download_result_video(url, f"./output/{taskid}.mp4")
print("已下载:", path)
# ---------- 启动回调服务(需要时取消注释)----------
# app = create_notify_app()
# app.run(host="0.0.0.0", port=8080)
八、Java 完整代码
依赖:JDK 8+,使用内置 HttpURLConnection,无需第三方库。
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 数字人 V3 合成 API - Java 完整示例
* 接口: https://api.hihookeji.com/api/humanmetav3/index
*/
public class HumanMetaV3Demo {
private static final String SUBMIT_URL = "https://api.hihookeji.com/api/humanmetav3/index";
private static final String QUERY_URL = "https://api.hihookeji.com/api/queryclonehumanv3/index";
private static final String API_KEY = "你的密钥"; // 替换为真实密钥
public static class HumanRequest {
public String videoUrl;
public String audioUrl;
public String notifyUrl;
public String videoName = "测试视频";
}
public static String createHumanVideo(HumanRequest req, int timeoutMs) throws IOException {
return postJson(SUBMIT_URL, toSubmitJson(req), timeoutMs);
}
/** tasktype: huamanclonev3=合成结果;huamanclonev1=V1 查询 */
public static String queryHumanVideo(String taskid, String tasktype, int timeoutMs) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(""key":").append(quote(API_KEY)).append(",");
sb.append(""taskid":").append(quote(taskid)).append(",");
sb.append(""tasktype":").append(quote(tasktype));
sb.append("}");
return postJson(QUERY_URL, sb.toString(), timeoutMs);
}
private static String postJson(String apiUrl, String json, int timeoutMs) throws IOException {
URL url = new URL(apiUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(15000);
conn.setReadTimeout(timeoutMs);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestProperty("Accept", "application/json");
byte[] body = json.getBytes(StandardCharsets.UTF_8);
conn.setRequestProperty("Content-Length", String.valueOf(body.length));
try (OutputStream os = conn.getOutputStream()) {
os.write(body);
}
int httpCode = conn.getResponseCode();
InputStream is = (httpCode >= 200 && httpCode < 300)
? conn.getInputStream()
: conn.getErrorStream();
String resp = readFully(is);
conn.disconnect();
return resp;
}
public static Path downloadResultVideo(String videoUrl, String savePath) throws IOException {
URL url = new URL(videoUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(15000);
conn.setReadTimeout(120000);
conn.setRequestMethod("GET");
try (InputStream is = conn.getInputStream()) {
byte[] bytes = readBytes(is);
Path path = Paths.get(savePath);
Path parent = path.getParent();
if (parent != null && !Files.exists(parent)) {
Files.createDirectories(parent);
}
Files.write(path, bytes);
return path;
} finally {
conn.disconnect();
}
}
private static String toSubmitJson(HumanRequest r) {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(""key":").append(quote(API_KEY)).append(",");
sb.append(""video_url":").append(quote(r.videoUrl)).append(",");
sb.append(""audio_url":").append(quote(r.audioUrl)).append(",");
sb.append(""notify_url":").append(quote(r.notifyUrl)).append(",");
sb.append(""video_name":").append(quote(r.videoName));
sb.append("}");
return sb.toString();
}
private static String quote(String s) {
if (s == null) {
return "null";
}
String escaped = s
.replace("\", "\\")
.replace(""", "\"")
.replace("n", "\n")
.replace("r", "\r")
.replace("t", "\t");
return """ + escaped + """;
}
private static String readFully(InputStream is) throws IOException {
return new String(readBytes(is), StandardCharsets.UTF_8);
}
private static byte[] readBytes(InputStream is) throws IOException {
if (is == null) {
return new byte[0];
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int n;
while ((n = is.read(buf)) != -1) {
bos.write(buf, 0, n);
}
return bos.toByteArray();
}
/*
* 回调 Servlet 伪代码:
* @WebServlet("/Notify")
* 表单字段: errcode(成功0/失败1001), taskid, target_file(视频URL), video_time, msg
* 成功时下载 target_file 保存为本地 mp4
*/
public static void main(String[] args) throws Exception {
HumanRequest req = new HumanRequest();
req.videoUrl = "https://abc.com/video.mp4";
req.audioUrl = "https://abc.com/audio.wav";
req.notifyUrl = "https://webhook.site/你的唯一ID";
req.videoName = "测试视频";
String resp = createHumanVideo(req, 60000);
System.out.println("任务提交: " + resp);
// 成功时 code==1,记录 data.taskid
// 合成非即时:每隔 60 秒调用 queryHumanVideo(taskid, "huamanclonev3", 60000)
// 直到 status==3 取 result_video_url 下载;status==4 失败结束
}
}
九、常见问题
| 问题 | 处理建议 |
|---|---|
| 仍传 mode_id | 已废除;请改用 video_url 直传原视频,无需训练 |
| 音频格式不对 | audio_url 必须是公网可访问的 wav;失败可用 ffmpeg 转码:ffmpeg -i 输入文件 -acodec pcm_s16le -ar 16000 输出.wav |
| 音频 / 视频链接后缀不对 | 音频 URL 建议以 .wav 结尾;视频建议 .mp4 结尾。OSS 带鉴权的长链接不以该后缀结尾时易失败,需换成后缀正确的直链或转存后再提交 |
| 驱动音频无法访问 | 确认 audio_url、video_url 公网可下载,且无防盗链拦截服务端拉取 |
| 只看 HTTP 200 | 提交接口成功示例为 code:1;查询接口成功为 code:200,勿混用 |
| 查询一直排队/合成中 | 提交后不会马上有结果:建议 每 60 秒查一次;status:1 排队,2 合成中,3 成功,4 失败 |
| 提交后立刻查询无视频 | 正常现象;请按 60 秒间隔继续轮询,勿只查一次就判定失败 |
| tasktype 填错 | 查合成结果必须传 huamanclonev3(按接口拼写原样传) |
| 异步收不到回调 | 确认 notify_url 公网可达;也可用查询接口轮询;联调可用 webhook.site |
| 回调当成 JSON 解析失败 | 回调是表单 POST,用 form/$_POST 取值,不要按 JSON Body 解析 |
| 回调成功却判失败 | 回调成功 errcode 为 0,失败为 1001 |
| 成果链接失效 | 回调 target_file 或查询 result_video_url,收到后及时下载落盘 |
| 计费疑问 | 当前 1 元 / 60 秒,按秒计费 |
十、快速对照
| 项目 | 说明 |
|---|---|
| 提交 | .../humanmetav3/index:key / video_url / audio_url / notify_url |
| 提交响应 | code:1 + data.taskid |
| 查询 | .../queryclonehumanv3/index:key / taskid / tasktype=huamanclonev3 |
| 轮询 | 非即时结果,建议 每 60 秒 查一次,直到 status=3/4 |
| 查询成功 | code:200,status=3 时取 result_video_url |
| 回调成果 | notify_url:errcode=0 + target_file(视频 URL) |

粤ICP备19001875号-6