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

# Grok Imagine 2.0 Ext Image Generation

>  - Async text-to-image; poll with task_id
- 1–12 images per request; billed per successfully delivered image ($0.08 each)
- URL output only; no image-to-image / streaming
- Image URLs expire in 72 hours 

<Info>
  **Text-to-image · async jobs.** Submit `POST /v1/images/generations`, then poll [Get task status](/en/api-reference/tasks/status).\
  Model name is fixed `grok-imagine-2.0-ext`. **Not supported**: reference images, `stream`, or `response_format` values other than `url`.
</Info>

<Warning>
  Do not put API keys in browser bundles (`VITE_*` / `NEXT_PUBLIC_*`, LocalStorage, etc.). Prefer calling your own BFF from the browser; keep the APIMart key on the server.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.apimart.ai/v1/images/generations \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --header 'Idempotency-Key: 8eacb46d-ef4e-4f4e-82de-830bd8bc67e2' \
    --header 'X-APIMart-Response-Version: 2026-07-27' \
    --data '{
      "model": "grok-imagine-2.0-ext",
      "prompt": "A red apple on a white ceramic plate, clean studio product photo",
      "n": 1,
      "size": "1:1",
      "resolution": "quality",
      "response_format": "url"
    }'
  ```

  ```python Python theme={null}
  import requests
  import uuid

  url = "https://api.apimart.ai/v1/images/generations"

  payload = {
      "model": "grok-imagine-2.0-ext",
      "prompt": "A red apple on a white ceramic plate, clean studio product photo",
      "n": 1,
      "size": "1:1",
      "resolution": "quality",
      "response_format": "url",
  }

  headers = {
      "Authorization": "Bearer <token>",
      "Content-Type": "application/json",
      "Accept": "application/json",
      "Idempotency-Key": str(uuid.uuid4()),
      "X-APIMart-Response-Version": "2026-07-27",
  }

  response = requests.post(url, json=payload, headers=headers)

  print(response.status_code, response.json())
  ```

  ```javascript JavaScript theme={null}
  const url = "https://api.apimart.ai/v1/images/generations";

  const payload = {
    model: "grok-imagine-2.0-ext",
    prompt: "A red apple on a white ceramic plate, clean studio product photo",
    n: 1,
    size: "1:1",
    resolution: "quality",
    response_format: "url",
  };

  const headers = {
    Authorization: "Bearer <token>",
    "Content-Type": "application/json",
    Accept: "application/json",
    "Idempotency-Key": crypto.randomUUID(),
    "X-APIMart-Response-Version": "2026-07-27",
  };

  fetch(url, {
    method: "POST",
    headers: headers,
    body: JSON.stringify(payload),
  })
    .then(async (response) => {
      console.log(response.status, await response.json());
    })
    .catch((error) => console.error("Error:", error));
  ```
</RequestExample>

<ResponseExample>
  ```json 202 theme={null}
  {
    "code": 202,
    "request_id": "2026081111342261665927mpb4IPDb",
    "data": {
      "id": "task_01KZQE5CM0Y3KZ6M1N1BK619MX",
      "object": "generation.task",
      "type": "image",
      "status": "pending",
      "progress": 0,
      "poll_url": "/v1/tasks/task_01KZQE5CM0Y3KZ6M1N1BK619MX"
    }
  }
  ```

  ```json 400 theme={null}
  {
    "request_id": "20260811...",
    "error": {
      "message": "`response_format` for grok-imagine-2.0-ext only supports `url` (got: b64_json)",
      "type": "invalid_response_format",
      "param": "",
      "code": "invalid_response_format"
    }
  }
  ```

  ```json 401 theme={null}
  {
    "error": {
      "code": 401,
      "message": "Authentication failed. Please check your API key",
      "type": "authentication_error"
    }
  }
  ```

  ```json 402 theme={null}
  {
    "error": {
      "code": 402,
      "message": "Insufficient balance. Please top up and try again",
      "type": "payment_required"
    }
  }
  ```

  ```json 429 theme={null}
  {
    "error": {
      "code": 429,
      "message": "Too many requests. Please try again later",
      "type": "rate_limit_error"
    }
  }
  ```
</ResponseExample>

## Capabilities and limits

| Dimension     | Contract                                                                        |
| ------------- | ------------------------------------------------------------------------------- |
| Model         | Fixed `grok-imagine-2.0-ext`                                                    |
| Capability    | **Text-to-image only**                                                          |
| Mode          | Async task                                                                      |
| Count `n`     | `1`–`12`, default `1`                                                           |
| `size`        | 7 aspect ratios + 5 pixel aliases (below)                                       |
| Output        | `response_format=url` only (also the default)                                   |
| Quality       | Public field `resolution`; verified value `quality`                             |
| Not supported | Image-to-image, `stream=true`, public `quality`, `style`, `b64_json` / `base64` |
| Billing       | Fixed unit price; charge **successfully delivered** images                      |

## Auth and recommended headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Get a key from the [API Key page](https://apimart.ai/keys).

  ```
  Authorization: Bearer YOUR_API_KEY
  ```
</ParamField>

| Header                       | Notes                                                                                                              |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `Content-Type`               | `application/json` (submit)                                                                                        |
| `Accept`                     | `application/json`                                                                                                 |
| `Idempotency-Key`            | Strongly recommended. New UUID per user-confirmed generation; network retries **must reuse** the same key and body |
| `X-APIMart-Response-Version` | Prefer `2026-07-27` for a stable submit shape (`data.id`)                                                          |

## Request parameters

<ParamField body="model" type="string" required>
  Fixed value: `grok-imagine-2.0-ext`
</ParamField>

<ParamField body="prompt" type="string" required>
  Prompt. Must be non-empty after trim. Trim before submit.
</ParamField>

<ParamField body="n" type="integer" default="1">
  Image count: `1`–`12`. Explicit `0` errors. Omit for `1`.
</ParamField>

<ParamField body="size" type="string">
  Aspect ratio. **Prefer ratio strings** (UI should only show ratios):

  | `size` | Orientation | Typical use               |
  | ------ | ----------- | ------------------------- |
  | `1:1`  | Square      | Product, avatar           |
  | `2:3`  | Portrait    | Poster, full-body         |
  | `3:2`  | Landscape   | Photo, wide scene         |
  | `3:4`  | Portrait    | E-commerce, people        |
  | `4:3`  | Landscape   | Display art               |
  | `9:16` | Vertical    | Story / short-video cover |
  | `16:9` | Wide        | Banner, video cover       |

  Pixel aliases: `1024x1024` (1:1), `1024x1792` (2:3), `1792x1024` (3:2), `720x1280` (9:16), `1280x720` (16:9).

  Values outside the whitelist return `400 invalid_size` (e.g. `1:2`, `2:1`, `4:5`, `auto`).

  <Note>
    Actual pixels for a given ratio may differ from the alias table (e.g. `1:1` may return 1408×1408). Trust the returned image; do not rewrite `size` from measured pixels.
  </Note>
</ParamField>

<ParamField body="resolution" type="string">
  Quality-mode field. Verified value: `quality`.

  * Omit (model is quality-mode by default), or
  * Pass `resolution: "quality"` explicitly

  **Not** a `1K` / `2K` / `4K` pixel tier; framing is controlled by `size`.

  <Warning>
    Do not send a public `quality` field — you get `400 invalid_quality`. Use `resolution`.
  </Warning>
</ParamField>

<ParamField body="response_format" type="string" default="url">
  Only `url` is allowed. May be omitted. `b64_json` / `base64` → `400 invalid_response_format`.
</ParamField>

<ParamField body="webhook" type="string">
  Optional public HTTPS **base URL**. On terminal status the platform POSTs `{webhook}/callback`. Server-side only — see [Webhook](#webhook-optional).
</ParamField>

### Unsupported parameters

| Parameter                                  | Behavior                                 |
| ------------------------------------------ | ---------------------------------------- |
| `quality`                                  | `400 invalid_quality` → use `resolution` |
| `style`                                    | `400 invalid_style`                      |
| `image_urls` / `image_with_roles`          | `400 invalid_image_input`                |
| `stream: true`                             | `400 invalid_stream`                     |
| `response_format: "b64_json"` / `"base64"` | `400 invalid_response_format`            |

Build requests with a whitelist; do not forward a generic image-form object from other models.

## Request examples

### Minimal

```json theme={null}
{
  "model": "grok-imagine-2.0-ext",
  "prompt": "A red apple on a white ceramic plate, clean studio product photo"
}
```

### Recommended

```json theme={null}
{
  "model": "grok-imagine-2.0-ext",
  "prompt": "A red apple on a white ceramic plate, clean studio product photo",
  "n": 1,
  "size": "1:1",
  "resolution": "quality",
  "response_format": "url"
}
```

## Submit response

Prefer `X-APIMart-Response-Version: 2026-07-27`. Success is HTTP **`202`**; task id is **`data.id`** (do not rely on legacy `data[0].task_id`).

Persist:

* `data.id` for polling
* `request_id` for gateway debugging
* the `Idempotency-Key` for safe retries when outcome is unknown
* original request params for UI / support

## Idempotency and safe retries

Image generation is billable — **strongly recommend** `Idempotency-Key` (1–191 printable ASCII chars; UUID is easiest; retained \~24 hours).

| Scenario                          | Behavior                                      | Action                                              |
| --------------------------------- | --------------------------------------------- | --------------------------------------------------- |
| Same key + same body already done | Replay; header `Idempotency-Replayed: true`   | Use the same task id                                |
| Same key still in flight          | `409 idempotency_in_progress` + `Retry-After` | Wait, retry **same key and body**                   |
| Same key, different body          | `409 idempotency_key_reused`                  | New logical job needs a new key                     |
| Outcome indeterminate             | `409 idempotency_result_indeterminate`        | Do not mint a new key; investigate with the old one |

On POST network timeout when you cannot tell if the server accepted the job, **do not immediately create a new key** — retry with the same key / body / response version.

## Poll tasks

```http theme={null}
GET /v1/tasks/{task_id}?language=en
Authorization: Bearer YOUR_API_KEY
Accept: application/json
```

Optional `language`: `zh` / `en` / `ko` / `ja` (failure message localization only). See [Get task status](/en/api-reference/tasks/status).

### Statuses

| `status`                 | Terminal | Handling                                                        |
| ------------------------ | :------: | --------------------------------------------------------------- |
| `pending` / `processing` |    No    | Keep polling (`result` may be absent — not a failure)           |
| `completed`              |    Yes   | Parse `result.images`                                           |
| `failed`                 |    Yes   | Show `error.message`; `cost` is `0` (pre-charge refunded)       |
| `unknown`                |    No    | Short retries; if it persists, contact support with the task id |

Poll about every **2 seconds**; cap near **10 minutes** or **120** attempts. Honor `Retry-After` on `429`. Tasks are kept \~3 days by default — keep the task id if the client times out.

### Completed example

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_01KZQE5CM0Y3KZ6M1N1BK619MX",
    "status": "completed",
    "progress": 100,
    "created": 1786419262,
    "completed": 1786419275,
    "actual_time": 13,
    "estimated_time": 100,
    "cost": 0.08,
    "credits_cost": 0.8,
    "result": {
      "images": [
        {
          "expires_at": 1786505675,
          "url": [
            "https://example.com/image/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.jpg"
          ],
          "image_ids": [
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
          ]
        }
      ]
    }
  }
}
```

