> ## Documentation Index
> Fetch the complete documentation index at: https://rendobar.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Generate an image from a prompt

> Generate a webp image from a text prompt on hosted diffusion models. Pick an economy, standard, or premium tier, or pin an exact model id.

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
__html: JSON.stringify({
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "@id": "https://rendobar.com/docs/jobs/image-generate/#article",
  "headline": "Generate an image from a prompt",
  "description": "Generate a webp image from a text prompt on hosted diffusion models. Pick an economy, standard, or premium tier, or pin an exact model id.",
  "datePublished": "2026-07-26",
  "dateModified": "2026-07-31",
  "author": { "@type": "Organization", "@id": "https://rendobar.com/#organization" },
  "publisher": { "@type": "Organization", "@id": "https://rendobar.com/#organization" },
  "isPartOf": { "@id": "https://rendobar.com/#website" }
})
}}
/>

`image.generate` turns a text prompt into an image on a hosted diffusion model. Ask for a tier and the platform picks the model, or pin an exact model id to reach its own controls. The result is a webp file returned as a signed URL, the same async job shape as everything else on Rendobar.

This is one product in [Rendobar's Generation API](/docs/concepts/generation).

## Generate an image

<CodeGroup>
  ```ts SDK theme={null}
  import { createClient, outputUrl } from "@rendobar/sdk";

  const client = createClient({ apiKey: "rb_YOUR_KEY" });

  // jobs.run submits and waits for the finished job in one call.
  const job = await client.jobs.run({
    type: "image.generate",
    params: {
      prompt:
        "A ceramic pour-over coffee dripper on a walnut counter, morning light from the left, soft shadows",
      width: 1024,
      height: 1024,
    },
  });

  console.log(outputUrl(job)); // signed URL to the webp
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.rendobar.com/jobs" \
    -H "Authorization: Bearer rb_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "image.generate",
      "params": {
        "prompt": "A ceramic pour-over coffee dripper on a walnut counter, morning light from the left, soft shadows",
        "width": 1024,
        "height": 1024
      }
    }'
  # Returns { "data": { "id": "job_...", "status": "waiting" } }.
  # Poll GET /jobs/{id} until "status": "complete".
  ```

  ```python Python theme={null}
  import requests, time

  base = "https://api.rendobar.com"
  headers = {"Authorization": "Bearer rb_YOUR_KEY"}

  job = requests.post(
      f"{base}/jobs",
      headers=headers,
      json={
          "type": "image.generate",
          "params": {
              "prompt": "A ceramic pour-over coffee dripper on a walnut counter, morning light from the left, soft shadows",
              "width": 1024,
              "height": 1024,
          },
      },
  ).json()["data"]

  while job["status"] not in ("complete", "failed", "cancelled"):
      time.sleep(1)
      job = requests.get(f"{base}/jobs/{job['id']}", headers=headers).json()["data"]

  print(job["output"]["file"]["url"])  # the webp
  ```
</CodeGroup>

No `inputs` are needed. The prompt is the whole request. Every generation job is async, so you submit, then poll, [wait](/docs/sdk#wait), or receive a [webhook](/docs/guides/webhooks).

## The output

A completed job carries a single webp file in `output.file`. `output.data` is `null`, because generation writes a file rather than computing an answer.

```json theme={null}
{
  "data": null,
  "file": {
    "url": "https://api.rendobar.com/dl/job_abc123?token=<token>",
    "path": "output.webp",
    "type": "image",
    "size": 512000,
    "meta": { "format": "webp", "width": 1024, "height": 1024 }
  },
  "files": [
    { "url": "https://api.rendobar.com/dl/job_abc123?token=<token>", "path": "output.webp", "type": "image", "size": 512000, "meta": { "format": "webp", "width": 1024, "height": 1024 } }
  ],
  "expiresAt": 1735689600000
}
```

The real dimensions are in `output.file.meta`. Each model snaps your requested `width` and `height` to a size it can render, so the returned image can differ from what you asked for. The full [output shape](/docs/concepts/job#the-output) is the same for every job type.

## Watch it render

A generation job emits `job.preview` events while the model denoises, each carrying a small webp frame. Subscribe with the SDK to show the picture resolving instead of a spinner.

```ts theme={null}
const sub = client.realtime.subscribeJob(created.id, {
  onPreview: (e) => {
    // e.data is base64 webp, roughly 256px on its long edge.
    setPreview(`data:image/webp;base64,${e.data}`);
    setBlur((1 - (e.progress ?? 0)) * 20); // ease the blur out as it sharpens
  },
  onComplete: (job) => sub.unsubscribe(),
});
```

| Field              | Meaning                                                                                 |
| ------------------ | --------------------------------------------------------------------------------------- |
| `seq`              | Monotonic per job. Drop any frame whose `seq` is at or below the last one you rendered. |
| `progress`         | Denoise progress from 0 to 1 at capture, or `null` when the model does not report it.   |
| `width` / `height` | Dimensions of the preview frame, not of the final image.                                |
| `data`             | Base64 webp, roughly 256px on its long edge.                                            |

Previews are decoration and never hold up a job. They are ephemeral: nothing is replayed on reconnect, and a late subscriber sees only the newest frame. A model with no fast decoder emits none, and a four-step model resolves too late to be worth watching. Build for zero frames and treat anything you get as a bonus.

## Tiers and models

Set `model` to a tier alias for a price and quality posture without naming a model, or pin an exact model id to reach that model's own controls. Omit `model` and you get the `economy` tier.

<CodeGroup>
  ```ts SDK theme={null}
  // Tier alias: the platform picks the model.
  await client.jobs.run({
    type: "image.generate",
    params: { prompt: "A red bicycle leaning on a brick wall", model: "premium" },
  });

  // Pinned model id: unlocks that model's own controls.
  await client.jobs.run({
    type: "image.generate",
    params: {
      prompt: "A red bicycle leaning on a brick wall",
      model: "qwen-image-2512",
      steps: 40,
      guidance: 4.5,
      negativePrompt: "blurry, low quality",
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.rendobar.com/jobs" \
    -H "Authorization: Bearer rb_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "image.generate",
      "params": { "prompt": "A red bicycle leaning on a brick wall", "model": "premium" }
    }'
  ```

  ```python Python theme={null}
  requests.post(
      f"{base}/jobs",
      headers=headers,
      json={
          "type": "image.generate",
          "params": {"prompt": "A red bicycle leaning on a brick wall", "model": "premium"},
      },
  )
  ```
</CodeGroup>

The three tiers map to these models today. Aliases can be re-pointed as the catalog grows, so pin an exact id when you need a result to stay stable.

| Tier                | Model             | Price  | Controls when pinned             | Best for                                     |
| ------------------- | ----------------- | ------ | -------------------------------- | -------------------------------------------- |
| `economy` (default) | `flux-2-klein-4b` | `$`    | prompt only                      | Drafts and fast iteration at the lowest cost |
| `standard`          | `z-image-turbo`   | `$$`   | steps (8 to 12)                  | Everyday images at turbo speed               |
| `premium`           | `qwen-image-2512` | `$$$$` | steps, guidance, negative prompt | Highest fidelity with full control           |

You can also pin a model that no tier points at:

| Model        | Price  | Controls                   | Best for                             |
| ------------ | ------ | -------------------------- | ------------------------------------ |
| `flux-2-dev` | `$$$$` | steps (10 to 50), guidance | FLUX.2 quality with guidance control |

<Info>
  `Price` is the model's `priceTier`, a relative comparison from `$` to `$$$$`. It is not a per-image charge. Jobs bill on the compute they actually use, cost-plus, so a slower model on a bigger GPU costs more per image. See [credits and billing](/docs/concepts/credits).
</Info>

### Models under review

A model can be pulled from tier resolution while we re-evaluate its cost and quality. It stays in [`GET /models`](#discover-models) with `underReview: true`, and pinning it returns `VALIDATION_ERROR` at submit before anything is billed.

`ernie-image-turbo` and `qwen-image-2512-lightning` are under review today. Use a tier, or pin one of the models above.

## Model-specific controls

Tier aliases accept the base fields only. To use `steps`, `guidance`, or `negativePrompt`, pin an exact model id. Sending a control the resolved model does not support returns `VALIDATION_ERROR` before anything is billed.

| Control          | Models that accept it                            | Range                      |
| ---------------- | ------------------------------------------------ | -------------------------- |
| `steps`          | `z-image-turbo`, `qwen-image-2512`, `flux-2-dev` | Per model, see table above |
| `guidance`       | `qwen-image-2512`, `flux-2-dev`                  | 1 to 10                    |
| `negativePrompt` | `qwen-image-2512`                                | Up to 1000 characters      |

`flux-2-klein-4b` fixes its own step count and exposes the base fields only.

## Parameters

<ParamField body="prompt" type="string" required>
  What you want, in plain language. No special syntax. Up to 4000 characters.
</ParamField>

<ParamField body="model" type="string" default="economy">
  A tier alias (`economy`, `standard`, `premium`) or an exact model id. A tier lets the platform pick the model. A pinned id unlocks that model's own controls.
</ParamField>

<ParamField body="width" type="integer">
  Requested output width in pixels, up to 4096. Snapped to what the model can render. The real size comes back in `output.file.meta`.
</ParamField>

<ParamField body="height" type="integer">
  Requested output height in pixels, up to 4096. Snapped to what the model can render.
</ParamField>

<ParamField body="seed" type="integer">
  A fixed seed makes the result reproducible. Omit it for a fresh random image each time.
</ParamField>

<ParamField body="enhancePrompt" type="boolean">
  Rewrite the prompt for the model before generating. Off by default on every model in the catalog today. The default is per model (`enhanceDefault` in [`GET /models`](#discover-models)), so omit it to keep whatever the model ships with.
</ParamField>

<ParamField body="steps" type="integer">
  Denoise steps. More steps means more detail and more time. Requires a pinned model that exposes steps. The accepted range depends on the model.
</ParamField>

<ParamField body="guidance" type="number">
  How strictly to follow the prompt. Higher is stricter. Range 1 to 10. Requires `qwen-image-2512` or `flux-2-dev`.
</ParamField>

<ParamField body="negativePrompt" type="string">
  What to keep out of the image. Up to 1000 characters. Requires `qwen-image-2512`.
</ParamField>

## Discover models

`GET /models` lists every generation model with its tier, relative price, capabilities, and step range, so a model picker never has to hardcode the catalog. Filter to one job type with `?job=`.

```bash theme={null}
curl "https://api.rendobar.com/models?job=image.generate" \
  -H "Authorization: Bearer rb_YOUR_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "flux-2-klein-4b",
      "jobs": ["image.generate", "image.edit"],
      "tier": "economy",
      "underReview": false,
      "priceTier": "$",
      "maxRefImages": 4,
      "steps": null,
      "supports": { "negativePrompt": false, "guidance": false },
      "enhanceDefault": false,
      "status": "active"
    }
  ]
}
```

Read `tier` to group models, `priceTier` to sort them by cost, and `underReview` to grey out the ones that will not run. `steps` is `null` on a model that fixes its own step count.

## Errors

A bad request fails on `POST /jobs` before anything is billed. An unknown model id, or a control the resolved model does not support, returns `VALIDATION_ERROR`. After the job starts, failures carry the standard [error](/docs/support/errors) shape.

## See also

* [Generation API](/docs/concepts/generation): the modalities and the shared model catalog
* [Image edit](/docs/jobs/image-edit): change an existing image from an instruction
* [Job output](/docs/concepts/job#the-output): the output shape every job returns
* [SDK](/docs/sdk): `jobs.run`, `jobs.wait`, and reading the output
* [Webhooks](/docs/guides/webhooks): receive `job.completed` instead of polling
