Image API Usage Guide
Generate an image from a prompt, or edit an existing image with one. Image generation is asynchronous: you submit a request, poll it until it settles, then fetch the generated file.
The same API covers video — see the Video API for the video-specific parameters and billing. Older clients may still call the legacy routes; they forward to the same engine and are documented at the bottom of this page.
Submit: POST https://api.radienthq.com/v1/tools/media/generate. Poll: GET /v1/tools/media/status?request_id=…. Fetch: GET /v1/tools/media/result?request_id=….
#Quickstart
Submit a request. The default image model is used when you do not name one:
curl https://api.radienthq.com/v1/tools/media/generate \
-H "Authorization: Bearer $RADIENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "A single red apple on a white table, studio photo", "image_size": "square"}'
The response is the queue handle and the quoted price — nothing is charged yet:
{"request_id":"01a1218e-4850-77e0-8370-ecd4926402ff","model":"…","provider":"…","status":"IN_QUEUE","queue_position":0,"cost_usd":0.03,"units":1,"unit":"megapixel"}
Poll the request id until it settles:
curl "https://api.radienthq.com/v1/tools/media/status?request_id=01a1218e-4850-77e0-8370-ecd4926402ff" \
-H "Authorization: Bearer $RADIENT_API_KEY"
{"request_id":"01a1218e-4850-77e0-8370-ecd4926402ff","model":"…","status":"COMPLETED","metrics":{"inference_time":7.422000169754028},"settled":true,"cost_usd":0.03,"units":1,"unit":"megapixel"}
Then fetch the result and download the file the output points at:
curl "https://api.radienthq.com/v1/tools/media/result?request_id=01a1218e-4850-77e0-8370-ecd4926402ff" \
-H "Authorization: Bearer $RADIENT_API_KEY"
{"request_id":"01a1218e-4850-77e0-8370-ecd4926402ff","model":"…","status":"COMPLETED","output":{"images":[{"content_type":"image/jpeg","file_name":"QWj1y0ZmH04O4QYZZEgHF_JngXHa9M.jpg","file_size":null,"height":512,"url":"https://…/QWj1y0ZmH04O4QYZZEgHF_JngXHa9M.jpg","width":512}],"seed":919543088}}
Example bodies on this page come from live calls; … marks a value trimmed here.
#Selecting a model
GET https://api.radienthq.com/v1/tools/media/models is public — no key required. It returns every model you can pass as model, with its display name, type (image or video), billing unit, unit price and a pricing note:
{"models":[{"id":"…","display_name":"…","type":"image","unit":"megapixel","unit_price_usd":0.03,"pricing_note":"First output megapixel $0.03; each extra megapixel $0.015 (input and output), rounded to the nearest megapixel (minimum 1).","default":true,"legacy":false}]}
Read the list from the endpoint — model ids and prices change over time. Omit model to use the default ("default": true, an image model today); the response always echoes the model that served your request. legacy: true marks entries kept for older clients.
#Submitting a request
The request body is the selected model's own parameter object, with two reserved keys the platform reads:
| Field | Type | Required | Notes |
|---|---|---|---|
model | string | No | A model id from /v1/tools/media/models. Empty → the default model. An unknown id is rejected with 400 media_rejected before any generation. |
| every other field | – | – | Forwarded as parameters to the model. Prompt, sizes, counts and seeds are per model; the platform validates and prices the ones that affect cost (below). |
Cost-relevant parameters the platform understands on every image model:
| Field | Type | Notes |
|---|---|---|
prompt | string | What to generate. |
image_size | string | object | A preset — square_hd (1024×1024), square (512×512), portrait_4_3 (768×1024), portrait_16_9 (576×1024), landscape_4_3 (1024×768, the default), landscape_16_9 (1024×576) — or {"width":…,"height":…} with each edge at most 8192. |
num_images | integer | How many images to generate, within the model's own limit (a model that produces one output per request rejects anything but 1). |
seed | integer | Reproducibility, where the model supports it. |
Anything the platform cannot price is refused up front with 400 media_rejected — you are never charged for a request that could not be quoted. The submission body is capped at 1 MiB (413 past it).
#Editing an image
Choosing the edit model (from GET /v1/tools/media/models; the editing entry's id and display name say so) and passing source image URLs performs image-to-image; image_size must be explicit because the output size cannot be inferred from a URL the platform does not fetch:
{
"model": "YOUR_EDIT_MODEL_ID",
"prompt": "Make the apple green",
"image_urls": ["https://…/QWj1y0ZmH04O4QYZZEgHF_JngXHa9M.jpg"],
"image_size": "square"
}
image_urls— one or more source images; the edit model reads this field. A request to an edit model without an explicitimage_sizeis rejected (this fails closed rather than guessing the output price).- To animate an image instead of editing it, see Image-to-video.
- Only output-side megapixels are billed on edits today (see Pricing).
#Job and progress model
A submission returns a request_id, which is the handle for everything else. Poll GET /v1/tools/media/status?request_id=… until the response carries "settled": true; then fetch the output from GET /v1/tools/media/result?request_id=….
Status values are IN_QUEUE, IN_PROGRESS, COMPLETED and CANCELLED. While a request is waiting, queue_position tells you where it is; the status response may also include logs and metrics from the generation service. There is no webhook support — poll at your own cadence (one to a few seconds apart is plenty for images; video takes longer).
The status response fields:
| Field | Notes |
|---|---|
request_id, model | The handle and the model serving it. |
status | IN_QUEUE, IN_PROGRESS, COMPLETED or CANCELLED. |
queue_position | Present while queued. |
settled | true once a terminal outcome has been recorded; the settled cost_usd / units / unit appear at the same time. |
cost | The versioned cost object: the quote while unsettled, billed with the settled figure once settled. See Cost reporting. |
error, error_type | Present on a failure — see Failures. |
metrics, logs | Generation telemetry from the service (latency, and any log lines it publishes). |
GET /v1/tools/media/result returns the same header fields plus output — the generated object. Image models return output.images (array of url, width, height, content_type, file_name); the top level carries the seed. The generated object appears once the status is COMPLETED.
Download the file, do not store the URL as your copy. The url points at the generated file on the generation service's storage; fetch it and save the bytes somewhere you control, and treat the link as temporary.
#Pricing
Every submission is quoted before it runs: the submit response's cost_usd / units / unit are the price, and GET /v1/tools/media/models publishes each model's unit_price_usd and a pricing_note describing the arithmetic. Nothing is charged at submit — the quote is only reserved against your balance until the request settles. The same figures travel in the response's cost object: basis quote at submission and while the request is unsettled, billed with the settled figure once settled — see Cost reporting.
How settlement works:
- The charge is written when the platform observes the terminal state and successfully fetches the result — i.e. on your first poll that sees it done. Poll your requests.
- Failures cost $0. A generation that fails settles at
cost_usd: 0witherror_type: "media_failed". - A cancelled request is not automatically free. Cancellation is best-effort: if the job completed anyway, fetching its result settles the full quote.
- The quote does not change once issued — the number on the submit response is what a completed request charges.
By the megapixel (most image models including the default): the first output megapixel is $0.03 and each extra megapixel $0.015, rounded to the nearest megapixel with a minimum of 1 — so a 512×512 or 1024×1024 image costs $0.03, and a 1920×1080 image $0.045. Per-image models publish a flat unit_price_usd per image (for example $0.08 or $0.15), with per-resolution and add-on multipliers described in the model's pricing_note. Edit models bill the output side only; the input image's megapixels are not charged today.
#Failures and cancellation
A generation that fails is a normal terminal state, not an HTTP error: the status settles with "status": "COMPLETED", an error string, error_type: "media_failed" and cost_usd: 0. Treat any settled failure as retryable.
Cancel with:
curl https://api.radienthq.com/v1/tools/media/cancel \
-H "Authorization: Bearer $RADIENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"request_id": "01a12193-1b1f-7ec0-a0cf-52e3e7b31a0d"}'
{"request_id":"01a12193-1b1f-7ec0-a0cf-52e3e7b31a0d","model":"…","status":"CANCELLED"}
- Cancelling is idempotent — repeating it returns the same response.
- Cancelling an already completed request is a 409
media_already_completed. - Cancellation asks the generation service to stop; it is not a guarantee. Measured in practice: jobs cancel while queued and, when the service completes one anyway, the result fetch charges the quote.
- The cancel answer reports where settlement stands: while a charge can still settle,
costcarries thequoteand a top-levelnotecarries “If the upstream job completes, the quoted charge will settle; poll /status.”; once settled,costreports thebilledfigure andnoteis absent. - There is no restart or resume endpoint. To retry, submit a new request — a new
request_id, a new quote.
#Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | media_rejected | The request could not be priced or accepted: unknown model, unknown image_size, a count or duration outside the model's range, or an unpriced parameter. Nothing ran, nothing was charged. |
| 400 | – | Malformed JSON or a missing request_id on status/result. |
| 401 | – | Missing or invalid credentials. |
| 402 | – | Insufficient credits — see Radient Pass. |
| 404 | media_not_found | Unknown request id, or a request that belongs to another account. The two answer identically by design. |
| 409 | media_already_completed | Cancel of a completed request. |
| 413 | payload_too_large | Submission body over 1 MiB; details.limit_bytes carries the limit. |
| 429 | media_rate_limited | The generation service is busy; retry shortly. |
| 503 | media_unavailable | The generation service is temporarily unavailable — also returned when a submission cannot be reserved, refused before any generation runs. |
Real bodies (… marks text trimmed in this page):
{"code":"media_rejected","error":"This generation request could not be priced or accepted."}
{"code":"media_not_found","error":"This generation request does not exist."}
{"code":"media_already_completed","error":"This generation request has already completed."}
{"code":"payload_too_large","details":{"limit_bytes":1048576},"error":"…"}
{"code":"media_unavailable","error":"Media generation is temporarily unavailable."}
{"error":"missing Authorization header"}
{"error":"insufficient credits"}
#Legacy image routes
Three routes predate the media API. They forward to the same engine and keep their old response shape, so existing integrations keep working — new work should use /v1/tools/media/*, which has cleaner error semantics and the same prices.
| Legacy route | What it does |
|---|---|
POST /v1/tools/images/generate | Submit; body fields prompt (required, 1–1000 chars), num_images (1–10), image_size, source_url (image-to-image), strength, seed, num_inference_steps, guidance_scale. sync_mode is accepted and ignored — the response has always been a queue handle, never an image. |
GET /v1/tools/images/status?request_id=… | Status, with cost_usd / units / unit and queue_position added where known. |
GET /v1/tools/images/providers | Lists the available image service ids. |
Differences from the media routes worth knowing: the legacy status response returns the images array directly once complete; the additive cost_usd / units / unit fields are absent until the request settles (0 is a settled zero, absent means not yet settled); and the legacy routes surface a rejected request and an unknown request id as HTTP 500 (with media_rejected / media_not_found in the body) rather than 400 / 404.
For the full endpoint list, see the API Reference.