图片生成

简介

图片生成是异步的:先获取报价,再提交任务,然后轮询任务并读取图片链接。

统一接口(推荐)

新接入请用 POST /v1/generations + type=image。下面的报价/提交接口继续支持,用于兼容旧调用。

BASH
curl -X POST https://api.anycap.io/v1/generations \  -H "Authorization: Bearer $ANYCAP_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "type": "image",    "model": "<来自 GET /v1/generations/models?type=image 的模型>",    "input": { "prompt": "白底商品图" },    "parameters": { "size": "1:1", "n": 1 }  }'
JSON
{  "type": "image",  "model": "gpt-image-2.6-flare",  "input": {    "prompt": "A paper lantern gently swaying, seamless loop"  },  "parameters": {    "output_format": "gif",    "size": "512x512",    "frame_count": 9,    "frame_duration_ms": 200,    "quality": "low",    "n": 1  }}
字段含义
input.prompt
图片指令;max_prompt_bytes 限制 UTF-8 字节数,不是字符数。
Idempotency-Key / idempotency_key
POST /v1/generations 可传最长255字节的稳定不透明键,请求头优先于正文;下方能力专用执行接口使用JSON正文中的幂等键。
parameters.size / resolution / quality
从priced_combinations原样选择精确组合。2.5/2.6 Flare/Sunburst使用小写1k/2k/4k及auto/low/medium/high/xhigh/max;Ext使用大写1K/2K/4K及quality=flare/sunburst。
parameters.n
1至发现结果的n_max;GIF表示动图段数。
parameters.frame_count / frame_duration_ms
仅2.6 GIF:2–16帧,默认9帧;每帧至少20毫秒,默认200毫秒,以10毫秒为渲染单位。size是单帧像素尺寸或auto=512x512,不能传比例。512x512支持全部帧数,768x768最多12帧。支持参考图,不支持蒙版、标注、压缩或透明背景。整张拼图单边不超过3840、总像素不超过8,294,400、长短边比不超过3:1。按能容纳整张拼图的已发布规格预留,最终按实际Token结算;参考图另按每张1536输入Token预估,n按动图段数计费,不逐帧计费。
parameters.output_format
2.5/2.6 Flare/Sunburst支持png/jpeg/webp;2.6另支持gif。Ext不接收输出格式等选项。
input.image_urls
公开HTTP(S)参考图片链接,与reference_file_ids合计最多16张;2.6也支持经校验的PNG/JPEG/GIF Data URL,请求总大小不超过64 KiB。
input.reference_file_ids
通过控制台会话接口POST /api/v1/files上传的自有临时文件UUID;原生POST /files返回的句柄不能填在这里。外部调用可直接使用image_urls提供参考图。
input / parameters
发现接口列出的模型字段(如 size、quality、n),会合并进模型原生请求。
model
来自 GET /v1/generations/models?type=image。
图片任务返回 202 + job;轮询 GET /v1/generations/{id},用 ?result=true 取最终图片链接。图片任务不可取消。失败任务保留 error_code,并可选返回 error_message(脱敏后的失败详情;历史文案可能截断)。

失败图片任务可包含以下字段。error_message 为可选字段,未保存详情时省略;文案已脱敏,可能截断。程序逻辑应判断 error_code,error_message 用于解释原因。

失败任务示例

JSON
{  "state": "failed",  "error_code": "upstream_call_failed",  "error_message": "Your request did not pass content safety review.",  "images": [],  "billing_status": "released",  "charged_microcredits": "0"}
步骤方法与路径成功状态
列出该密钥可调用的模型GET /v1/images/models200
获取报价(不调用生成上游)POST /api/v1/capabilities/image.generate/quotes201
提交异步任务POST /api/v1/capabilities/image.generate/executions202
查询自己的任务GET /api/v1/image-executions/{id}200
完成后获取图片链接GET /api/v1/image-executions/{id}?result=true200
JSON
{  "native_request": {    "model": "<来源准确模型名>",    "prompt": "A red apple on a white background",    "size": "1:1",    "resolution": "1k",    "quality": "medium",    "n": 1  }}
  • 使用目录实际返回的模型 ID。2.6 的 model 为 gpt-image-2.6-flare 或 gpt-image-2.6-sunburst,继续使用相同端点和 native_request 字段。2.5/2.6 的 Flare、Sunburst 模型使用六档 quality(auto/low/medium/high/xhigh/max);Ext 使用 quality=flare/sunburst,version 仅作废弃别名。n 为 1~4。
  • 从 GET /v1/images/models 选定模型,再原样取用该模型某一条 priced_combinations,不要自行拼接未定价组合。报价可省略 resource_id:平台在该精确规格下可用、已授权、已定价的来源中自动选择客户售价最低的一条,固定在报价里,执行沿用。
  • 所有模型的报价、预估与原生请求统一使用 quality 字段;Ext 的取值为 flare/sunburst。
  • GET /v1/images/models 中 billing_mode=tokens 表示按 Token 结算;该模型的 priced_combinations[].credits 仅为图片输出预估,不是最终固定价。免费预估的 image.prompt_bytes 为提示词 UTF-8 字节数,image.reference_images 为外链与上传参考图的合计张数;正式请求仍传原始 prompt、image_urls / reference_file_ids。auto 使用同规格 max 预留,任务按实际用量结算;预估不是扣费上限,实际费用较高时从可用余额补扣,较低时退还差额;用量缺失或余额不足时返回 pending。2.6 支持 png/jpeg/webp/gif。