### Parsing `url` and `image_ids`

```text theme={null}
result.images[]
  ├─ url[]          ← authoritative display/download field (array)
  ├─ image_ids[]    ← optional opaque IDs
  └─ expires_at     ← Unix seconds; multiply by 1000 for JS Date
```

1. Use `url[]` for display; when `n>1`, walk all entries
2. Pair by index only if `image_ids.length === url.length`
3. Missing `image_ids` still allows display
4. Links last **72 hours** — download promptly; also trust `expires_at`

## Billing

Base price **\$0.08 per image** (successful deliveries):

| `n` | Estimated base |
| --: | -------------: |
|   1 |         \$0.08 |
|   4 |         \$0.32 |
|   8 |         \$0.64 |
|  12 |         \$0.96 |

* Pre-submit UI should say “estimate”; final USD is **`data.cost`**
* **`data.credits_cost`** is the credits view (currently \~ USD × 10)
* Pre-charge by requested count; settle on successful count (partial refunds if partial failure)
* Full failure: `cost=0`, pre-charge refunded
* Do not build price keys from `resolution`; this model is flat per image

## Webhook (optional)

```json theme={null}
{
  "webhook": "https://your-service.example.com/apimart"
}
```

* Provide a **base URL**; the platform calls `{base}/callback`
* Must be public and pass SSRF checks
* If `webhook_secret` is set, signature is `hex(HMAC-SHA256(secret, raw_body))` over raw bytes
* Callback body matches task query `data` (no extra `{code,data}` wrapper)
* Still keep low-frequency polling as a fallback

