Build with the AI

Image generation

Generate images from a prompt with a synchronous, credit-metered endpoint.

The imagine endpoint turns a text prompt into an image and returns the finished image URL in a single request. It is synchronous: the connection is held open while the job runs, up to a bounded timeout, and the response carries the result directly. Any request that fails or does not finish in time is cancelled and refunded, so you are never charged for an image you did not receive. There is no separate fire-and-forget submit/poll flow in v1.

Endpoint

POST/api/v1/ai/imagine

Requires a krallo_live_… bearer key with the ai:imagine scope. See Authentication for how keys are issued and sent. The request body is JSON; only prompt is required.

Unlike the native REST resources, this endpoint returns a bare JSON object (no { "data": …, "meta": … } envelope), matching the style of the OpenAI-compatible Chat completions endpoint. Errors raised by the endpoint itself (400, 405, 502, 504) use the shape { "error": { "message": …, "type": … } }; the shared authentication, scope, and credit refusals (401, 403, 402) use the native { "error": { "code": …, "message": … }, "meta": … } envelope.

Request parameters

All parameters live in the JSON request body.

NameTypeRequiredDescription
prompt string required The text description of the image to generate. Must be present and non-blank, or the call returns 400 invalid_request_error.
negative_prompt string optional Concepts to steer away from. Support depends on the underlying provider.
seed integer optional Seed for reproducible sampling. Omit for a random seed on each call.
steps integer optional Sampler step count. Omit to use the provider default.
guidance number optional Prompt-adherence (guidance) strength. Omit to use the provider default.
width integer optional Requested output width in pixels. Omit to use the provider default.
height integer optional Requested output height in pixels. Omit to use the provider default.
output_count integer optional Number of images the job should render. The synchronous response surfaces a single output_url (the primary image) regardless of count.
strength number optional Denoising/transform strength for the sampler. Omit to use the provider default.
project_id string optional Your own grouping/reference id, attached to the job for organization. It does not affect generation.
wait_timeout_ms integer optional How long the request will wait for the job to finish, in milliseconds. Default 90000; clamped to the range 15000–180000. Values outside the range are pulled to the nearest bound.

The mode is fixed to text-to-image. There is no image-input, mask, or edit parameter on this endpoint in v1; steps, guidance, and strength are passed through to the image pipeline as-is and are not range-validated by the API, so unsupported values fall back to provider behavior.

Synchronous wait behavior

When you POST, the request submits the job and blocks until one of the following happens:

Cancelling a timed-out job before refunding is deliberate: it prevents a background job from finishing and delivering an image after you have already been refunded. Choose wait_timeout_ms to fit your client’s own read timeout — a larger value tolerates slow provider queues, a smaller value returns a retryable 504 sooner.

Example request

cURL
curl https://exe.krallo.com/api/v1/ai/imagine \
  -H "Authorization: Bearer krallo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a lighthouse on a cliff at dawn, cinematic, volumetric light",
    "negative_prompt": "text, watermark, low quality",
    "width": 1024,
    "height": 1024,
    "seed": 42,
    "wait_timeout_ms": 120000
  }'
Node.js
const res = await fetch("https://exe.krallo.com/api/v1/ai/imagine", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.KRALLO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    prompt: "a lighthouse on a cliff at dawn, cinematic, volumetric light",
    width: 1024,
    height: 1024,
    wait_timeout_ms: 120000,
  }),
});

if (!res.ok) {
  const err = await res.json();
  throw new Error(`${res.status} ${err.error?.type}: ${err.error?.message}`);
}

const { output_url } = await res.json();
console.log("image:", output_url);

Success response

A completed job returns 200 OK with a bare object. output_url is an absolute, publicly fetchable URL for the finished image.

{
  "job_id": "9f2c1b7a-4e6d-40b1-a0c3-f5e8d2b1a6c4",
  "status": "SUCCEEDED",
  "output_url": "https://exe.krallo.com/img/ai/c8b1f0d2-7a3e-4f19-9d5c-2e6b8a4c0d17.png",
  "provider_used": "..."
}
FieldTypeDescription
job_idstringUnique id for this generation job. Also the idempotency key on the webhook, if you subscribe to one.
statusstringAlways SUCCEEDED on a 200 response.
output_urlstringAbsolute URL of the generated image.
provider_usedstringOptional. A label for the backend that rendered the image. Present when known; omitted otherwise.

Billing and refunds

Image generation is billed at a flat per-image cost (default 10 credit units), not by tokens — the price is known up front, so the cost is pre-charged before the job runs and refunded on any non-delivery. Each app has a free daily allowance (default 10000 units) that resets at UTC midnight; charges hit your prepaid wallet only after that allowance is exhausted. See Billing for the credit model, free quota, and how the owner’s wallet is debited.

A successful image is charged once, on the developer’s wallet that owns the app’s key. Retrying a 502/504 is safe from a billing standpoint — the failed attempt was already refunded.

Webhook: imagine.completed

If you have a verified webhook endpoint subscribed to imagine.completed, a successful generation also fires that event (best-effort, after the image is finalized). The synchronous response is authoritative for a single call; the webhook is useful for auditing or for reconciling generations across your fleet. The payload is URL-only — it never carries model, cost, or other provider internals beyond the provider label.

{
  "job_id": "9f2c1b7a-4e6d-40b1-a0c3-f5e8d2b1a6c4",
  "status": "SUCCEEDED",
  "output_url": "https://exe.krallo.com/img/ai/c8b1f0d2-7a3e-4f19-9d5c-2e6b8a4c0d17.png",
  "provider_used": "...",
  "mode": "text_to_image",
  "width": 1024,
  "height": 1024
}

Because job_id is a unique per-job UUID, it is a natural idempotency key — deduplicate on it if a delivery is retried.

Errors

Errors raised by the endpoint itself (400, 405, 502, 504) use { "error": { "message": …, "type": … } }; authentication, scope, and credit refusals (401, 403, 402) use the native envelope, with the identifier in error.code. See Errors for the shared model and correlation ids.

StatusTypeWhen
400invalid_request_errorBody is not valid JSON, or prompt is missing/blank.
401unauthorized / invalid_token / token_expired / token_revokedMissing, malformed, expired, or revoked bearer key. See Authentication.
402insufficient_creditsOver the free allowance and the wallet cannot cover the flat image cost. Body includes required_units and balance.
403insufficient_scope / app_disabled / owner_suspendedKey lacks the ai:imagine scope (body includes required_scope), or the app/account is disabled.
405method_not_allowedAny method other than POST (or the pre-flight OPTIONS).
429rate_limitedPer-key rate limit exceeded. Honor the Retry-After header. See Rate limits.
502upstream_errorThe job reached a terminal failed/cancelled state. Refunded; retryable.
504timeoutThe job did not finish within wait_timeout_ms; it was cancelled. Refunded; retryable.

Rate limits

Imagine calls count against your key’s per-minute rate limit, which is driven by the app’s tier. Every response past the authentication and scope checks carries advisory X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers; a 429 additionally sets Retry-After. Because a request holds the connection for the full generation, keep your client’s read timeout at least as large as your chosen wait_timeout_ms.

Related