提交时保留原报价请求,在顶层增加 quote_id 与 idempotency_key(放 JSON 正文,不放请求头);不必再传 resource_id,执行来源由报价固定继承。报价有效期五分钟,以 expires_at 为准。
完整图片生成接入文档 →

Python:发现、报价、提交与恢复查询

PYTHON
# Python 3 standard library; save as anycap_image.py and run on your server.import json, os, time, uuidfrom pathlib import Pathfrom urllib.request import Request, urlopenfrom urllib.error import HTTPError, URLError base = os.environ["ANYCAP_API_BASE"].rstrip("/")key = os.environ["ANYCAP_API_KEY"]state_file = Path(os.environ.get("ANYCAP_IMAGE_STATE_FILE", "anycap-image-job.json")) def call(method, path, body=None):    data = None if body is None else json.dumps(body).encode()    req = Request(base + path, data=data, method=method, headers={        "Authorization": "Bearer " + key, "Content-Type": "application/json"    })    with urlopen(req, timeout=30) as response:        return json.load(response) def poll(job_id):    for _ in range(120):        try:            job = call("GET", "/api/v1/image-executions/" + job_id)        except HTTPError as error:            if error.code == 404:                raise RuntimeError("Task not visible; retain state file and contact support. Do not resubmit.") from None            if error.code not in (429, 500, 502, 503, 504):                raise RuntimeError("Task query rejected: HTTP " + str(error.code)) from None            time.sleep(15)            continue        except (URLError, TimeoutError):            time.sleep(15)            continue        if job["state"] in ("completed", "failed", "pending"):            if job["state"] == "completed":                job = call("GET", "/api/v1/image-executions/" + job_id + "?result=true")            print(json.dumps(job))            return        time.sleep(15)    print(json.dumps({"id": job_id, "message": "Polling deadline; rerun to resume GET only."})) # An existing state file always resumes reads, including after a lost POST response.if state_file.exists():    saved = json.loads(state_file.read_text())    if saved["api_base"] != base:        raise RuntimeError("State belongs to another API origin")    poll(saved["id"])else:    models = call("GET", "/v1/images/models")["items"]    selected = os.environ.get("ANYCAP_IMAGE_MODEL")    if not selected:        print(json.dumps({"models": models}))        raise SystemExit("Set ANYCAP_IMAGE_MODEL and an exact priced combination; see the catalog above.")    model = next((m for m in models if m["model"] == selected), None)    if model is None:        raise RuntimeError("Selected model is unavailable or not authorized")    params = {k: os.environ["ANYCAP_IMAGE_" + k.upper()] for k in ("size", "resolution", "quality")}    axis = "quality"    if not any(c["size"] == params["size"] and c["resolution"] == params["resolution"] and c.get(axis, "") == params["quality"] for c in model["priced_combinations"]):        raise RuntimeError("Select an exact priced combination from the model catalog")    n = int(os.environ.get("ANYCAP_IMAGE_N", "1"))    native = {"model": selected, "prompt": os.environ["ANYCAP_IMAGE_PROMPT"], "size": params["size"], "resolution": params["resolution"], "n": n}    native[axis] = params["quality"]    body = {"native_request": native}    quote = call("POST", "/api/v1/capabilities/image.generate/quotes", body)    print(json.dumps({"quote": quote}))    limit = int(os.environ["ANYCAP_MAX_MICROCREDITS"])    if limit <= 0 or int(quote["amount_microcredits"]) > limit:        raise RuntimeError("Quote exceeds authorized budget")    if os.environ.get("ANYCAP_ALLOW_PAID_GENERATION") != "yes":        raise SystemExit("No generation submitted. Set ANYCAP_ALLOW_PAID_GENERATION=yes only after authorization.")    job_id = str(uuid.uuid4())    submission = dict(body, quote_id=quote["id"], idempotency_key=job_id)    # Exclusive private file creation; flush to disk BEFORE the billable POST.    fd = os.open(state_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)    with os.fdopen(fd, "w") as saved:        json.dump({"api_base": base, "id": job_id, "submission": submission}, saved)        saved.flush()        os.fsync(saved.fileno())    print(json.dumps({"id": job_id, "state_file": str(state_file)}))    try:        call("POST", "/api/v1/capabilities/image.generate/executions", submission)    except (HTTPError, URLError, TimeoutError):        raise SystemExit("Submission not confirmed. Retain state file; rerun for GET only. Do not resubmit.") from None    poll(job_id)
上一篇商品套图
下一篇配音生成