Image Generation

Generate and edit images with the OpenAI Images API format. Generation is synchronous: one request returns base64 images directly in the JSON response, no polling. Both endpoints use the same API key as the chat endpoints.

Base URL: https://api.techopenclaw.com — authentication uses the same Authorization: Bearer toc-..... header as the chat endpoints (details in API Reference).

POST/v1/images/generationsOpenAI

Text-to-image — one synchronous request returns the images inline.

FieldTypeRequiredDescription
modelstringYesImage model name from the LLM Catalog — gpt-image-2 or gpt-image-2.5-flare.
promptstringYesWhat to draw.
nintegerNoNumber of images requested, clamped to 1–10 (default 1). The value is not forwarded to the model — the response contains the images the model produced (see the note below the cards).
sizestringNoWxH (e.g. 1024x1024). OpenAI-image models receive it as-is; Gemini-image models approximate it to the nearest supported ratio (1:1, 16:9, 9:16, 4:3, 3:4) and ignore unparseable values. An unsupported size on an OpenAI-image model surfaces as a generic 529, not a size-specific error.
qualitystringNoForwarded on OpenAI-image models; ignored (no effect) on Gemini-image models.
image / imagesstring / arrayNoIgnored on this endpoint — input images take effect only on /v1/images/edits.
output_formatstringNopng (the default), jpeg, or webp — any other value falls back to png.
response_formatstringNob64_json (the default) or url. url returns a data: URI built from the returned image — images are not hosted as files, so store the data yourself.
Request — curl
curl https://api.techopenclaw.com/v1/images/generations \
  -H "Authorization: Bearer toc-....." \
  -H "Content-Type: application/json" \
  -d '{
  "model": "gpt-image-2",
  "prompt": "A vivid neon-lit alley in Tokyo after rain, reflections on wet asphalt",
  "size": "1024x1024"
}'
Response
{
  "created": 1712345678,
  "data": [
    {
      "b64_json": "<base64…>",
      "revised_prompt": null
    }
  ]
}

Works with the OpenAI SDK by pointing base_url at Techopenclaw:

Request — Python (openai SDK)
import base64

from openai import OpenAI

client = OpenAI(api_key="toc-.....", base_url="https://api.techopenclaw.com/v1")

result = client.images.generate(
    model="gpt-image-2",
    prompt="A vivid neon-lit alley in Tokyo after rain, reflections on wet asphalt",
    size="1024x1024",
)

with open("image.png", "wb") as f:
    f.write(base64.b64decode(result.data[0].b64_json))
POST/v1/images/editsOpenAI

Edit or reference-based generation — same behavior as generations, but an input image is required.

Send raw JSON. The body is JSON with image as a data-URI (or bare base64) — the OpenAI SDK's default multipart upload is not supported here, so build the JSON yourself like the curl example below. The whole body is capped at 32 MiB.

FieldTypeRequiredDescription
modelstringYesImage model name from the LLM Catalog — gpt-image-2 or gpt-image-2.5-flare.
promptstringYesWhat to change — the edit instruction.
nintegerNoNumber of images requested, clamped to 1–10 (default 1). The value is not forwarded to the model — the response contains the images the model produced (see the note below the cards).
sizestringNoWxH (e.g. 1024x1024). OpenAI-image models receive it as-is; Gemini-image models approximate it to the nearest supported ratio (1:1, 16:9, 9:16, 4:3, 3:4) and ignore unparseable values. An unsupported size on an OpenAI-image model surfaces as a generic 529, not a size-specific error.
qualitystringNoForwarded on OpenAI-image models; ignored (no effect) on Gemini-image models.
image / imagesstring / arrayYesRequired. The image(s) to edit, as data-URIs or bare base64 (bare base64 is wrapped as PNG). image accepts a string or an array; images accepts an array only — a string under images is ignored. Missing or empty returns 400 with edits require an `image` data-URI.
output_formatstringNopng (the default), jpeg, or webp — any other value falls back to png.
response_formatstringNob64_json (the default) or url. url returns a data: URI built from the returned image — images are not hosted as files, so store the data yourself.
Request — curl (raw JSON, image as data-URI)
curl https://api.techopenclaw.com/v1/images/edits \
  -H "Authorization: Bearer toc-....." \
  -H "Content-Type: application/json" \
  -d '{
  "model": "gpt-image-2",
  "prompt": "Put a pair of sunglasses on the cat, keep everything else unchanged",
  "image": "data:image/png;base64,iVBORw0KGgo…",
  "size": "1024x1024"
}'

The number of images returned follows what the model produced (requested n caps at 10) — and a successful response always contains at least one image: a model that returns nothing is reported as an error, not as an empty list.

Billing

Images are priced per request: each request costs the model's flat credits-per-request rate — tokens are not counted, and the rate is the same whether the model returns one image or several. Both endpoints (/v1/images/generations and /v1/images/edits) are billed the same way. Rates per model are shown in the Pricing Policy. You are charged only when the request succeeds.

OutcomeBilling
200Charged in full: the model's flat credits-per-request rate — one charge per request, however many images come back
Any error statusNot charged — a zero-cost row with status error appears in the activity list

Error Codes

CodeMeaningAction
400Malformed request — missing model or prompt, an edit without an image, or a body over 32 MiB (easy to hit with large base64 payloads)Read error.message — an edit without an image returns 'edits require an `image` data-URI'
401Invalid or missing API keyCheck your toc- key
402Key out of credits (daily quota and top-up exhausted, or key expired), or the model has no per-request price configuredTop up, or verify the model in the LLM Catalog
404Unknown model aliasVerify the model name against the LLM Catalog
413The image payload is too large for the upstream providerShrink or downscale the input image
529Provider busy, rate-limited, failed, or the request timed out — provider errors never surface as 429/5xx hereRetry after a pause — respect Retry-After when present. Failed requests are not charged
Support · Telegram