---
title: "Generate a hero image for existing content"
method: POST
path: "/content/{id}/generate-image"
tags: ["Content"]
---

# Generate a hero image for existing content

`POST /content/{id}/generate-image`

## Request

- Base URL: `https://next.frase.io/api/v1`
- URL: `https://next.frase.io/api/v1/content/{id}/generate-image`
- Auth: one of:
  - API key in header `X-API-KEY`
  - HTTP bearer

Queue one AI hero image for an article that already exists and attach it as the featured image. The article body, brief and meta are never touched, so edits made after generation are kept. Runs asynchronously: a render takes 20 to 60 seconds. Poll GET /content/{id}: `progress_details.heroImageStatus` goes `queued` then `completed` (image on `featured_image`) or `failed` (`heroImageFailureReason` says why; stop polling). Uses one image credit and one AI-image unit of the site budget, both reserved at request time and refunded exactly once if the render fails. Refuses with 409 when the article already has a hero image unless `replace` is true.

## Path parameters

- `id` string, uuid, required

## Request body

- object
  - `replace` boolean: Overwrite an existing hero image. Off by default so an automated pipeline never silently replaces a picture someone chose by hand.

## Response `202`

Hero image generation queued

- object
  - `data` object
    - `id` string, uuid
    - `status` 'queued'
    - `replace` boolean
    - `request_id` string, uuid: This request's receipt id. The article's `progress_details.heroImageRequestId` names the request that last wrote its hero status; compare the two so a concurrent request's outcome is never read as this one's.
    - `previous_featured_image` string, nullable: The hero image on the article when the request was accepted, or null. With `replace`, the old picture stays up until the new one lands, so poll until `featured_image` differs from this value.
    - `poll_url` string: GET this until `progress_details.heroImageStatus` is `completed` with `heroImageRequestId` equal to `request_id` (and, on a replace, `featured_image` differs from `previous_featured_image`). `failed` for this request id, with `heroImageFailureReason`, means the render will not arrive; stop polling.
    - `updated_at` string, date-time, nullable

## Other responses

- `400`: Invalid content ID, invalid body, or the content has no body text yet to write a prompt from
- `401`: Authentication failed
- `403`: AI images are turned off for this content's site (`ai_images_disabled`)
- `404`: Resource not found
- `409`: The content already has a hero image and `replace` was not true (`hero_image_exists`)
- `429`: No image credits left (`image_credits_exhausted`, no Retry-After: the allowance resets monthly), or the per-caller generation rate limit (`rate_limit_exceeded`, carries Retry-After)

---

[API](https://skmtc.dev/frase/apis/frase-api.md) · [All operations](https://skmtc.dev/frase/apis/frase-api/llms.txt) · [OpenAPI document](https://skmtc.dev/frase/apis/frase-api/revisions/7fb75d89f7a4?raw)
