查询上传限制
“查询上传限制”章节发送文件前先查询允许的格式、大小、尺寸和时长。能够上传不代表所有工具都可以处理该素材,每个工具还有自己的限制。
/api/v1/assets/upload-limits调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request GET 'https://app.berserkapi.com/api/v1/assets/upload-limits' \ --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/assets/upload-limits', { 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/assets/upload-limits", 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/assets/upload-limits", 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/assets/upload-limits") .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/assets/upload-limits")) .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/assets/upload-limits");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
imageobject必填展开字段 / 类型
maxAspectinteger · int64必填长边 / 短边上限
maxBytesinteger · int64必填文件大小上限(字节)
maxDurationMsinteger · int64必填视频时长上限(毫秒)
maxFrameRateinteger · int64必填视频帧率上限(帧/秒)
maxFramesinteger · int64必填视频帧数上限
maxPixelsinteger · int64必填总像素(宽 × 高)上限
maxSideinteger · int64必填长边上限(像素)
minSideinteger · int64必填短边下限(像素)
videoobject必填展开字段 / 类型
maxAspectinteger · int64必填长边 / 短边上限
maxBytesinteger · int64必填文件大小上限(字节)
maxDurationMsinteger · int64必填视频时长上限(毫秒)
maxFrameRateinteger · int64必填视频帧率上限(帧/秒)
maxFramesinteger · int64必填视频帧数上限
maxPixelsinteger · int64必填总像素(宽 × 高)上限
maxSideinteger · int64必填长边上限(像素)
minSideinteger · int64必填短边下限(像素)
申请上传凭证
“申请上传凭证”章节传入文件名、MIME 类型与准确字节数。请把示例中的 123456 改为实际文件大小。201 响应包含 assetId、uploadUrl、method、headers(名称 / 值二元数组)、fileField 和 expiresAt。将响应保存为本地 ticket.json 供下一步读取;其中包含临时签名地址,请妥善保管。
/api/v1/assets/uploads请求体字段 · application/json
mimeTypestring必填multipartboolean可选用表单(multipart/form-data)POST 上传:给只能发表单的客户端(微信小程序 wx.uploadFile);默认 PUT 原始字节
namestring必填sizeBytesinteger · int64必填
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request POST 'https://app.berserkapi.com/api/v1/assets/uploads' \ --header "Authorization: Bearer $GLACIER_API_KEY" \ --header 'Content-Type: application/json' \ --data-raw '{ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}'// 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/assets/uploads', { method: 'POST', headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}), 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/assets/uploads", method="POST", headers=headers, data="""{ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}""".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/assets/uploads", strings.NewReader(`{ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}`)) 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/assets/uploads") .bearer_auth(env::var("GLACIER_API_KEY")?) .header("Content-Type", "application/json") .body(r#"{ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}"#) .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/assets/uploads")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .header("Content-Type", "application/json") .method("POST", HttpRequest.BodyPublishers.ofString("""{ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}""")) .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/assets/uploads");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);request.Content = new StringContent("""{ "name": "input.png", "mimeType": "image/png", "sizeBytes": 123456}""", 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}");响应字段 · 201
assetIdstring必填expiresAtstring · date-time必填fileFieldstring | null必填method 为 POST(表单上传)时文件所在的字段名;PUT 为 null
headersstring[][]必填展开字段 / 类型
stringmethod"PUT" | "POST"必填uploadUrlstring必填
直传文件字节
“直传文件字节”章节下面使用默认 PUT 凭证,把 input.png 流式上传到返回的 URL,并使用返回的请求头。这是存储请求,不经过 /api/v1,不要附带 API Key。
# Save the upload-ticket response as ticket.json. Requires jq.# The ticket must be for input.png with its exact byte length.test "$(jq -r '.method' ticket.json)" = PUT || exit 1headers=()while IFS= read -r header; do headers+=(--header "$header"); done < <( jq -r '.headers[] | .[0] + ": " + .[1]' ticket.json)curl --fail-with-body --silent --show-error --max-time 1800 \ --request PUT "$(jq -r '.uploadUrl' ticket.json)" \ "${headers[@]}" --upload-file input.png// Node.js 22+; example.mjsimport { readFile, stat } from 'node:fs/promises';import { createReadStream } from 'node:fs';const ticket = JSON.parse(await readFile('ticket.json', 'utf8'));if (ticket.method !== 'PUT') throw new Error('Request a default PUT ticket');const headers = Object.fromEntries(ticket.headers);headers['Content-Length'] = String((await stat('input.png')).size);const response = await fetch(ticket.uploadUrl, { method: 'PUT', headers, body: createReadStream('input.png'), duplex: 'half', signal: AbortSignal.timeout(1_800_000),});if (!response.ok) throw new Error(await response.text());console.log('Uploaded; now confirm asset', ticket.assetId);# Python 3.10+; standard libraryimport jsonimport osimport urllib.requestwith open('ticket.json', encoding='utf-8') as source: ticket = json.load(source)if ticket['method'] != 'PUT': raise ValueError('Request a default PUT ticket')headers = dict(ticket['headers'])headers['Content-Length'] = str(os.path.getsize('input.png'))with open('input.png', 'rb') as source: request = urllib.request.Request(ticket['uploadUrl'], data=source, headers=headers, method='PUT') with urllib.request.urlopen(request, timeout=1800) as response: print(response.status, 'Confirm asset', ticket['assetId'])// go run upload.gopackage mainimport ( "encoding/json" "fmt" "net/http" "os" "time")func main() { var ticket struct { AssetID string UploadURL string Method string Headers [][2]string } raw, err := os.ReadFile("ticket.json") if err != nil { panic(err) } if err = json.Unmarshal(raw, &ticket); err != nil { panic(err) } if ticket.Method != "PUT" { panic("Request a default PUT ticket") } file, err := os.Open("input.png") if err != nil { panic(err) } defer file.Close() info, err := file.Stat() if err != nil { panic(err) } req, err := http.NewRequest("PUT", ticket.UploadURL, file) if err != nil { panic(err) } req.ContentLength = info.Size() for _, h := range ticket.Headers { req.Header.Set(h[0], h[1]) } res, err := (&http.Client{Timeout: 30 * time.Minute}).Do(req) if err != nil { panic(err) } defer res.Body.Close() if res.StatusCode >= 300 { panic(res.Status) } fmt.Println("Uploaded; confirm asset", ticket.AssetID)}// cargo add reqwest@0.13 --features blocking// cargo add serde_jsonuse std::{error::Error, fs::File, time::Duration};fn main() -> Result<(), Box<dyn Error>> { let ticket: serde_json::Value = serde_json::from_reader(File::open("ticket.json")?)?; if ticket["method"] != "PUT" { return Err("Request a default PUT ticket".into()); } let file = File::open("input.png")?; let client = reqwest::blocking::Client::builder() .timeout(Duration::from_secs(1800)).build()?; let mut request = client.put(ticket["uploadUrl"].as_str().ok_or("Missing uploadUrl")?) .header("Content-Length", file.metadata()?.len()); for header in ticket["headers"].as_array().ok_or("Missing headers")? { request = request.header(header[0].as_str().ok_or("Invalid header")?, header[1].as_str().ok_or("Invalid header")?); } request.body(reqwest::blocking::Body::new(file)).send()?.error_for_status()?; println!("Uploaded; confirm asset {}", ticket["assetId"]); Ok(())}// Java 17+; add com.fasterxml.jackson.core:jackson-databind to your project.import com.fasterxml.jackson.databind.ObjectMapper;import java.nio.file.Path;import java.net.URI;import java.net.http.*;import java.time.Duration;class Upload { public static void main(String[] args) throws Exception { var ticket = new ObjectMapper().readTree(Path.of("ticket.json").toFile()); if (!ticket.get("method").asText().equals("PUT")) throw new IllegalArgumentException("Request a default PUT ticket"); var request = HttpRequest.newBuilder(URI.create(ticket.get("uploadUrl").asText())) .timeout(Duration.ofMinutes(30)); for (var header : ticket.get("headers")) request.header(header.get(0).asText(), header.get(1).asText()); var response = HttpClient.newHttpClient().send(request .PUT(HttpRequest.BodyPublishers.ofFile(Path.of("input.png"))).build(), HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 300) throw new IllegalStateException(response.body()); System.out.println("Uploaded; confirm asset " + ticket.get("assetId").asText()); }}// .NET 8+; Program.csusing System;using System.IO;using System.Net.Http;using System.Text.Json;using var ticket = JsonDocument.Parse(await File.ReadAllTextAsync("ticket.json"));var root = ticket.RootElement;if (root.GetProperty("method").GetString() != "PUT") throw new InvalidOperationException("Request a default PUT ticket");using var client = new HttpClient { Timeout = TimeSpan.FromMinutes(30) };using var file = File.OpenRead("input.png");using var request = new HttpRequestMessage(HttpMethod.Put, root.GetProperty("uploadUrl").GetString());request.Content = new StreamContent(file);foreach (var header in root.GetProperty("headers").EnumerateArray()) { var name = header[0].GetString()!; var value = header[1].GetString(); if (!request.Headers.TryAddWithoutValidation(name, value)) request.Content.Headers.TryAddWithoutValidation(name, value);}using var response = await client.SendAsync(request);response.EnsureSuccessStatusCode();Console.WriteLine("Uploaded; confirm asset " + root.GetProperty("assetId").GetString());如果申请凭证时明确传入 multipart: true,返回的方法会变成 POST。使用 multipart/form-data,把文件放在返回的 fileField 字段,保留返回的请求头,并让 HTTP 库自动设置 multipart boundary。不要把 POST 凭证用于上面的 PUT 示例。
确认上传完成
“确认上传完成”章节存储上传成功后,使用凭证中的 assetId 确认完成,服务会检查真实媒体信息并加入素材库。此接口是幂等的。上传响应丢失时,先用已知素材 ID 尝试确认,再决定是否重新上传。未确认的素材不会出现在列表和详情中,请保留凭证直到确认成功。
/api/v1/assets/{assetId}/complete路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
assetId必填 | path | string素材 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request POST 'https://app.berserkapi.com/api/v1/assets/ASSET_ID/complete' \ --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/assets/ASSET_ID/complete', { 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/assets/ASSET_ID/complete", 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/assets/ASSET_ID/complete", 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/assets/ASSET_ID/complete") .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/assets/ASSET_ID/complete")) .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/assets/ASSET_ID/complete");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
coverUrlstring | null必填视频封面(上传时截取的原件首帧 JPEG,长边不超过 1920);图片或截取失败时为 null
createdAtstring · date-time必填durationMsinteger | null · int64必填视频与动图有值(动图为各帧时长之和)
expiresAtstring | null · date-time必填素材保留期限;null 表示沿用原保留规则
heightinteger · int64必填idstring必填isPublicboolean必填公共素材(示范素材):所有用户可见、不可删除;对它发起处理时自动复制到个人素材库,任务的 assetId 为副本
kind"image" | "video"必填mimeTypestring必填namestring必填sizeBytesinteger · int64必填thumbnailUrlstring | null必填updatedAtstring · date-time必填urlstring必填原件地址(已签名,可直接用于 <img>/<video>)
versionCountinteger · int64必填现存版本数(含原件;删除的版本不计)
widthinteger · int64必填
列表与分页
“列表与分页”章节scope=mine 只查询自己的素材,scope=public 查询示范素材,默认 scope=all。可用 kind、search 筛选。把返回的 nextCursor 作为下一页的 cursor 参数,直到它为 null。示例每页查询 24 条。
/api/v1/assets路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
scope可选 | query | "all" | "mine" | "public"不传 = all:公共素材在前,然后是我的素材 |
kind可选 | query | "image" | "video"不传 = 全部类型 |
search可选 | query | string按名称模糊搜索 maxLength: 100 |
limit可选 | query | integer · int64minimum: 1 · maximum: 100 · default: 24 |
cursor可选 | query | string上一页返回的 nextCursor |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request GET 'https://app.berserkapi.com/api/v1/assets?scope=mine&limit=24' \ --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/assets?scope=mine&limit=24', { 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/assets?scope=mine&limit=24", 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/assets?scope=mine&limit=24", 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/assets?scope=mine&limit=24") .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/assets?scope=mine&limit=24")) .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/assets?scope=mine&limit=24");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
itemsobject[]必填展开字段 / 类型
coverUrlstring | null必填视频封面(上传时截取的原件首帧 JPEG,长边不超过 1920);图片或截取失败时为 null
createdAtstring · date-time必填durationMsinteger | null · int64必填视频与动图有值(动图为各帧时长之和)
expiresAtstring | null · date-time必填素材保留期限;null 表示沿用原保留规则
heightinteger · int64必填idstring必填isPublicboolean必填公共素材(示范素材):所有用户可见、不可删除;对它发起处理时自动复制到个人素材库,任务的 assetId 为副本
kind"image" | "video"必填mimeTypestring必填namestring必填sizeBytesinteger · int64必填thumbnailUrlstring | null必填updatedAtstring · date-time必填urlstring必填原件地址(已签名,可直接用于 <img>/<video>)
versionCountinteger · int64必填现存版本数(含原件;删除的版本不计)
widthinteger · int64必填
nextCursorstring | null必填totalinteger · int64必填
查询素材与版本
“查询素材与版本”章节响应包含 asset 和 versions。如需以某个处理版本继续加工,保留它的版本 ID,作为后续任务的 sourceVersionId。文件地址是临时链接,需要时重新查询获取。处理公共素材时可能生成个人副本,后续以任务返回的 assetId 为准。
/api/v1/assets/{assetId}路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
assetId必填 | path | string素材 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request GET 'https://app.berserkapi.com/api/v1/assets/ASSET_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/assets/ASSET_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/assets/ASSET_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/assets/ASSET_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/assets/ASSET_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/assets/ASSET_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/assets/ASSET_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
assetobject必填展开字段 / 类型
coverUrlstring | null必填视频封面(上传时截取的原件首帧 JPEG,长边不超过 1920);图片或截取失败时为 null
createdAtstring · date-time必填durationMsinteger | null · int64必填视频与动图有值(动图为各帧时长之和)
expiresAtstring | null · date-time必填素材保留期限;null 表示沿用原保留规则
heightinteger · int64必填idstring必填isPublicboolean必填公共素材(示范素材):所有用户可见、不可删除;对它发起处理时自动复制到个人素材库,任务的 assetId 为副本
kind"image" | "video"必填mimeTypestring必填namestring必填sizeBytesinteger · int64必填thumbnailUrlstring | null必填updatedAtstring · date-time必填urlstring必填原件地址(已签名,可直接用于 <img>/<video>)
versionCountinteger · int64必填现存版本数(含原件;删除的版本不计)
widthinteger · int64必填
versionsobject[]必填展开字段 / 类型
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必填
重命名素材
“重命名素材”章节仅修改传入的字段。名称会去掉首尾空白,可以与其他素材重名。
/api/v1/assets/{assetId}路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
assetId必填 | path | string素材 ID |
请求体字段 · application/json
namestring可选素材名称(首尾空白会去掉);可与其他素材重名
minLength: 1 · maxLength: 255
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request PATCH 'https://app.berserkapi.com/api/v1/assets/ASSET_ID' \ --header "Authorization: Bearer $GLACIER_API_KEY" \ --header 'Content-Type: application/json' \ --data-raw '{ "name": "Enhanced photo"}'// 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/assets/ASSET_ID', { method: 'PATCH', headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "Enhanced photo"}), 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/assets/ASSET_ID", method="PATCH", headers=headers, data="""{ "name": "Enhanced photo"}""".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("PATCH", "https://app.berserkapi.com/api/v1/assets/ASSET_ID", strings.NewReader(`{ "name": "Enhanced photo"}`)) 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::PATCH, "https://app.berserkapi.com/api/v1/assets/ASSET_ID") .bearer_auth(env::var("GLACIER_API_KEY")?) .header("Content-Type", "application/json") .body(r#"{ "name": "Enhanced photo"}"#) .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/assets/ASSET_ID")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .header("Content-Type", "application/json") .method("PATCH", HttpRequest.BodyPublishers.ofString("""{ "name": "Enhanced photo"}""")) .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("PATCH"), "https://app.berserkapi.com/api/v1/assets/ASSET_ID");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", key);request.Content = new StringContent("""{ "name": "Enhanced photo"}""", 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
coverUrlstring | null必填视频封面(上传时截取的原件首帧 JPEG,长边不超过 1920);图片或截取失败时为 null
createdAtstring · date-time必填durationMsinteger | null · int64必填视频与动图有值(动图为各帧时长之和)
expiresAtstring | null · date-time必填素材保留期限;null 表示沿用原保留规则
heightinteger · int64必填idstring必填isPublicboolean必填公共素材(示范素材):所有用户可见、不可删除;对它发起处理时自动复制到个人素材库,任务的 assetId 为副本
kind"image" | "video"必填mimeTypestring必填namestring必填sizeBytesinteger · int64必填thumbnailUrlstring | null必填updatedAtstring · date-time必填urlstring必填原件地址(已签名,可直接用于 <img>/<video>)
versionCountinteger · int64必填现存版本数(含原件;删除的版本不计)
widthinteger · int64必填
删除处理版本
“删除处理版本”章节删除某个处理结果版本,不能通过此接口删除原件;删除原件需要删除整个素材。成功返回 204,没有响应正文。
/api/v1/assets/{assetId}/versions/{versionId}路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
assetId必填 | path | string素材 ID |
versionId必填 | path | string版本 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request DELETE 'https://app.berserkapi.com/api/v1/assets/ASSET_ID/versions/VERSION_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/assets/ASSET_ID/versions/VERSION_ID', { method: 'DELETE', 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/assets/ASSET_ID/versions/VERSION_ID", method="DELETE", 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("DELETE", "https://app.berserkapi.com/api/v1/assets/ASSET_ID/versions/VERSION_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::DELETE, "https://app.berserkapi.com/api/v1/assets/ASSET_ID/versions/VERSION_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/assets/ASSET_ID/versions/VERSION_ID")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .method("DELETE", 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("DELETE"), "https://app.berserkapi.com/api/v1/assets/ASSET_ID/versions/VERSION_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}");响应字段 · 204
成功后无响应体。不要解析为空 JSON。
删除素材
“删除素材”章节删除自己的素材并取消相关任务。请在确认不再需要原件及其版本时调用,公共示范素材不可删除。成功返回 204,没有响应正文。
/api/v1/assets/{assetId}路径、查询与请求头
| 参数 | 位置 | 类型与约束 |
|---|---|---|
assetId必填 | path | string素材 ID |
调用示例
curl --include --fail-with-body --silent --show-error --max-time 60 \ --request DELETE 'https://app.berserkapi.com/api/v1/assets/ASSET_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/assets/ASSET_ID', { method: 'DELETE', 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/assets/ASSET_ID", method="DELETE", 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("DELETE", "https://app.berserkapi.com/api/v1/assets/ASSET_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::DELETE, "https://app.berserkapi.com/api/v1/assets/ASSET_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/assets/ASSET_ID")) .timeout(Duration.ofSeconds(60)) .header("Authorization", "Bearer " + key) .method("DELETE", 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("DELETE"), "https://app.berserkapi.com/api/v1/assets/ASSET_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}");响应字段 · 204
成功后无响应体。不要解析为空 JSON。