获取任务报价
“获取任务报价”章节使用已经确认上传的素材,以及工具目录中的工具。示例是 image-sr 图片 2 倍超分。以已有版本为输入时传入 sourceVersionId,否则使用原件;允许超限时等比缩小到工具上限,可传 fitToLimits: true。
此请求只需读取权限,不会扣积分。problem 非空表示当前选择不能提交。有效报价返回 quoteId、expiresAt 和整数 costCredits。报价不预留余额和算力。
/api/v1/tasks/quote请求体字段 · application/json
assetIdstring必填fitToLimitsboolean可选素材分辨率超出工具上限时,先等比缩小到上限内再处理(见 TaskQuote.canFit);不传 = 超限直接拒绝
maxCostCreditsinteger · int64可选最高允许扣费积分;超过时返回 409,不扣费。不传报价 id 的客户端可用此字段控制价格变化
minimum: 0
paramsobject | object | object | object | object | object必填展开字段 / 类型
类型 1 ·
objecttype"none"必填
类型 2 ·
objecttype"scale"必填valueinteger · int64必填
类型 3 ·
objecttype"fps"必填valueinteger · int64必填
类型 4 ·
objecttype"faceProtect"必填valueboolean必填
类型 5 ·
objecttype"regions"必填valueobject[]必填minItems: 1
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
类型 6 ·
objecttype"watermark"必填valueobject必填展开字段 / 类型
objectsobject[]可选水印名称(只用于显示,不影响识别)
maxItems: 8
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
regionsobject[]必填全部标记
maxItems: 64
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
revisionIdstring可选复用的识别结果:同一输入版本、同一组标记、成功的 watermark-mask 任务 ID。去水印直接用它的原尺寸遮罩,不再重新识别;标记变了、识别版本过期时返回 409
quoteIdstring可选quoteTask 返回的报价 id;有效期内按原报价成交,一份报价只创建一个任务
rushboolean可选sourceVersionIdstring可选以哪个版本为输入;不传表示原件
toolIdstring必填useImagePackboolean可选
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request POST 'https://app.berserkapi.com/api/v1/tasks/quote' \ --header "Authorization: Bearer $GLACIER_API_KEY" \ --header 'Content-Type: application/json' \ --data-raw '{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}'// Node.js 22+; save as example.mjs, then: node example.mjsconst key = process.env.GLACIER_API_KEY;if (!key) throw new Error('Set GLACIER_API_KEY');const response = await fetch('https://app.berserkapi.com/api/v1/tasks/quote', { method: 'POST', headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}), signal: AbortSignal.timeout(60_000),});const text = await response.text();if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);console.log(response.status, text);# Python 3.10+; standard library; python example.pyimport osimport urllib.requestimport urllib.error
headers = {"Authorization": "Bearer " + os.environ["GLACIER_API_KEY"]}headers["Content-Type"] = "application/json"request = urllib.request.Request( "https://app.berserkapi.com/api/v1/tasks/quote", method="POST", headers=headers, data="""{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}""".encode("utf-8"),)try: with urllib.request.urlopen(request, timeout=60) as response: print(response.status, response.read().decode("utf-8"))except urllib.error.HTTPError as error: raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8')}") from error// Go 1.22+; go run example.gopackage main
import ( "fmt" "io" "net/http" "os" "strings" "time")
func main() { key := os.Getenv("GLACIER_API_KEY") if key == "" { panic("Set GLACIER_API_KEY") } req, err := http.NewRequest("POST", "https://app.berserkapi.com/api/v1/tasks/quote", strings.NewReader(`{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}`)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer " + key) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 60 * time.Second} res, err := client.Do(req) if err != nil { panic(err) } defer res.Body.Close() data, err := io.ReadAll(res.Body) if err != nil { panic(err) } if res.StatusCode >= 300 { panic(fmt.Sprintf("HTTP %d: %s", res.StatusCode, data)) } fmt.Println(res.StatusCode, string(data))}// cargo add reqwest@0.13 --features blocking// Save in src/main.rs, then: cargo runuse std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(60)).build()?; let response = client.request(reqwest::Method::POST, "https://app.berserkapi.com/api/v1/tasks/quote") .bearer_auth(env::var("GLACIER_API_KEY")?) .header("Content-Type", "application/json") .body(r#"{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}"#) .send()?; let status = response.status(); let text = response.text()?; if !status.is_success() { return Err(format!("HTTP {status}: {text}").into()); } println!("{status} {text}"); Ok(())}// Java 17+; save as Example.java, then: java Example.javaimport java.net.URI;import java.net.http.*;import java.time.Duration;import java.util.Objects;
class Example { public static void main(String[] args) throws Exception { var key = Objects.requireNonNull(System.getenv("GLACIER_API_KEY")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); var request = HttpRequest.newBuilder(URI.create("https://app.berserkapi.com/api/v1/tasks/quote")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .header("Content-Type", "application/json") .method("POST", HttpRequest.BodyPublishers.ofString("""{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}""")) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException("HTTP " + response.statusCode() + ": " + response.body()); System.out.println(response.statusCode() + " " + response.body()); }}// .NET 8+; dotnet new console; replace Program.cs; dotnet runusing System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;
var key = Environment.GetEnvironmentVariable("GLACIER_API_KEY") ?? throw new InvalidOperationException("Set GLACIER_API_KEY");using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };using var request = new HttpRequestMessage(new HttpMethod("POST"), "https://app.berserkapi.com/api/v1/tasks/quote");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);request.Content = new StringContent("""{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }}""", Encoding.UTF8, "application/json");using var response = await client.SendAsync(request);var text = await response.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}: {text}");Console.WriteLine($"{(int)response.StatusCode} {text}");响应字段 · 200
balanceCreditsinteger · int64必填当前积分余额,前端据此提示「积分不足 · 去购买」
canFitboolean必填素材只在分辨率上超出工具上限,传 fitToLimits = true 可先缩小再处理
costCreditsinteger · int64必填消耗的积分,提交时按这个数预扣(报价即成交价)
expiresAtstring | null · date-time必填报价有效期;过期后需重新报价
fixedCreditsinteger · int64必填每任务固定费(积分)
imageUnitsinteger · int64必填minCreditsinteger · int64必填最低消耗(积分)
needsTopupboolean必填problem 可通过购买积分解决(没买过积分的账户的视频时长限制)
outputHeightinteger · int64必填outputWidthinteger · int64必填处理后的画面宽度;problem 非 null 时为 0
priceIdstring | null必填本次使用的价目版本 id
problemstring | null必填不能提交的原因(与 createTask 返回 400 时的文案一致);null 表示可以提交
quoteIdstring | null必填有效报价的 id;problem 非空时为 null
resizedboolean必填是否会先缩小输入(fitToLimits = true 且素材超出工具上限)
unitCreditsinteger · int64必填单价(积分 / U;按次计费为每次的积分;分档计价为所在档每张的积分,动图为每帧)
unitDivisorinteger · int64必填单价分母;实际单价为 unitCredits / unitDivisor
unitSizeinteger · int64必填计量单位像素或像素帧数;0 表示媒体默认单位
unitsMilliinteger · int64必填计费量 U × 1000(视频与动图 1 U = 1080p·30fps·1 秒输出画面,图片 1 U = 1600 万输出像素;按次计费为 1000)
usesFreeRushboolean必填
提交处理任务
“提交处理任务”章节把 QUOTE_ID 替换为刚取得的报价 ID,保持素材、输入版本、工具、参数和 fit-to-limits 设置不变。在有效期内按该报价成交;报价过期或参数不匹配返回 409。一份报价最多创建一个任务,也可用 maxCostCredits 限制最高扣费。
为这一次逻辑任务设置唯一的 GLACIER_IDEMPOTENCY_KEY,例如一个 UUID。发送前保存幂等键与完整请求体,重试时同时复用,即使进程重启也不变。 只有主动创建新任务时才换新键。键允许字母、数字、.、_、:、-,最长 128 个字符。
成功返回 202 Accepted,表示已受理。创建任务与预扣积分在同一事务完成。响应包含 id、status、urls.status、urls.result、urls.cancel,以及 Location、Retry-After: 2 响应头。客户端网络超时只会停止本地等待,不会取消服务端任务。
/api/v1/tasks路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
Idempotency-Key必填 | header | string一次逻辑提交一个唯一键;网络重试使用原键与原请求体;不同参数复用返回 409 minLength: 1 · maxLength: 128 · pattern: "^[A-Za-z0-9._:-]+$" |
请求体字段 · application/json
assetIdstring必填fitToLimitsboolean可选素材分辨率超出工具上限时,先等比缩小到上限内再处理(见 TaskQuote.canFit);不传 = 超限直接拒绝
maxCostCreditsinteger · int64可选最高允许扣费积分;超过时返回 409,不扣费。不传报价 id 的客户端可用此字段控制价格变化
minimum: 0
paramsobject | object | object | object | object | object必填展开字段 / 类型
类型 1 ·
objecttype"none"必填
类型 2 ·
objecttype"scale"必填valueinteger · int64必填
类型 3 ·
objecttype"fps"必填valueinteger · int64必填
类型 4 ·
objecttype"faceProtect"必填valueboolean必填
类型 5 ·
objecttype"regions"必填valueobject[]必填minItems: 1
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
类型 6 ·
objecttype"watermark"必填valueobject必填展开字段 / 类型
objectsobject[]可选水印名称(只用于显示,不影响识别)
maxItems: 8
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
regionsobject[]必填全部标记
maxItems: 64
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
revisionIdstring可选复用的识别结果:同一输入版本、同一组标记、成功的 watermark-mask 任务 ID。去水印直接用它的原尺寸遮罩,不再重新识别;标记变了、识别版本过期时返回 409
quoteIdstring可选quoteTask 返回的报价 id;有效期内按原报价成交,一份报价只创建一个任务
rushboolean可选sourceVersionIdstring可选以哪个版本为输入;不传表示原件
toolIdstring必填useImagePackboolean可选
调用示例
# Set GLACIER_IDEMPOTENCY_KEY once per logical task; reuse it AND the body on retries.curl --include --fail-with-body --silent --show-error --max-time 60 \ --request POST 'https://app.berserkapi.com/api/v1/tasks' \ --header "Authorization: Bearer $GLACIER_API_KEY" \ --header "Idempotency-Key: $GLACIER_IDEMPOTENCY_KEY" \ --header 'Content-Type: application/json' \ --data-raw '{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}'// Node.js 22+; save as example.mjs, then: node example.mjsconst key = process.env.GLACIER_API_KEY;if (!key) throw new Error('Set GLACIER_API_KEY');const idempotencyKey = process.env.GLACIER_IDEMPOTENCY_KEY;if (!idempotencyKey) throw new Error('Set a stable GLACIER_IDEMPOTENCY_KEY');const response = await fetch('https://app.berserkapi.com/api/v1/tasks', { method: 'POST', headers: { Authorization: `Bearer ${key}`, 'Idempotency-Key': idempotencyKey, 'Content-Type': 'application/json', }, body: JSON.stringify({ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}), signal: AbortSignal.timeout(60_000),});const text = await response.text();if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);console.log(response.status, text);# Python 3.10+; standard library; python example.pyimport osimport urllib.requestimport urllib.error
headers = {"Authorization": "Bearer " + os.environ["GLACIER_API_KEY"]}headers["Idempotency-Key"] = os.environ["GLACIER_IDEMPOTENCY_KEY"]headers["Content-Type"] = "application/json"request = urllib.request.Request( "https://app.berserkapi.com/api/v1/tasks", method="POST", headers=headers, data="""{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}""".encode("utf-8"),)try: with urllib.request.urlopen(request, timeout=60) as response: print(response.status, response.read().decode("utf-8"))except urllib.error.HTTPError as error: raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8')}") from error// Go 1.22+; go run example.gopackage main
import ( "fmt" "io" "net/http" "os" "strings" "time")
func main() { key := os.Getenv("GLACIER_API_KEY") if key == "" { panic("Set GLACIER_API_KEY") } idem := os.Getenv("GLACIER_IDEMPOTENCY_KEY") if idem == "" { panic("Set a stable GLACIER_IDEMPOTENCY_KEY") } req, err := http.NewRequest("POST", "https://app.berserkapi.com/api/v1/tasks", strings.NewReader(`{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}`)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer " + key) req.Header.Set("Idempotency-Key", idem) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 60 * time.Second} res, err := client.Do(req) if err != nil { panic(err) } defer res.Body.Close() data, err := io.ReadAll(res.Body) if err != nil { panic(err) } if res.StatusCode >= 300 { panic(fmt.Sprintf("HTTP %d: %s", res.StatusCode, data)) } fmt.Println(res.StatusCode, string(data))}// cargo add reqwest@0.13 --features blocking// Save in src/main.rs, then: cargo runuse std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(60)).build()?; let response = client.request(reqwest::Method::POST, "https://app.berserkapi.com/api/v1/tasks") .bearer_auth(env::var("GLACIER_API_KEY")?) .header("Idempotency-Key", env::var("GLACIER_IDEMPOTENCY_KEY")?) .header("Content-Type", "application/json") .body(r#"{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}"#) .send()?; let status = response.status(); let text = response.text()?; if !status.is_success() { return Err(format!("HTTP {status}: {text}").into()); } println!("{status} {text}"); Ok(())}// Java 17+; save as Example.java, then: java Example.javaimport java.net.URI;import java.net.http.*;import java.time.Duration;import java.util.Objects;
class Example { public static void main(String[] args) throws Exception { var key = Objects.requireNonNull(System.getenv("GLACIER_API_KEY")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); var request = HttpRequest.newBuilder(URI.create("https://app.berserkapi.com/api/v1/tasks")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .header("Idempotency-Key", Objects.requireNonNull(System.getenv("GLACIER_IDEMPOTENCY_KEY"))) .header("Content-Type", "application/json") .method("POST", HttpRequest.BodyPublishers.ofString("""{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}""")) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException("HTTP " + response.statusCode() + ": " + response.body()); System.out.println(response.statusCode() + " " + response.body()); }}// .NET 8+; dotnet new console; replace Program.cs; dotnet runusing System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;
var key = Environment.GetEnvironmentVariable("GLACIER_API_KEY") ?? throw new InvalidOperationException("Set GLACIER_API_KEY");using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };using var request = new HttpRequestMessage(new HttpMethod("POST"), "https://app.berserkapi.com/api/v1/tasks");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("GLACIER_IDEMPOTENCY_KEY") ?? throw new InvalidOperationException("Set a stable GLACIER_IDEMPOTENCY_KEY"));request.Content = new StringContent("""{ "assetId": "ASSET_ID", "toolId": "image-sr", "params": { "type": "scale", "value": 2 }, "quoteId": "QUOTE_ID"}""", Encoding.UTF8, "application/json");using var response = await client.SendAsync(request);var text = await response.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}: {text}");Console.WriteLine($"{(int)response.StatusCode} {text}");响应字段 · 202
assetIdstring必填assetNamestring必填costCreditsinteger · int64必填消耗的积分:建任务时按报价预扣,失败、取消时全额退回
createdAtstring · date-time必填enginestring必填任务使用的推理引擎:超分(rrdb / seedvr2 / flashvsr / swiftvr / recavsr / relayvsr / flashvsr_plus_turbo / flashvsr_plus_base)或本机去水印(sttn / propainter),建任务时记下;其他任务为空串
errorstring | null必填finishedAtstring | null · date-time必填idstring必填maskPreviewUrlstring | null必填去水印识别预览(watermark-mask)成功后的识别遮罩视频(H.264 灰度,亮 = 将被修复的像素,与输入版本按时间对齐);其他任务为 null
modelLoadingboolean必填处理节点正在加载模型(冷启动或空闲卸载后重新加载),此时进度不前进;只在 running 时可能为 true
outputVersionDeletedboolean必填产出的版本已被用户删除(文件已删,记录保留用于对账)
outputVersionIdstring | null必填成功后产出的素材版本;其他状态为 null;检测类工具不产出版本
outputVersionIndexinteger | null · int64必填产出的版本号,与 outputVersionId 同时有值;版本删除后不变(对账用)
paramsobject | object | object | object | object | object必填展开字段 / 类型
类型 1 ·
objecttype"none"必填
类型 2 ·
objecttype"scale"必填valueinteger · int64必填
类型 3 ·
objecttype"fps"必填valueinteger · int64必填
类型 4 ·
objecttype"faceProtect"必填valueboolean必填
类型 5 ·
objecttype"regions"必填valueobject[]必填minItems: 1
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
类型 6 ·
objecttype"watermark"必填valueobject必填展开字段 / 类型
objectsobject[]可选水印名称(只用于显示,不影响识别)
maxItems: 8
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
regionsobject[]必填全部标记
maxItems: 64
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
revisionIdstring可选复用的识别结果:同一输入版本、同一组标记、成功的 watermark-mask 任务 ID。去水印直接用它的原尺寸遮罩,不再重新识别;标记变了、识别版本过期时返回 409
processingStartProgressnumber · double必填processingStartedAt 时的进度 0 ~ 1
processingStartedAtstring | null · date-time必填本轮推理开始的时间(认领或模型加载完成时),估算剩余时间的起点;非运行中或加载模型中为 null
progressnumber · double必填0 ~ 1
queuePositioninteger | null · int64必填排队中时在同一队列中的位置(从 1 开始,1 = 下一个被处理);其他状态为 null
queueRunninginteger | null · int64必填排队中时同一队列正在处理的任务数(所有用户,含分段),>0 说明算力已占满在等待;其他状态为 null
reportobject必填检测类工具成功后的报告;其他任务为 null
展开字段 / 类型
degradedboolean必填有检测器失败,结果可信度降低
detectorsobject[]必填各检测器的判定依据
展开字段 / 类型
idstring必填检测器标识,如 vpvp_videomae、qwen25_vit_448p
marginnumber | null · double必填归一化偏离度:> 0 倾向 AI,< 0 倾向真实;检测器失败时为 null
modelVersionstring必填算法版本,便于追溯
probabilitynumber · double必填校准后的 AI 概率,用于判定
minimum: 0 · maximum: 1
scorenumber | null · double必填AI 率展示分 0 ~ 100,越高越像 AI;展示模型不可用时为 null
minimum: 0 · maximum: 100
thresholdnumber · double必填判定阈值:probability ≥ threshold 判为 AI
minimum: 0 · maximum: 1
verdict"ai" | "real"必填判定结论(权威)
sourceVersionIdstring | null必填以哪个版本为输入;null 表示原件
sourceVersionIndexinteger · int64必填输入版本号(0 = 原件),建任务时记下,版本删除后不变
startedAtstring | null · date-time必填开始处理的时间(第一次被 worker 认领)
status"queued" | "running" | "succeeded" | "failed" | "canceled"必填toolIdstring必填toolNamestring必填工具名称(含不在工具列表里的隐藏工具,如水印追踪)
urlsobject必填展开字段 / 类型
cancelstring必填POST 取消任务的同源相对路径
resultstring必填获取结果及刷新签名下载地址的同源相对路径
statusstring必填查询任务状态的同源相对路径
watermarkAnalysisboolean必填有识别分析报告(每个水印每一段的状态、需要复核的区间),用 getWatermarkAnalysis 读取;水印追踪、本机去水印成功后才有
watermarkFindobject必填自动查找水印(watermark-find)成功后的候选水印;其他任务为 null
展开字段 / 类型
candidatesobject[]必填候选水印,按出现时长从长到短
展开字段 / 类型
boxobject必填展开字段 / 类型
heightnumber · double必填minimum: 0 · maximum: 1
widthnumber · double必填minimum: 0 · maximum: 1
xnumber · double必填minimum: 0 · maximum: 1
ynumber · double必填minimum: 0 · maximum: 1
endMsinteger | null · int64必填大致消失的时间;一直到结尾为 null
evidencestring[]必填找到它的依据:stable(时间上稳定的笔画)、text(文字检测)、concept(概念分割)、short_event(只在首尾短暂出现,证据较少)
展开字段 / 类型
stringkeyframesMsinteger · int64[]必填建议标记的时间点(毫秒):第一次稳定出现、中间、最后;位置以 box 为准
展开字段 / 类型
integer · int64namestring | null必填可信的文字识别结果(如账号名);没有为 null,前端显示「水印 n」,不编造名称
objectIdinteger · int64必填建议的水印编号
minimum: 0 · maximum: 7
startMsinteger · int64必填大致出现的时间
subtitleboolean必填更像字幕(画面下方居中、内容在变):默认不选
thumbnailstring | null必填候选所在画面的缩略图(JPEG data URI),给建议清单核对用;没有为 null
maxLength: 65536
modelVersionstring必填
watermarkRevisionboolean必填这次水印追踪的原尺寸遮罩可复用:提交去水印时 params.value.revisionId 传本任务 ID,直接用这份遮罩、不再重新识别
响应头: Location, Retry-After
轮询处理进度
“轮询处理进度”章节使用返回的 id 查询,或将 urls.status 与站点域名拼接;它已经以 /api/v1 开头,不要重复拼接 API 基础路径。遵守 Retry-After,建议间隔为两秒,进入终态后停止轮询。progress 为 0 到 1,排队位置和部分时间字段可能为空。
| 状态 | 下一步 |
|---|---|
queued |
正在排队,继续轮询 |
running |
正在处理,继续轮询 |
succeeded |
获取处理结果 |
failed |
读取任务错误,不会产生可用输出 |
canceled |
已停止,不再请求结果 |
/api/v1/tasks/{taskId}路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
taskId必填 | path | string任务 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request GET 'https://app.berserkapi.com/api/v1/tasks/TASK_ID' \ --header "Authorization: Bearer $GLACIER_API_KEY"// Node.js 22+; save as example.mjs, then: node example.mjsconst key = process.env.GLACIER_API_KEY;if (!key) throw new Error('Set GLACIER_API_KEY');const response = await fetch('https://app.berserkapi.com/api/v1/tasks/TASK_ID', { method: 'GET', headers: { Authorization: `Bearer ${key}`, }, signal: AbortSignal.timeout(60_000),});const text = await response.text();if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);console.log(response.status, text);# Python 3.10+; standard library; python example.pyimport osimport urllib.requestimport urllib.error
headers = {"Authorization": "Bearer " + os.environ["GLACIER_API_KEY"]}request = urllib.request.Request( "https://app.berserkapi.com/api/v1/tasks/TASK_ID", method="GET", headers=headers,)try: with urllib.request.urlopen(request, timeout=60) as response: print(response.status, response.read().decode("utf-8"))except urllib.error.HTTPError as error: raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8')}") from error// Go 1.22+; go run example.gopackage main
import ( "fmt" "io" "net/http" "os" "time")
func main() { key := os.Getenv("GLACIER_API_KEY") if key == "" { panic("Set GLACIER_API_KEY") } req, err := http.NewRequest("GET", "https://app.berserkapi.com/api/v1/tasks/TASK_ID", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer " + key) client := &http.Client{Timeout: 60 * time.Second} res, err := client.Do(req) if err != nil { panic(err) } defer res.Body.Close() data, err := io.ReadAll(res.Body) if err != nil { panic(err) } if res.StatusCode >= 300 { panic(fmt.Sprintf("HTTP %d: %s", res.StatusCode, data)) } fmt.Println(res.StatusCode, string(data))}// cargo add reqwest@0.13 --features blocking// Save in src/main.rs, then: cargo runuse std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(60)).build()?; let response = client.request(reqwest::Method::GET, "https://app.berserkapi.com/api/v1/tasks/TASK_ID") .bearer_auth(env::var("GLACIER_API_KEY")?) .send()?; let status = response.status(); let text = response.text()?; if !status.is_success() { return Err(format!("HTTP {status}: {text}").into()); } println!("{status} {text}"); Ok(())}// Java 17+; save as Example.java, then: java Example.javaimport java.net.URI;import java.net.http.*;import java.time.Duration;import java.util.Objects;
class Example { public static void main(String[] args) throws Exception { var key = Objects.requireNonNull(System.getenv("GLACIER_API_KEY")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); var request = HttpRequest.newBuilder(URI.create("https://app.berserkapi.com/api/v1/tasks/TASK_ID")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .method("GET", HttpRequest.BodyPublishers.noBody()) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException("HTTP " + response.statusCode() + ": " + response.body()); System.out.println(response.statusCode() + " " + response.body()); }}// .NET 8+; dotnet new console; replace Program.cs; dotnet runusing System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;
var key = Environment.GetEnvironmentVariable("GLACIER_API_KEY") ?? throw new InvalidOperationException("Set GLACIER_API_KEY");using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://app.berserkapi.com/api/v1/tasks/TASK_ID");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);using var response = await client.SendAsync(request);var text = await response.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}: {text}");Console.WriteLine($"{(int)response.StatusCode} {text}");响应字段 · 200
assetIdstring必填assetNamestring必填costCreditsinteger · int64必填消耗的积分:建任务时按报价预扣,失败、取消时全额退回
createdAtstring · date-time必填enginestring必填任务使用的推理引擎:超分(rrdb / seedvr2 / flashvsr / swiftvr / recavsr / relayvsr / flashvsr_plus_turbo / flashvsr_plus_base)或本机去水印(sttn / propainter),建任务时记下;其他任务为空串
errorstring | null必填finishedAtstring | null · date-time必填idstring必填maskPreviewUrlstring | null必填去水印识别预览(watermark-mask)成功后的识别遮罩视频(H.264 灰度,亮 = 将被修复的像素,与输入版本按时间对齐);其他任务为 null
modelLoadingboolean必填处理节点正在加载模型(冷启动或空闲卸载后重新加载),此时进度不前进;只在 running 时可能为 true
outputVersionDeletedboolean必填产出的版本已被用户删除(文件已删,记录保留用于对账)
outputVersionIdstring | null必填成功后产出的素材版本;其他状态为 null;检测类工具不产出版本
outputVersionIndexinteger | null · int64必填产出的版本号,与 outputVersionId 同时有值;版本删除后不变(对账用)
paramsobject | object | object | object | object | object必填展开字段 / 类型
类型 1 ·
objecttype"none"必填
类型 2 ·
objecttype"scale"必填valueinteger · int64必填
类型 3 ·
objecttype"fps"必填valueinteger · int64必填
类型 4 ·
objecttype"faceProtect"必填valueboolean必填
类型 5 ·
objecttype"regions"必填valueobject[]必填minItems: 1
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
类型 6 ·
objecttype"watermark"必填valueobject必填展开字段 / 类型
objectsobject[]可选水印名称(只用于显示,不影响识别)
maxItems: 8
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
regionsobject[]必填全部标记
maxItems: 64
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
revisionIdstring可选复用的识别结果:同一输入版本、同一组标记、成功的 watermark-mask 任务 ID。去水印直接用它的原尺寸遮罩,不再重新识别;标记变了、识别版本过期时返回 409
processingStartProgressnumber · double必填processingStartedAt 时的进度 0 ~ 1
processingStartedAtstring | null · date-time必填本轮推理开始的时间(认领或模型加载完成时),估算剩余时间的起点;非运行中或加载模型中为 null
progressnumber · double必填0 ~ 1
queuePositioninteger | null · int64必填排队中时在同一队列中的位置(从 1 开始,1 = 下一个被处理);其他状态为 null
queueRunninginteger | null · int64必填排队中时同一队列正在处理的任务数(所有用户,含分段),>0 说明算力已占满在等待;其他状态为 null
reportobject必填检测类工具成功后的报告;其他任务为 null
展开字段 / 类型
degradedboolean必填有检测器失败,结果可信度降低
detectorsobject[]必填各检测器的判定依据
展开字段 / 类型
idstring必填检测器标识,如 vpvp_videomae、qwen25_vit_448p
marginnumber | null · double必填归一化偏离度:> 0 倾向 AI,< 0 倾向真实;检测器失败时为 null
modelVersionstring必填算法版本,便于追溯
probabilitynumber · double必填校准后的 AI 概率,用于判定
minimum: 0 · maximum: 1
scorenumber | null · double必填AI 率展示分 0 ~ 100,越高越像 AI;展示模型不可用时为 null
minimum: 0 · maximum: 100
thresholdnumber · double必填判定阈值:probability ≥ threshold 判为 AI
minimum: 0 · maximum: 1
verdict"ai" | "real"必填判定结论(权威)
sourceVersionIdstring | null必填以哪个版本为输入;null 表示原件
sourceVersionIndexinteger · int64必填输入版本号(0 = 原件),建任务时记下,版本删除后不变
startedAtstring | null · date-time必填开始处理的时间(第一次被 worker 认领)
status"queued" | "running" | "succeeded" | "failed" | "canceled"必填toolIdstring必填toolNamestring必填工具名称(含不在工具列表里的隐藏工具,如水印追踪)
urlsobject必填展开字段 / 类型
cancelstring必填POST 取消任务的同源相对路径
resultstring必填获取结果及刷新签名下载地址的同源相对路径
statusstring必填查询任务状态的同源相对路径
watermarkAnalysisboolean必填有识别分析报告(每个水印每一段的状态、需要复核的区间),用 getWatermarkAnalysis 读取;水印追踪、本机去水印成功后才有
watermarkFindobject必填自动查找水印(watermark-find)成功后的候选水印;其他任务为 null
展开字段 / 类型
candidatesobject[]必填候选水印,按出现时长从长到短
展开字段 / 类型
boxobject必填展开字段 / 类型
heightnumber · double必填minimum: 0 · maximum: 1
widthnumber · double必填minimum: 0 · maximum: 1
xnumber · double必填minimum: 0 · maximum: 1
ynumber · double必填minimum: 0 · maximum: 1
endMsinteger | null · int64必填大致消失的时间;一直到结尾为 null
evidencestring[]必填找到它的依据:stable(时间上稳定的笔画)、text(文字检测)、concept(概念分割)、short_event(只在首尾短暂出现,证据较少)
展开字段 / 类型
stringkeyframesMsinteger · int64[]必填建议标记的时间点(毫秒):第一次稳定出现、中间、最后;位置以 box 为准
展开字段 / 类型
integer · int64namestring | null必填可信的文字识别结果(如账号名);没有为 null,前端显示「水印 n」,不编造名称
objectIdinteger · int64必填建议的水印编号
minimum: 0 · maximum: 7
startMsinteger · int64必填大致出现的时间
subtitleboolean必填更像字幕(画面下方居中、内容在变):默认不选
thumbnailstring | null必填候选所在画面的缩略图(JPEG data URI),给建议清单核对用;没有为 null
maxLength: 65536
modelVersionstring必填
watermarkRevisionboolean必填这次水印追踪的原尺寸遮罩可复用:提交去水印时 params.value.revisionId 传本任务 ID,直接用这份遮罩、不再重新识别
响应头: Location, Retry-After
获取与下载结果
“获取与下载结果”章节任务成功后调用。文件任务返回 output 版本,其中的 downloadUrl 是签名下载地址,不要携带 API Key。检测和水印辅助任务可能返回 report、watermarkFind 或 maskPreviewUrl,同时 output 为 null,读取前应先判断。watermarkAnalysisUrl 是 API 路径,需要携带 API Key 请求。
urlExpiresAt 表示本次签名链接的有效期。过期后重新请求结果接口即可刷新,无需再次处理或扣费。尚未成功返回 409,产物已删除返回 410。
/api/v1/tasks/{taskId}/result路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
taskId必填 | path | string任务 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request GET 'https://app.berserkapi.com/api/v1/tasks/TASK_ID/result' \ --header "Authorization: Bearer $GLACIER_API_KEY"// Node.js 22+; save as example.mjs, then: node example.mjsconst key = process.env.GLACIER_API_KEY;if (!key) throw new Error('Set GLACIER_API_KEY');const response = await fetch('https://app.berserkapi.com/api/v1/tasks/TASK_ID/result', { method: 'GET', headers: { Authorization: `Bearer ${key}`, }, signal: AbortSignal.timeout(60_000),});const text = await response.text();if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);console.log(response.status, text);# Python 3.10+; standard library; python example.pyimport osimport urllib.requestimport urllib.error
headers = {"Authorization": "Bearer " + os.environ["GLACIER_API_KEY"]}request = urllib.request.Request( "https://app.berserkapi.com/api/v1/tasks/TASK_ID/result", method="GET", headers=headers,)try: with urllib.request.urlopen(request, timeout=60) as response: print(response.status, response.read().decode("utf-8"))except urllib.error.HTTPError as error: raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8')}") from error// Go 1.22+; go run example.gopackage main
import ( "fmt" "io" "net/http" "os" "time")
func main() { key := os.Getenv("GLACIER_API_KEY") if key == "" { panic("Set GLACIER_API_KEY") } req, err := http.NewRequest("GET", "https://app.berserkapi.com/api/v1/tasks/TASK_ID/result", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer " + key) client := &http.Client{Timeout: 60 * time.Second} res, err := client.Do(req) if err != nil { panic(err) } defer res.Body.Close() data, err := io.ReadAll(res.Body) if err != nil { panic(err) } if res.StatusCode >= 300 { panic(fmt.Sprintf("HTTP %d: %s", res.StatusCode, data)) } fmt.Println(res.StatusCode, string(data))}// cargo add reqwest@0.13 --features blocking// Save in src/main.rs, then: cargo runuse std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(60)).build()?; let response = client.request(reqwest::Method::GET, "https://app.berserkapi.com/api/v1/tasks/TASK_ID/result") .bearer_auth(env::var("GLACIER_API_KEY")?) .send()?; let status = response.status(); let text = response.text()?; if !status.is_success() { return Err(format!("HTTP {status}: {text}").into()); } println!("{status} {text}"); Ok(())}// Java 17+; save as Example.java, then: java Example.javaimport java.net.URI;import java.net.http.*;import java.time.Duration;import java.util.Objects;
class Example { public static void main(String[] args) throws Exception { var key = Objects.requireNonNull(System.getenv("GLACIER_API_KEY")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); var request = HttpRequest.newBuilder(URI.create("https://app.berserkapi.com/api/v1/tasks/TASK_ID/result")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .method("GET", HttpRequest.BodyPublishers.noBody()) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException("HTTP " + response.statusCode() + ": " + response.body()); System.out.println(response.statusCode() + " " + response.body()); }}// .NET 8+; dotnet new console; replace Program.cs; dotnet runusing System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;
var key = Environment.GetEnvironmentVariable("GLACIER_API_KEY") ?? throw new InvalidOperationException("Set GLACIER_API_KEY");using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://app.berserkapi.com/api/v1/tasks/TASK_ID/result");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);using var response = await client.SendAsync(request);var text = await response.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}: {text}");Console.WriteLine($"{(int)response.StatusCode} {text}");响应字段 · 200
maskPreviewUrlstring | null必填outputobject必填文件处理任务的输出版本;报告与遮罩预览类任务为空
展开字段 / 类型
assetIdstring必填createdAtstring · date-time必填downloadUrlstring必填下载地址(响应带 Content-Disposition: attachment,文件名形如 原名_V2_超分.mp4)
durationMsinteger | null · int64必填视频与动图有值(动图为各帧时长之和)
frameCountinteger | null · int64必填帧数,视频与动图有值(图片的 frameCount > 1 即动图,按帧计费);旧版本为 null
frameRatenumber | null · double必填平均帧率(帧/秒),视频与动图有值(动图各帧时长不同,只用于展示);旧版本为 null
heightinteger · int64必填idstring必填indexinteger · int64必填版本号,0 = 原件;只增不减,删除过的版本号不复用(版本列表可能不连续)
labelstring必填maskUrlstring | null必填去水印的识别遮罩视频(H.264,与输入版本 sourceVersionId 按时间对齐,亮 = 被修复的像素,分辨率可能低于原视频),前端对比时叠加在原视频上;其他版本为 null
mimeTypestring必填previewPendingboolean必填预览正在后台转码,完成后 previewUrl 才有值;期间可轮询 getAsset
previewUrlstring | null必填浏览器可播放的 H.264 预览;为 null 时直接播放 url(原视频浏览器可播放、图片、转码失败)
sizeBytesinteger · int64必填sourceVersionIdstring | null必填由哪个版本处理而来(前端默认与它对比);原件为 null;该版本可能已被删除
thumbnailUrlstring | null必填toolIdstring | null必填urlstring必填原始文件地址(已签名)
watermarkMarksobject[]必填用户在这个版本上的去水印标记(随版本保存,下次打开继续编辑);没有为空数组
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
watermarkObjectsobject[]必填用户给这个版本上各个水印起的名字;没有为空数组(前端显示「水印 n」)
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
widthinteger · int64必填
reportobject必填展开字段 / 类型
degradedboolean必填有检测器失败,结果可信度降低
detectorsobject[]必填各检测器的判定依据
展开字段 / 类型
idstring必填检测器标识,如 vpvp_videomae、qwen25_vit_448p
marginnumber | null · double必填归一化偏离度:> 0 倾向 AI,< 0 倾向真实;检测器失败时为 null
modelVersionstring必填算法版本,便于追溯
probabilitynumber · double必填校准后的 AI 概率,用于判定
minimum: 0 · maximum: 1
scorenumber | null · double必填AI 率展示分 0 ~ 100,越高越像 AI;展示模型不可用时为 null
minimum: 0 · maximum: 100
thresholdnumber · double必填判定阈值:probability ≥ threshold 判为 AI
minimum: 0 · maximum: 1
verdict"ai" | "real"必填判定结论(权威)
status"queued" | "running" | "succeeded" | "failed" | "canceled"必填taskIdstring必填urlExpiresAtstring | null · date-time必填本次签名文件链接的有效期;到期重新 GET result,勿重新提交任务
watermarkAnalysisUrlstring | null必填使用 API Key 请求的分析报告地址
watermarkFindobject必填展开字段 / 类型
candidatesobject[]必填候选水印,按出现时长从长到短
展开字段 / 类型
boxobject必填展开字段 / 类型
heightnumber · double必填minimum: 0 · maximum: 1
widthnumber · double必填minimum: 0 · maximum: 1
xnumber · double必填minimum: 0 · maximum: 1
ynumber · double必填minimum: 0 · maximum: 1
endMsinteger | null · int64必填大致消失的时间;一直到结尾为 null
evidencestring[]必填找到它的依据:stable(时间上稳定的笔画)、text(文字检测)、concept(概念分割)、short_event(只在首尾短暂出现,证据较少)
展开字段 / 类型
stringkeyframesMsinteger · int64[]必填建议标记的时间点(毫秒):第一次稳定出现、中间、最后;位置以 box 为准
展开字段 / 类型
integer · int64namestring | null必填可信的文字识别结果(如账号名);没有为 null,前端显示「水印 n」,不编造名称
objectIdinteger · int64必填建议的水印编号
minimum: 0 · maximum: 7
startMsinteger · int64必填大致出现的时间
subtitleboolean必填更像字幕(画面下方居中、内容在变):默认不选
thumbnailstring | null必填候选所在画面的缩略图(JPEG data URI),给建议清单核对用;没有为 null
maxLength: 65536
modelVersionstring必填
查询任务记录
“查询任务记录”章节可用 assetId、toolId、status 筛选。limit 默认 50,最多 200,此接口没有游标分页。请保存任务 ID,以便单独查询自己的任务。
/api/v1/tasks路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
assetId可选 | query | string只看某个素材的任务 |
toolId可选 | query | string只看某个工具的任务(如 detect 取检测报告) |
status可选 | query | "queued" | "running" | "succeeded" | "failed" | "canceled" |
limit可选 | query | integer · int64minimum: 1 · maximum: 200 · default: 50 |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request GET 'https://app.berserkapi.com/api/v1/tasks?limit=20' \ --header "Authorization: Bearer $GLACIER_API_KEY"// Node.js 22+; save as example.mjs, then: node example.mjsconst key = process.env.GLACIER_API_KEY;if (!key) throw new Error('Set GLACIER_API_KEY');const response = await fetch('https://app.berserkapi.com/api/v1/tasks?limit=20', { method: 'GET', headers: { Authorization: `Bearer ${key}`, }, signal: AbortSignal.timeout(60_000),});const text = await response.text();if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);console.log(response.status, text);# Python 3.10+; standard library; python example.pyimport osimport urllib.requestimport urllib.error
headers = {"Authorization": "Bearer " + os.environ["GLACIER_API_KEY"]}request = urllib.request.Request( "https://app.berserkapi.com/api/v1/tasks?limit=20", method="GET", headers=headers,)try: with urllib.request.urlopen(request, timeout=60) as response: print(response.status, response.read().decode("utf-8"))except urllib.error.HTTPError as error: raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8')}") from error// Go 1.22+; go run example.gopackage main
import ( "fmt" "io" "net/http" "os" "time")
func main() { key := os.Getenv("GLACIER_API_KEY") if key == "" { panic("Set GLACIER_API_KEY") } req, err := http.NewRequest("GET", "https://app.berserkapi.com/api/v1/tasks?limit=20", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer " + key) client := &http.Client{Timeout: 60 * time.Second} res, err := client.Do(req) if err != nil { panic(err) } defer res.Body.Close() data, err := io.ReadAll(res.Body) if err != nil { panic(err) } if res.StatusCode >= 300 { panic(fmt.Sprintf("HTTP %d: %s", res.StatusCode, data)) } fmt.Println(res.StatusCode, string(data))}// cargo add reqwest@0.13 --features blocking// Save in src/main.rs, then: cargo runuse std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(60)).build()?; let response = client.request(reqwest::Method::GET, "https://app.berserkapi.com/api/v1/tasks?limit=20") .bearer_auth(env::var("GLACIER_API_KEY")?) .send()?; let status = response.status(); let text = response.text()?; if !status.is_success() { return Err(format!("HTTP {status}: {text}").into()); } println!("{status} {text}"); Ok(())}// Java 17+; save as Example.java, then: java Example.javaimport java.net.URI;import java.net.http.*;import java.time.Duration;import java.util.Objects;
class Example { public static void main(String[] args) throws Exception { var key = Objects.requireNonNull(System.getenv("GLACIER_API_KEY")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); var request = HttpRequest.newBuilder(URI.create("https://app.berserkapi.com/api/v1/tasks?limit=20")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .method("GET", HttpRequest.BodyPublishers.noBody()) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException("HTTP " + response.statusCode() + ": " + response.body()); System.out.println(response.statusCode() + " " + response.body()); }}// .NET 8+; dotnet new console; replace Program.cs; dotnet runusing System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;
var key = Environment.GetEnvironmentVariable("GLACIER_API_KEY") ?? throw new InvalidOperationException("Set GLACIER_API_KEY");using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://app.berserkapi.com/api/v1/tasks?limit=20");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);using var response = await client.SendAsync(request);var text = await response.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}: {text}");Console.WriteLine($"{(int)response.StatusCode} {text}");响应字段 · 200
assetIdstring必填assetNamestring必填costCreditsinteger · int64必填消耗的积分:建任务时按报价预扣,失败、取消时全额退回
createdAtstring · date-time必填enginestring必填任务使用的推理引擎:超分(rrdb / seedvr2 / flashvsr / swiftvr / recavsr / relayvsr / flashvsr_plus_turbo / flashvsr_plus_base)或本机去水印(sttn / propainter),建任务时记下;其他任务为空串
errorstring | null必填finishedAtstring | null · date-time必填idstring必填maskPreviewUrlstring | null必填去水印识别预览(watermark-mask)成功后的识别遮罩视频(H.264 灰度,亮 = 将被修复的像素,与输入版本按时间对齐);其他任务为 null
modelLoadingboolean必填处理节点正在加载模型(冷启动或空闲卸载后重新加载),此时进度不前进;只在 running 时可能为 true
outputVersionDeletedboolean必填产出的版本已被用户删除(文件已删,记录保留用于对账)
outputVersionIdstring | null必填成功后产出的素材版本;其他状态为 null;检测类工具不产出版本
outputVersionIndexinteger | null · int64必填产出的版本号,与 outputVersionId 同时有值;版本删除后不变(对账用)
paramsobject | object | object | object | object | object必填展开字段 / 类型
类型 1 ·
objecttype"none"必填
类型 2 ·
objecttype"scale"必填valueinteger · int64必填
类型 3 ·
objecttype"fps"必填valueinteger · int64必填
类型 4 ·
objecttype"faceProtect"必填valueboolean必填
类型 5 ·
objecttype"regions"必填valueobject[]必填minItems: 1
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
类型 6 ·
objecttype"watermark"必填valueobject必填展开字段 / 类型
objectsobject[]可选水印名称(只用于显示,不影响识别)
maxItems: 8
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
regionsobject[]必填全部标记
maxItems: 64
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
revisionIdstring可选复用的识别结果:同一输入版本、同一组标记、成功的 watermark-mask 任务 ID。去水印直接用它的原尺寸遮罩,不再重新识别;标记变了、识别版本过期时返回 409
processingStartProgressnumber · double必填processingStartedAt 时的进度 0 ~ 1
processingStartedAtstring | null · date-time必填本轮推理开始的时间(认领或模型加载完成时),估算剩余时间的起点;非运行中或加载模型中为 null
progressnumber · double必填0 ~ 1
queuePositioninteger | null · int64必填排队中时在同一队列中的位置(从 1 开始,1 = 下一个被处理);其他状态为 null
queueRunninginteger | null · int64必填排队中时同一队列正在处理的任务数(所有用户,含分段),>0 说明算力已占满在等待;其他状态为 null
reportobject必填检测类工具成功后的报告;其他任务为 null
展开字段 / 类型
degradedboolean必填有检测器失败,结果可信度降低
detectorsobject[]必填各检测器的判定依据
展开字段 / 类型
idstring必填检测器标识,如 vpvp_videomae、qwen25_vit_448p
marginnumber | null · double必填归一化偏离度:> 0 倾向 AI,< 0 倾向真实;检测器失败时为 null
modelVersionstring必填算法版本,便于追溯
probabilitynumber · double必填校准后的 AI 概率,用于判定
minimum: 0 · maximum: 1
scorenumber | null · double必填AI 率展示分 0 ~ 100,越高越像 AI;展示模型不可用时为 null
minimum: 0 · maximum: 100
thresholdnumber · double必填判定阈值:probability ≥ threshold 判为 AI
minimum: 0 · maximum: 1
verdict"ai" | "real"必填判定结论(权威)
sourceVersionIdstring | null必填以哪个版本为输入;null 表示原件
sourceVersionIndexinteger · int64必填输入版本号(0 = 原件),建任务时记下,版本删除后不变
startedAtstring | null · date-time必填开始处理的时间(第一次被 worker 认领)
status"queued" | "running" | "succeeded" | "failed" | "canceled"必填toolIdstring必填toolNamestring必填工具名称(含不在工具列表里的隐藏工具,如水印追踪)
watermarkAnalysisboolean必填有识别分析报告(每个水印每一段的状态、需要复核的区间),用 getWatermarkAnalysis 读取;水印追踪、本机去水印成功后才有
watermarkFindobject必填自动查找水印(watermark-find)成功后的候选水印;其他任务为 null
展开字段 / 类型
candidatesobject[]必填候选水印,按出现时长从长到短
展开字段 / 类型
boxobject必填展开字段 / 类型
heightnumber · double必填minimum: 0 · maximum: 1
widthnumber · double必填minimum: 0 · maximum: 1
xnumber · double必填minimum: 0 · maximum: 1
ynumber · double必填minimum: 0 · maximum: 1
endMsinteger | null · int64必填大致消失的时间;一直到结尾为 null
evidencestring[]必填找到它的依据:stable(时间上稳定的笔画)、text(文字检测)、concept(概念分割)、short_event(只在首尾短暂出现,证据较少)
展开字段 / 类型
stringkeyframesMsinteger · int64[]必填建议标记的时间点(毫秒):第一次稳定出现、中间、最后;位置以 box 为准
展开字段 / 类型
integer · int64namestring | null必填可信的文字识别结果(如账号名);没有为 null,前端显示「水印 n」,不编造名称
objectIdinteger · int64必填建议的水印编号
minimum: 0 · maximum: 7
startMsinteger · int64必填大致出现的时间
subtitleboolean必填更像字幕(画面下方居中、内容在变):默认不选
thumbnailstring | null必填候选所在画面的缩略图(JPEG data URI),给建议清单核对用;没有为 null
maxLength: 65536
modelVersionstring必填
watermarkRevisionboolean必填这次水印追踪的原尺寸遮罩可复用:提交去水印时 params.value.revisionId 传本任务 ID,直接用这份遮罩、不再重新识别
取消任务
“取消任务”章节取消操作是幂等的。排队任务立即取消,运行任务需要等待 worker 确认。取消到达前任务可能已经完成,请继续查询实际状态。失败或取消按正常退款规则处理;取消已经成功的任务不会退款。
/api/v1/tasks/{taskId}/cancel路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
taskId必填 | path | string任务 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request POST 'https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel' \ --header "Authorization: Bearer $GLACIER_API_KEY"// Node.js 22+; save as example.mjs, then: node example.mjsconst key = process.env.GLACIER_API_KEY;if (!key) throw new Error('Set GLACIER_API_KEY');const response = await fetch('https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel', { method: 'POST', headers: { Authorization: `Bearer ${key}`, }, signal: AbortSignal.timeout(60_000),});const text = await response.text();if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);console.log(response.status, text);# Python 3.10+; standard library; python example.pyimport osimport urllib.requestimport urllib.error
headers = {"Authorization": "Bearer " + os.environ["GLACIER_API_KEY"]}request = urllib.request.Request( "https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel", method="POST", headers=headers,)try: with urllib.request.urlopen(request, timeout=60) as response: print(response.status, response.read().decode("utf-8"))except urllib.error.HTTPError as error: raise RuntimeError(f"HTTP {error.code}: {error.read().decode('utf-8')}") from error// Go 1.22+; go run example.gopackage main
import ( "fmt" "io" "net/http" "os" "time")
func main() { key := os.Getenv("GLACIER_API_KEY") if key == "" { panic("Set GLACIER_API_KEY") } req, err := http.NewRequest("POST", "https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer " + key) client := &http.Client{Timeout: 60 * time.Second} res, err := client.Do(req) if err != nil { panic(err) } defer res.Body.Close() data, err := io.ReadAll(res.Body) if err != nil { panic(err) } if res.StatusCode >= 300 { panic(fmt.Sprintf("HTTP %d: %s", res.StatusCode, data)) } fmt.Println(res.StatusCode, string(data))}// cargo add reqwest@0.13 --features blocking// Save in src/main.rs, then: cargo runuse std::{env, error::Error, time::Duration};
fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(60)).build()?; let response = client.request(reqwest::Method::POST, "https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel") .bearer_auth(env::var("GLACIER_API_KEY")?) .send()?; let status = response.status(); let text = response.text()?; if !status.is_success() { return Err(format!("HTTP {status}: {text}").into()); } println!("{status} {text}"); Ok(())}// Java 17+; save as Example.java, then: java Example.javaimport java.net.URI;import java.net.http.*;import java.time.Duration;import java.util.Objects;
class Example { public static void main(String[] args) throws Exception { var key = Objects.requireNonNull(System.getenv("GLACIER_API_KEY")); var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); var request = HttpRequest.newBuilder(URI.create("https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .method("POST", HttpRequest.BodyPublishers.noBody()) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException("HTTP " + response.statusCode() + ": " + response.body()); System.out.println(response.statusCode() + " " + response.body()); }}// .NET 8+; dotnet new console; replace Program.cs; dotnet runusing System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;
var key = Environment.GetEnvironmentVariable("GLACIER_API_KEY") ?? throw new InvalidOperationException("Set GLACIER_API_KEY");using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };using var request = new HttpRequestMessage(new HttpMethod("POST"), "https://app.berserkapi.com/api/v1/tasks/TASK_ID/cancel");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);using var response = await client.SendAsync(request);var text = await response.Content.ReadAsStringAsync();if (!response.IsSuccessStatusCode) throw new HttpRequestException($"HTTP {(int)response.StatusCode}: {text}");Console.WriteLine($"{(int)response.StatusCode} {text}");响应字段 · 200
assetIdstring必填assetNamestring必填costCreditsinteger · int64必填消耗的积分:建任务时按报价预扣,失败、取消时全额退回
createdAtstring · date-time必填enginestring必填任务使用的推理引擎:超分(rrdb / seedvr2 / flashvsr / swiftvr / recavsr / relayvsr / flashvsr_plus_turbo / flashvsr_plus_base)或本机去水印(sttn / propainter),建任务时记下;其他任务为空串
errorstring | null必填finishedAtstring | null · date-time必填idstring必填maskPreviewUrlstring | null必填去水印识别预览(watermark-mask)成功后的识别遮罩视频(H.264 灰度,亮 = 将被修复的像素,与输入版本按时间对齐);其他任务为 null
modelLoadingboolean必填处理节点正在加载模型(冷启动或空闲卸载后重新加载),此时进度不前进;只在 running 时可能为 true
outputVersionDeletedboolean必填产出的版本已被用户删除(文件已删,记录保留用于对账)
outputVersionIdstring | null必填成功后产出的素材版本;其他状态为 null;检测类工具不产出版本
outputVersionIndexinteger | null · int64必填产出的版本号,与 outputVersionId 同时有值;版本删除后不变(对账用)
paramsobject | object | object | object | object | object必填展开字段 / 类型
类型 1 ·
objecttype"none"必填
类型 2 ·
objecttype"scale"必填valueinteger · int64必填
类型 3 ·
objecttype"fps"必填valueinteger · int64必填
类型 4 ·
objecttype"faceProtect"必填valueboolean必填
类型 5 ·
objecttype"regions"必填valueobject[]必填minItems: 1
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
类型 6 ·
objecttype"watermark"必填valueobject必填展开字段 / 类型
objectsobject[]可选水印名称(只用于显示,不影响识别)
maxItems: 8
展开字段 / 类型
namestring必填水印名称
minLength: 1 · maxLength: 24
objectIdinteger · int64必填minimum: 0 · maximum: 7
regionsobject[]必填全部标记
maxItems: 64
展开字段 / 类型
colorstring可选纯色水印的颜色(#rrggbb,如白字 #ffffff);不填表示不按颜色收紧遮罩
pattern: "^#[0-9a-fA-F]{6}$"
endMsinteger · int64可选作用区间的结束时间(毫秒,含),须带 timeMs 且不早于它;不填表示只作用于 timeMs 这一帧。positive 带 endMs 表示确认这一段要修复
minimum: 0
heightnumber · double必填高度(占画面高度的比例)
minimum: 0 · maximum: 1
kind"positive" | "negative" | "absent" | "suggested"可选标记类型:positive(默认)这里有水印;negative 减选,这里不是水印;absent 这一段没有这个水印(须带 objectId);suggested 自动查找的建议,用户确认(改成 positive)前不参与识别
objectIdinteger · int64可选水印编号(前端同一种颜色):同一编号在不同时间点的标记是同一个水印,各自向前后跟踪到彼此相遇;不填表示每处标记各是一个水印
minimum: 0 · maximum: 7
strokesobject[]可选涂抹的画笔轨迹;为空表示框选(整个框都是水印)
maxItems: 64
展开字段 / 类型
pointsnumber · double[][]必填轨迹点 [x, y](相对画面)
minItems: 1 · maxItems: 4000
展开字段 / 类型
number · doubleradiusnumber · double必填画笔半径(占画面宽度的比例)
minimum: 0 · maximum: 0.25
timeMsinteger · int64可选标记时的视频时间点(毫秒);不填表示整段视频固定位置
minimum: 0
widthnumber · double必填宽度(占画面宽度的比例)
minimum: 0 · maximum: 1
xnumber · double必填左边缘(占画面宽度的比例)
minimum: 0 · maximum: 1
ynumber · double必填上边缘(占画面高度的比例)
minimum: 0 · maximum: 1
revisionIdstring可选复用的识别结果:同一输入版本、同一组标记、成功的 watermark-mask 任务 ID。去水印直接用它的原尺寸遮罩,不再重新识别;标记变了、识别版本过期时返回 409
processingStartProgressnumber · double必填processingStartedAt 时的进度 0 ~ 1
processingStartedAtstring | null · date-time必填本轮推理开始的时间(认领或模型加载完成时),估算剩余时间的起点;非运行中或加载模型中为 null
progressnumber · double必填0 ~ 1
queuePositioninteger | null · int64必填排队中时在同一队列中的位置(从 1 开始,1 = 下一个被处理);其他状态为 null
queueRunninginteger | null · int64必填排队中时同一队列正在处理的任务数(所有用户,含分段),>0 说明算力已占满在等待;其他状态为 null
reportobject必填检测类工具成功后的报告;其他任务为 null
展开字段 / 类型
degradedboolean必填有检测器失败,结果可信度降低
detectorsobject[]必填各检测器的判定依据
展开字段 / 类型
idstring必填检测器标识,如 vpvp_videomae、qwen25_vit_448p
marginnumber | null · double必填归一化偏离度:> 0 倾向 AI,< 0 倾向真实;检测器失败时为 null
modelVersionstring必填算法版本,便于追溯
probabilitynumber · double必填校准后的 AI 概率,用于判定
minimum: 0 · maximum: 1
scorenumber | null · double必填AI 率展示分 0 ~ 100,越高越像 AI;展示模型不可用时为 null
minimum: 0 · maximum: 100
thresholdnumber · double必填判定阈值:probability ≥ threshold 判为 AI
minimum: 0 · maximum: 1
verdict"ai" | "real"必填判定结论(权威)
sourceVersionIdstring | null必填以哪个版本为输入;null 表示原件
sourceVersionIndexinteger · int64必填输入版本号(0 = 原件),建任务时记下,版本删除后不变
startedAtstring | null · date-time必填开始处理的时间(第一次被 worker 认领)
status"queued" | "running" | "succeeded" | "failed" | "canceled"必填toolIdstring必填toolNamestring必填工具名称(含不在工具列表里的隐藏工具,如水印追踪)
urlsobject必填展开字段 / 类型
cancelstring必填POST 取消任务的同源相对路径
resultstring必填获取结果及刷新签名下载地址的同源相对路径
statusstring必填查询任务状态的同源相对路径
watermarkAnalysisboolean必填有识别分析报告(每个水印每一段的状态、需要复核的区间),用 getWatermarkAnalysis 读取;水印追踪、本机去水印成功后才有
watermarkFindobject必填自动查找水印(watermark-find)成功后的候选水印;其他任务为 null
展开字段 / 类型
candidatesobject[]必填候选水印,按出现时长从长到短
展开字段 / 类型
boxobject必填展开字段 / 类型
heightnumber · double必填minimum: 0 · maximum: 1
widthnumber · double必填minimum: 0 · maximum: 1
xnumber · double必填minimum: 0 · maximum: 1
ynumber · double必填minimum: 0 · maximum: 1
endMsinteger | null · int64必填大致消失的时间;一直到结尾为 null
evidencestring[]必填找到它的依据:stable(时间上稳定的笔画)、text(文字检测)、concept(概念分割)、short_event(只在首尾短暂出现,证据较少)
展开字段 / 类型
stringkeyframesMsinteger · int64[]必填建议标记的时间点(毫秒):第一次稳定出现、中间、最后;位置以 box 为准
展开字段 / 类型
integer · int64namestring | null必填可信的文字识别结果(如账号名);没有为 null,前端显示「水印 n」,不编造名称
objectIdinteger · int64必填建议的水印编号
minimum: 0 · maximum: 7
startMsinteger · int64必填大致出现的时间
subtitleboolean必填更像字幕(画面下方居中、内容在变):默认不选
thumbnailstring | null必填候选所在画面的缩略图(JPEG data URI),给建议清单核对用;没有为 null
maxLength: 65536
modelVersionstring必填
watermarkRevisionboolean必填这次水印追踪的原尺寸遮罩可复用:提交去水印时 params.value.revisionId 传本任务 ID,直接用这份遮罩、不再重新识别
响应头: Location, Retry-After