## Common errors

| HTTP | `error.code`              | Cause                   | Action                                      |
| ---: | ------------------------- | ----------------------- | ------------------------------------------- |
|  400 | `invalid_request`         | Empty prompt / bad JSON | Validate input                              |
|  400 | `invalid_n`               | `n` outside 1–12        | Clamp count                                 |
|  400 | `invalid_size`            | Size not whitelisted    | Fixed select options                        |
|  400 | `invalid_response_format` | Not `url`               | Fix or omit                                 |
|  400 | `invalid_quality`         | Public `quality` sent   | Use `resolution`                            |
|  400 | `invalid_style`           | `style` sent            | Remove                                      |
|  400 | `invalid_image_input`     | Reference images        | Switch models                               |
|  400 | `invalid_stream`          | `stream=true`           | Remove                                      |
|  400 | `invalid_idempotency_key` | Bad key                 | Use UUID                                    |
|  401 | Auth failure              | Bad key                 | Fix server credentials                      |
|  402 | Payment required          | Low balance             | Top up                                      |
|  409 | `idempotency_*`           | Idempotency conflict    | See table above                             |
|  429 | Rate limit                | Too fast                | Honor `Retry-After`                         |
|  5xx | Server error              | —                       | Keep Idempotency-Key; do not blindly rotate |

Prefer `error.message` for UI. Do not surface raw auth internals to end users.

## Differences from 1.5 (summary)

| Item           | Grok Imagine 1.5                 | 2.0 Ext                                          |
| -------------- | -------------------------------- | ------------------------------------------------ |
| Model          | `grok-imagine-1.5-apimart`, etc. | `grok-imagine-2.0-ext`                           |
| Image-to-image | Supported (see 1.5 docs)         | **Not supported**                                |
| Count          | See 1.5 docs                     | **1–12**                                         |
| Quality field  | See 1.5 docs                     | `resolution` (`quality`); never public `quality` |
| Output         | See 1.5 docs                     | **URL only**                                     |
| URL TTL        | See 1.5 docs (often 24h)         | **72 hours**                                     |
| Unit price     | See 1.5 docs                     | **\$0.08 / image**                               |
