Product image kit

Introduction

Create a consistent product image set from 1–6 original photos: main image, scene, selling points, details, atmosphere and advertisement. Capability key: image.product_kit. All Anycap API requests use Authorization: Bearer <ANYCAP_API_KEY> without browser Cookie or Origin. Provide an existing valid plan directly, or prepare one with the optional text/vision calls below. Each final image uses the existing image generation service.

Requests

MethodPathResponse
GET/api/v1/capabilities/image.product_kitPermitted vision/image models, exact image specifications and endpoint paths
POST/api/v1/capabilities/image.product_kit/analysis-requestsFree preparation: native_request, execution_path, execution_required=true
POST/v1/chat/completionsSeparately billed analysis; JSON plan in choices[0].message.content
POST/api/v1/capabilities/image.product_kit/quotes201 persisted image quote manifest; 200 on identical replay
POST/api/v1/capabilities/image.product_kit/executions202 original batch and child task states
GET/api/v1/capabilities/image.product_kit/executions/{id}?result=trueBatch and temporary completed image links

Call sequence: discover image_models → select one priced_combinations entry → provide plan → quote → submit the saved batch id → query result=true → save all completed images. The analysis-requests and chat/completions calls are optional when you already have a plan. The /api/v1/product-kit/workflows paths use browser sessions and are not Bearer integration endpoints.

Optional plan preparation

FieldTypeMeaning
modelstring
MeaningChoose analysis_models[].model from discovery
image_urlsstring[1–6]
MeaningUnique public HTTPS originals; no credentials, private/local address, fragment or Data URL
max_tokensinteger
Meaning1024–16000, default 8192, within the discovered model limit
settings.platform / market / language / stylestring
MeaningRequired sales platform, market, text language and art direction
settings.requirementstring
MeaningOptional verified product details; at most 1600 characters
settings.countsobject
Meaninghero / scene / selling / detail / mood / ad; each 0–4, total 1–12. Missing object defaults to one of each; missing keys in an explicit object mean zero
settings.listingboolean
MeaningRequest listing title and description; default false
settings.commercial_style_analysisboolean
MeaningComposition guidance; default false, no live bestseller lookup

Preparation does not invoke a model. Send the returned native_request unchanged to execution_path with a stable Idempotency-Key. Accept a plan only when choices[0].finish_reason is stop and content parses as JSON. Preserve the analysis operation key before that paid call; a timeout is not permission to run analysis again. Check the recorded text call when its outcome is unknown.

Image quote input

FieldTypeMeaning
idempotency_keystring
MeaningRequired stable 1–255 byte set identity, isolated per connection. Same key and changed input returns 409
model / size / resolution / qualitystring
MeaningUse image_models[].model and copy size/resolution/quality verbatim from ONE priced_combinations entry. A ratio and resolution must be priced together; quality may identify a version. Do not hard-code the number of options or normalize casing.
image_urlsstring[1–6]
MeaningSame original photos; effective image model limit also applies
plan.product / summarystring
MeaningRequired product name (≤300 characters) and facts/art direction (≤2000)
plan.selling_points / title / descriptionarray / string
MeaningOptional verified points (≤8), listing title (≤300) and body (≤3000)
plan.imagesarray[1–12]
MeaningEach item requires kind, title (≤120) and standalone prompt (20–6000 characters); at most four of each kind

You may edit the analyzed plan or provide a valid plan directly. Every child uses n=1 and the same original photos. Quote returns id, amount_microcredits, expires_at and items containing index, id, kind, title, quote_id, resource_id, native_request, amount_microcredits and expires_at. The total is the sum of each independently rounded image quote, with analysis billed separately. Save the complete manifest before executing. Quoting makes no reservations. expires_at is the earliest child quote deadline.

Execution and results

Submit {"id":"BATCH_UUID","max_microcredits":"AUTHORIZED_IMAGE_BUDGET"}. The positive int64 budget bounds the quoted image total. The server validates every quote before claiming the batch and submits the persisted child IDs in parallel. Each child has independent billing; one rejected image does not cancel others. Repeating the submission returns the original batch without a new submission wave.

Batch stateMeaning
quotedNo submission wave started
processingAt least one child is still working
completedAll images completed
partialAll children terminal, with both successes and failures
failedAll children failed or were clearly not submitted
pendingAt least one child outcome requires verification; do not automatically create a replacement set

Results include id, image_count, items, charged_microcredits, reserved_microcredits, manifest_expires_at and the unexpired manifest. Each item carries index, id, state and, when available, the original ImageJob. charged_microcredits sums known image charges; reserved_microcredits sums known held/pending reservations. Missing children with submission_unconfirmed are unknown, not proof of zero cost. Use result=true for temporary .items[].job.images links; save images promptly. The original child may also be read at GET /api/v1/image-executions/{id}?result=true. The manifest is available for 72 hours; batch identities and existing child financial records remain afterward.

End-to-end example

BASH
# 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

Regenerate one image

For an explicitly requested new image, create a new quote input using the original image_urls and exact model/size/resolution/quality, keep plan.product and plan.summary, and set plan.images to just the selected saved plan entry. Use a new idempotency_key for that new image, then submit and query its returned batch id. No new text/vision call is needed. Use this for completed/released results, not to replace an unknown pending outcome.

Save the whole set to a folder and ZIP

Save the result=true response as kit-result.json. Run this Python 3 example from that directory; KIT_OUTPUT_DIR optionally selects an output directory. It saves all completed and settled originals together in a new numbered-image folder and creates a ZIP beside it. A partial batch exports its successful images only. Download links are temporary; obtain fresh links by querying the same batch. Directory selection and ZIP creation happen in your client, not through an additional generation endpoint.

PYTHON
# 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()}") 

Errors

StatusAction
400Correct fields, photo URLs, plan or budget
401 / 403Check key, member and source permissions
402Budget below the quoted image total, or insufficient credits; inspect child states after a claimed batch
404Batch/quote not owned by this connection or full manifest expired
409Changed idempotent input or quote binding conflict; retain the original batch and child IDs
429Request or concurrency limit reached; inspect existing child results
503Unavailable model/source or temporary service failure; query any already submitted batch instead of creating replacements