商品套图
简介
上传同一商品的 1–6 张原图,规划并生成主图、场景图、卖点图、细节图、氛围图与广告图。能力标识为 image.product_kit。平台接口使用 Authorization: Bearer <ANYCAP_API_KEY>,不携带浏览器 Cookie 或 Origin。可直接提供已有有效方案,或先通过下方可选的文本/视觉接口准备方案;成图复用现有图片生成服务。
请求接口
| 方法 | 路径 | 返回 |
|---|---|---|
| GET | /api/v1/capabilities/image.product_kit | 有权限的分析/图片模型、精确规格与接口路径 |
| POST | /api/v1/capabilities/image.product_kit/analysis-requests | 免费构造 native_request、execution_path、execution_required=true |
| POST | /v1/chat/completions | 单独计费的商品分析,JSON 方案位于 choices[0].message.content |
| POST | /api/v1/capabilities/image.product_kit/quotes | 201 保存的整套报价;相同请求回放返回 200 |
| POST | /api/v1/capabilities/image.product_kit/executions | 202 原套图与逐张任务状态 |
| GET | /api/v1/capabilities/image.product_kit/executions/{id}?result=true | 套图状态与已完成图片临时链接 |
调用顺序:发现 image_models → 选择 priced_combinations 中的一组规格 → 提供 plan → 报价 → 提交已保存的套图 id → 使用 result=true 查询 → 批量保存成图。已有方案可跳过 analysis-requests 和 chat/completions。/api/v1/product-kit/workflows 使用浏览器会话,不作为 Bearer 接入地址。
可选:准备套图方案
modelstring
image_urlsstring[1–6]
max_tokensinteger
settings.platform / market / language / stylestring
settings.requirementstring
settings.countsobject
settings.listingboolean
settings.commercial_style_analysisboolean
构造请求不会调用模型。将返回的 native_request 原样发往 execution_path,并附稳定的 Idempotency-Key,才会执行单独计费的分析。仅在 choices[0].finish_reason 为 stop,且 content 可解析为 JSON 时使用方案。在付费调用前保存分析操作键;超时不代表未调用,不可据此换键重做,应先核对原文本调用。
套图报价参数
idempotency_keystring
model / size / resolution / qualitystring
image_urlsstring[1–6]
plan.product / summarystring
plan.selling_points / title / descriptionarray / string
plan.imagesarray[1–12]
可编辑分析方案,也可直接提供有效方案。每张固定n=1,均携带同一组商品原图。报价返回id、amount_microcredits、expires_at及items;每项包含index、id、kind、title、quote_id、resource_id、native_request、amount_microcredits和expires_at。整套金额为各张独立取整后的合计,分析费用另计。提交前保存完整报价清单;报价本身不预留积分。expires_at为最早一张报价的到期时间。
提交与结果
提交 {"id":"BATCH_UUID","max_microcredits":"已授权图片总预算"}。预算为正int64微积分,限制本套已报价图片总额。服务端先校验全部报价,再原子领取本套提交权,使用已保存的逐张任务ID并发生成。每张独立计费,某张拒绝不会取消其他任务。重复提交返回原套图,不会再次发起整套提交。
| 套图state | 含义 |
|---|---|
quoted | 尚未开始整套提交 |
processing | 仍有图片处理 |
completed | 全部完成 |
partial | 全部进入终态,部分成功、部分失败 |
failed | 全部失败或明确未提交 |
pending | 至少一张结果待核对,不自动新建替代套图 |
返回id、image_count、items、charged_microcredits、reserved_microcredits、manifest_expires_at及未过期的manifest。每项含index、id、state;任务存在时带原ImageJob。charged_microcredits合计已知图片实扣,reserved_microcredits合计已知held/pending预留。submission_unconfirmed表示缺失任务结果尚不确定,不能把已知合计当作最终零费用。result=true读取.items[].job.images临时链接,请及时保存;也可GET /api/v1/image-executions/{id}?result=true读取原单张任务。完整方案清单保留72小时,到期后保留套图身份与原单张账务记录。
完整调用示例
# Requires bash, curl and jq. Save artifacts privately; the API key stays in the environment.set -euo pipefailumask 077BASE="https://api.anycap.io"KIT="$BASE/api/v1/capabilities/image.product_kit"curl --fail-with-body "$KIT" -H "Authorization: Bearer $ANYCAP_API_KEY" > kit-models.json # Set ORIGINAL_IMAGE_URL to a public HTTPS photo of your own product.# Choose one exact image specification from the returned catalog.# Supply kit-plan.json yourself to skip the optional text/vision call.if [ ! -s kit-plan.json ]; thenANALYSIS_MODEL=$(jq -er '.analysis_models[0].model' kit-models.json)ANALYSIS_TOKENS=$(jq -er '.analysis_models[0].max_output_tokens | if . < 8192 then . else 8192 end' kit-models.json)jq -n --arg model "$ANALYSIS_MODEL" --arg image "$ORIGINAL_IMAGE_URL" --argjson tokens "$ANALYSIS_TOKENS" \ '{model:$model,image_urls:[$image],max_tokens:$tokens,settings:{platform:"Amazon",market:"United States",language:"English",style:"Minimal studio photography",listing:true}}' > analysis-input.jsoncurl --fail-with-body "$KIT/analysis-requests" -H "Authorization: Bearer $ANYCAP_API_KEY" \ -H "Content-Type: application/json" --data-binary @analysis-input.json > analysis-request.jsonjq -e '.native_request' analysis-request.json > analysis-native.json # This next request is a separately billed text/vision call. Run only after authorization.# Set and persist ANALYSIS_OPERATION before calling; never change it to retry an unknown outcome.: "${ANALYSIS_OPERATION:?Set a stable analysis operation key}"curl --fail-with-body "$BASE/v1/chat/completions" -H "Authorization: Bearer $ANYCAP_API_KEY" \ -H "Idempotency-Key: $ANALYSIS_OPERATION" -H "Content-Type: application/json" \ --data-binary @analysis-native.json > analysis-result.jsonjq -e '.choices[0].finish_reason == "stop"' analysis-result.json >/dev/null || exit 1jq -er '.choices[0].message.content' analysis-result.json | jq -e . > kit-plan.jsonfi # Review/edit kit-plan.json. Use a new operation key for each NEW set, never for a lost response.: "${KIT_OPERATION:?Set a stable kit operation key}"jq -n --arg operation "$KIT_OPERATION" --arg image "$ORIGINAL_IMAGE_URL" \ --slurpfile catalog kit-models.json --slurpfile plan kit-plan.json \ '{idempotency_key:$operation,model:$catalog[0].image_models[0].model,image_urls:[$image],plan:$plan[0]} + ($catalog[0].image_models[0].priced_combinations[0] | {size,resolution,quality})' > kit-input.jsoncurl --fail-with-body "$KIT/quotes" -H "Authorization: Bearer $ANYCAP_API_KEY" \ -H "Content-Type: application/json" --data-binary @kit-input.json > kit-quote.json# The complete batch id, child ids and prices are now saved BEFORE the paid POST.jq '{id,amount_microcredits,expires_at,items:[.items[]|{id,kind,amount_microcredits}]}' kit-quote.json # Explicit image budget in integer microcredits; 1 credit = 1000000 microcredits.: "${ANYCAP_MAX_MICROCREDITS:?Set the authorized total image budget}"jq --arg budget "$ANYCAP_MAX_MICROCREDITS" '{id,max_microcredits:$budget}' kit-quote.json > kit-submit.jsoncurl --fail-with-body "$KIT/executions" -H "Authorization: Bearer $ANYCAP_API_KEY" \ -H "Content-Type: application/json" --data-binary @kit-submit.json > kit-execution.json # Also use this GET if the submission response was lost. Poll at 15-second intervals.BATCH_ID=$(jq -er '.id' kit-quote.json)curl --fail-with-body "$KIT/executions/$BATCH_ID?result=true" \ -H "Authorization: Bearer $ANYCAP_API_KEY" > kit-result.jsonjq '.items[]|{id,state,images:(.job.images // []),error_code:(.error_code // .job.error_code)}' kit-result.json单张重新生成
用户明确要求重做时,新建报价请求:保留原image_urls及model/size/resolution/quality,保留plan.product和plan.summary,将plan.images设为已保存方案中选定的那一项。为这张新图使用新的idempotency_key,再提交、查询返回的新套图id;不必再次调用文本/视觉接口。适用于已完成或已释放的任务,不能用来替代结果未知的pending任务。
整套保存到目录与ZIP
把result=true响应保存为kit-result.json,在同一目录运行以下Python 3示例;可用KIT_OUTPUT_DIR指定保存目录。程序一次保存全部已完成且已结算的原图,按序号写入新文件夹,并在旁边生成ZIP;partial套图仅导出成功图片。链接临时有效,失效时查询原套图获取新链接。选择目录和打包由接入客户端完成,不需要新增生成请求。
# Python 3 standard library. Input: the saved GET .../executions/{id}?result=true response.import json, os, uuid, zipfilefrom pathlib import Pathfrom urllib.parse import urlsplitfrom urllib.request import Request, urlopen result = json.loads(Path("kit-result.json").read_text(encoding="utf-8"))if result.get("state") not in ("completed", "partial"): raise SystemExit("Query the original batch until completed or partial before saving.")files, total = [], 0extensions = {"image/png": "png", "image/jpeg": "jpg", "image/webp": "webp", "image/gif": "gif"}for index, item in enumerate(result["items"]): job = item.get("job", {}) if job.get("state") != "completed" or job.get("billing_status") != "settled": continue for image_index, url in enumerate(job.get("images", [])): parsed = urlsplit(url) if parsed.scheme != "https" or not parsed.hostname or parsed.username or parsed.password: raise SystemExit("Invalid image link. Query result=true again.") # Image downloads do not receive your Anycap key or browser cookies. with urlopen(Request(url), timeout=60) as response: if urlsplit(response.geturl()).scheme != "https": raise SystemExit("Unexpected image redirect.") extension = extensions.get(response.headers.get("Content-Type", "").split(";")[0]) if not extension: raise SystemExit("Image unavailable. Query result=true again.") data = response.read(20 * 1024 * 1024 + 1) total += len(data) if not data or len(data) > 20 * 1024 * 1024 or total > 120 * 1024 * 1024: raise SystemExit("Image set exceeds download limits.") files.append((f"{item.get('index', index) + 1:02d}_{image_index + 1:02d}.{extension}", data))if not files: raise SystemExit("No completed images available. Query result=true again.")output = Path(os.environ.get("KIT_OUTPUT_DIR", "."))output.mkdir(parents=True, exist_ok=True)folder = output / ("anycap-product-kit-" + uuid.uuid4().hex[:12])folder.mkdir() # A new folder prevents overwriting existing images.archive = folder.with_suffix(".zip")with zipfile.ZipFile(archive, "x", compression=zipfile.ZIP_STORED) as bundle: for name, data in files: (folder / name).write_bytes(data) bundle.writestr(folder.name + "/" + name, data)print(f"Saved {len(files)} images: {folder.resolve()}")print(f"ZIP: {archive.resolve()}") 错误
| 状态码 | 处理 |
|---|---|
| 400 | 修正字段、商品图URL、方案或预算 |
| 401 / 403 | 检查密钥、成员与来源权限 |
| 402 | 预算低于图片报价总额或余额不足;整套已领取提交权时检查逐张状态 |
| 404 | 套图/报价不属于当前连接,或完整清单已过期 |
| 409 | 同键修改输入或报价绑定不一致,保留原套图与任务ID |
| 429 | 请求或并发限制,检查已有逐张结果 |
| 503 | 模型/来源不可用或服务暂时异常;已提交时查询原套图,不自动重建 |