图片生成
简介
图片生成是异步的:先获取报价,再提交任务,然后轮询任务并读取图片链接。
统一接口(推荐)
新接入请用 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。失败图片任务可包含以下字段。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/models | 200 |
| 获取报价(不调用生成上游) | POST /api/v1/capabilities/image.generate/quotes | 201 |
| 提交异步任务 | POST /api/v1/capabilities/image.generate/executions | 202 |
| 查询自己的任务 | GET /api/v1/image-executions/{id} | 200 |
| 完成后获取图片链接 | GET /api/v1/image-executions/{id}?result=true | 200 |
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。
完整图片生成接入文档 →
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)