Generate an image from a prompt (asynchronous)
Sample request
curl -X POST \
-H "x-api-key: $ABYSSALE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A red fox sitting in fresh snow at sunrise, soft light",
"model": "nano-banana-2",
"aspect_ratio": "16:9",
"callback_url": "https://example.com/webhooks/abyssale"
}' \
https://api.abyssale.com/images/text-to-image
# {"job_id":"8f0c2a4e-…","status":"PENDING"} — then poll it, or wait for the webhook.Example request body
{
"prompt": "A red fox sitting in fresh snow at sunrise, soft light",
"model": "nano-banana-2",
"aspect_ratio": "16:9",
"callback_url": "https://example.com/webhooks/abyssale"
}Example 202 response
{
"job_id": "8f0c2a4e-4869-11f0-96f2-0a00d9eb8f78",
"status": "PENDING",
"version": "v2026-11-09"
}Example 400 error response
{
"id": "unknown_enum_value",
"message": "aspect_ratio '16:9' is not supported by the model 'flux-2-pro'.",
"errors": [
{
"path": "aspect_ratio",
"code": "unknown_enum_value",
"message": "Must be one of: square_hd, square, portrait_4_3, portrait_16_9, landscape_4_3, landscape_16_9.",
"expected": [
"square_hd",
"square",
"portrait_4_3",
"portrait_16_9",
"landscape_4_3",
"landscape_16_9"
]
}
]
}Generate one image from a text prompt, with an AI model, optionally guided by reference images. The work runs in the background: this call answers 202 with a job_id in milliseconds, before anything is generated. Follow the job by polling GET /asset-jobs/{jobId}, or pass a callback_url to receive the ASSET_JOB_FINALIZED webhook when it ends. One image per job: the finished job has exactly one entry in outputs.
Behaviour, limits and caveats
Spends AI credits, not generation credits, and only once the job completes; the cost is on the finished job (credits.ai_credits). The price depends on the model and on what you ask for (aspect_ratio, quality, reference images). Before anything is queued, the workspace's AI credit balance is checked against the price of this exact call (402 insufficient_credits otherwise). That check is not a reservation: a job can still end FAILED with insufficient_credits if the balance runs out before it runs.
Models. Omit model to use the workspace's default for image generation (set in the workspace settings, otherwise Abyssale's default). A model disabled in the workspace settings answers 400 ai_model_disabled.
aspect_ratio and quality depend on the model. Each model accepts its own values — a ratio such as 16:9 for some, a size such as landscape_16_9 or 1536x1024 for others — and some models take no quality at all. GET /ai-models?operation=images_text_to_image lists, per model, the accepted values and the default. Omit either one to get the model's default. A value the model does not accept, or a quality sent to a model that takes none, answers 400 unknown_enum_value before anything is queued or charged, with the accepted values in errors[].expected (an empty list when the model takes no such parameter).
Prompt. At least 3 words and at most 4 000 characters.
Reference images (reference_image_urls) guide the generation — a product to stage, a style to follow. They are downloaded when the job runs; one that cannot be read fails the job with invalid_input_asset. The model must accept reference images: one that does not answers 400 unknown_enum_value on model, listing the models that do. Each model takes at most its max_reference_images from GET /ai-models (3 to 16 depending on the model); more answers 400 out_of_range on reference_image_urls. Some models charge more per reference image; the pre-check prices them in.
At most a few jobs per workspace run at once (pending or processing, every kind of asset job together). Past that, a new one answers 429 too_many_asset_jobs: wait for one to finish, then retry. This endpoint has no per-minute budget of its own — that cap is what bounds it.
Never retried for you, and do not retry blindly. Every 202 is a new job, charged on its own. A 503 (asset_jobs_disabled, ai_service_unavailable) means no job was created and nothing was charged.
Request body
prompt string required
What to generate. At least 3 words. At most 4000 characters.
model string
The AI model to use, by id — one listed by GET /ai-models?operation=images_text_to_image. Omitted, the workspace's default is used. An id that is not an image generation model answers 400.
aspect_ratio string
The image's shape, as the model names it — one of the model's aspect_ratios.allowed in GET /ai-models?operation=images_text_to_image. Omitted, the model's default. Another value answers 400 unknown_enum_value with the accepted ones in errors[].expected.
quality string
The image's quality or resolution tier, as the model names it — one of the model's qualities.allowed. Omitted, the model's default. Send none for a model that lists no qualities: it takes none, and any value answers 400 unknown_enum_value.
reference_image_urls array of string (uri)
Public http(s) URLs of images that guide the generation: PNG, JPEG or WebP, up to 20 MB each. 1–16 items.
More about reference_image_urls
Public http(s) URLs of images that guide the generation: PNG, JPEG or WebP, up to 20 MB each. At least 1, at most the model's max_reference_images from GET /ai-models (16 for the models that take the most). Downloaded when the job runs. The model must accept reference images; some models charge for each one.
callback_url string (uri)
The http(s) URL called once the job ends, completed or failed. Length 5–2048 characters.
More about callback_url
The http(s) URL called once the job ends, completed or failed.
We will do a POST request of the ASSET_JOB_FINALIZED event on this URL with a JSON payload.
Response 202
The job is queued; poll job_id or wait for the webhook.
job_id string (uuid) always
Identifier of the job — poll GET /asset-jobs/{jobId} with it.
status string always
Always PENDING here. One of "PENDING".
version string
The API version that produced this response, named by release date (vYYYY-MM-DD). Pattern ^v\d{4}-\d{2}-\d{2}$.
More about version
The API version that produced this response, named by release date (vYYYY-MM-DD). Stamped as a top-level field on JSON object bodies, success and error alike, so a client can always tell which contract answered. There is no version-selection parameter — a single version is maintained at a time.
Two kinds of body are not stamped. Array bodies (the listings) carry no envelope. And a body that already has a version key of its own is left alone — which in practice means Banner, whose version is the generated file's integer counter. So GET /banners/{bannerId} and the synchronous generate are the two responses that do not tell you which contract answered.
The value changes when a new version is released. Match the vYYYY-MM-DD shape rather than pinning today's literal, or your client breaks on the next release.
Errors
Every failure uses the shared error envelope — {id, message, errors?}. Branch on id, never on message. See Errors.
| Status | When |
|---|---|
400 | Bad Request — nothing was queued. |
401 | Unauthorized — missing or invalid API key. |
402 | Not enough AI credits for this call (insufficient_credits). Nothing was queued. |
429 | Too Many Requests. |
500 | Internal Server Error — an unexpected error occurred on our side. |
503 | The job could not be started right now — no job was created and nothing was charged. asset_jobs_disabled while the feature is switched off, ai_service_unavailable when a service it depends on cannot be reached. Retry later. |
