---
title: Submit a generation
description: POST /v1/models/{model} — enqueue an image, video or audio generation, get a request_id, and send the request straight from this page.
icon: Send
---

Enqueue an image, video or audio generation. Fill in the parameters below and send the request with your key.

## Playground

<Callout type="info" title="Use a test key here">
Paste a `clipia_test_…` key into the form below: it charges no credits and calls no models, and the response has the same shape. The form keeps the key in this browser, so never enter a live `clipia_live_…` key here: it is a server-side secret. See [Sandbox](/en/docs/getting-started/sandbox).
</Callout>

<ApiPlayground path="/v1/models/{model}" method="post" />

`POST /v1/models/:model` enqueues a generation and immediately returns a `request_id`, links to its status and result, and the reserved cost in credits. The key needs the `generate` permission.

<Method name="POST" path="/v1/models/:model" />

## Request

**Path parameter**

<TypeTable
  type={{
    model: { type: 'string', description: 'Model slug, e.g. nano-banana-2 or seedance-2-fast-i2v. Lowercase letters, digits and hyphens. See GET /v1/models for the list.', required: true }
  }}
/>

**Headers**

<TypeTable
  type={{
    Authorization: { type: 'string', description: 'Bearer clipia_live_… (or the Key scheme, or the X-Api-Key header)', required: true },
    'Content-Type': { type: 'string', description: 'application/json', required: true },
    'Idempotency-Key': { type: 'string', description: 'Up to 255 characters; a UUID v4 is recommended. Makes retries safe for 24 hours.' }
  }}
/>

**Body**

<TypeTable
  type={{
    input: { type: 'object', description: 'Generation parameters. The shape depends on the model — see input_schema in GET /v1/models/:model. Up to 100,000 bytes and 8 levels of nesting.', required: true },
    webhook_url: { type: 'string', description: 'HTTPS address that receives a POST when the job finishes. Internal and private addresses are rejected.' }
  }}
/>

Common keys inside `input`: `prompt`, `image_url`, `image_urls`, `aspect_ratio`, `duration`, `resolution` — but every model has its own set: pass what the model declares in `input_schema` (what happens to an extra key is [below](#warnings)). If the model works from a source image or video, `prompt` is optional; otherwise it is required.

## Example

<Tabs items={['cURL', 'TypeScript', 'Python']}>
<Tab value="cURL">

```bash
curl -X POST https://api.clipia.ai/v1/models/nano-banana-2 \
  -H "Authorization: Bearer $CLIPIA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f3a1c7e-2b4d-4e6f-9a01-23456789abcd" \
  -d '{
    "input": {
      "prompt": "a sunset over mountains, cinematic",
      "aspect_ratio": "16:9"
    },
    "webhook_url": "https://your-server.com/clipia/webhook"
  }'
```

</Tab>
<Tab value="TypeScript">

```ts
import { createClient } from 'clipia-ai';

const clipia = createClient({ apiKey: process.env.CLIPIA_KEY! });

// The Idempotency-Key is added automatically.
const job = await clipia.queue.submit('nano-banana-2', {
  input: { prompt: 'a sunset over mountains, cinematic', aspect_ratio: '16:9' },
  webhookUrl: 'https://your-server.com/clipia/webhook',
});

console.log(job.request_id, job.cost);
```

</Tab>
<Tab value="Python">

```python
import os
from clipia import Clipia

client = Clipia(api_key=os.environ["CLIPIA_KEY"])

# The Idempotency-Key is added automatically.
job = client.submit(
    "nano-banana-2",
    {"prompt": "a sunset over mountains, cinematic", "aspect_ratio": "16:9"},
    webhook_url="https://your-server.com/clipia/webhook",
)

print(job.request_id, job.cost)
```

</Tab>
</Tabs>

**Response `200`**

```json
{
  "request_id": "764cabcf-b745-4b3e-ae38-1200304cf45b",
  "status": "IN_QUEUE",
  "queue_position": 0,
  "status_url": "https://api.clipia.ai/v1/requests/764cabcf-b745-4b3e-ae38-1200304cf45b/status",
  "response_url": "https://api.clipia.ai/v1/requests/764cabcf-b745-4b3e-ae38-1200304cf45b",
  "cost": 12
}
```

**Response fields**

<TypeTable
  type={{
    request_id: { type: 'string', description: 'Job identifier (UUID)' },
    status: { type: 'string', description: 'IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED or CANCELED' },
    queue_position: { type: 'integer', description: 'Deprecated: always 0, the queue position is not computed. Kept for compatibility, do not branch on it — see Versioning and compatibility' },
    status_url: { type: 'string', description: 'Link for checking the status' },
    response_url: { type: 'string', description: 'Link for fetching the result' },
    cost: { type: 'integer', description: 'Credits reserved for the job' },
    warnings: { type: 'array', description: 'Input parameters the model will not apply. Present only when such parameters were found — see below.' }
  }}
/>

The credits in `cost` are reserved on submit, charged on success, and refunded in full if the generation fails. To see the price up front, call [cost estimation](/en/docs/api-reference/estimate) with the same `input`.

<Callout type="info" title="Retries">
With an `Idempotency-Key` header, repeating the same body within 24 hours returns the same job without a second charge. Without the header, identical requests that arrive within 20 seconds are also merged into one job. In both cases the replayed response carries the `Idempotent-Replayed: true` header. See [Idempotency](/en/docs/api-reference/idempotency).
</Callout>

## Ignored parameters [#warnings]

Every model has its own set of parameters. If `input` contains a key the model will not apply — a typo, a parameter of another model, a key from an outdated example — the request is accepted as before and is not rejected. The response, however, also carries a `warnings` array with one entry per such key:

```json
{
  "request_id": "5b0c1f7e-2a53-4a0e-9d0b-6f1f4d0f2c11",
  "status": "IN_QUEUE",
  "queue_position": 0,
  "status_url": "https://api.clipia.ai/v1/requests/5b0c1f7e-2a53-4a0e-9d0b-6f1f4d0f2c11/status",
  "response_url": "https://api.clipia.ai/v1/requests/5b0c1f7e-2a53-4a0e-9d0b-6f1f4d0f2c11",
  "cost": 12,
  "warnings": [
    {
      "code": "unsupported_parameter",
      "parameter": "duration",
      "message": "Parameter \"duration\" is not supported by model \"nano-banana-2\" and was ignored. Parameters declared by this model: aspect_ratio, prompt, resolution. Full schema: GET /v1/models/nano-banana-2."
    }
  ]
}
```

<TypeTable
  type={{
    code: { type: 'string', description: 'unknown_parameter — the name is not known to the server (for example, a typo); unsupported_parameter — the name exists, but this model neither declares nor uses it. Treat an unfamiliar value as "not applied".' },
    parameter: { type: 'string', description: 'The key name from input' },
    message: { type: 'string', description: 'An explanation in English listing the parameters the model declares' }
  }}
/>

- When every parameter is accepted, the response has no `warnings` field. The array holds at most 20 entries.
- Another spelling of a declared parameter that the server maps to the right name itself is not a warning.
- A warning appears only where the server is certain the parameter will not be applied. Its absence does not confirm that every parameter took effect: check `input` against the model's `input_schema`.
- The same `warnings` are returned by the [cost estimate](/en/docs/api-reference/estimate), by a test key, and by a retry with the same `Idempotency-Key`.
- In MCP the same situation is a refusal before the generation starts: see [MCP tools](/en/docs/mcp/tools#unsupported-parameters).

## Submit errors [#submit-errors]

Errors use the common `{"error": {"type", "code", "message"}}` format — see [Errors](/en/docs/api-reference/errors). For `400` errors, the specific reason is in the `message` field.

| HTTP | `code` | `message` | When |
|------|--------|-----------|------|
| 400 | `invalid_request` | `invalid_model` | the slug doesn't match the format: lowercase letters, digits, hyphens |
| 400 | `invalid_request` | `prompt_required` | no `prompt` and no source image or video |
| 400 | `invalid_request` | `input_too_large` / `input_too_deep` | `input` exceeds 100,000 bytes or 8 levels of nesting |
| 400 | `invalid_request` | `idempotency_key_too_long` | `Idempotency-Key` is longer than 255 characters |
| 400 | `invalid_request` | `webhook_url_invalid` | `webhook_url` isn't HTTPS or points to an internal address |
| 400 | `invalid_request` | `Too many media URLs…` / `Too many items in <field>…` | more than 30 source file URLs in one list or more than 60 distinct URLs in the request — rejected before the addresses are checked and credits are reserved |
| 400 | `invalid_request` | `media_url_invalid` | a source file URL points to an internal address or uses a scheme other than `http(s)`. For HeyGen Video the server copies files from external URLs into its own storage before reserving credits; if a file can't be downloaded or has the wrong type or size, the message names the field and the reason: `media_url_invalid: <field>: <reason>` |
| 400 | `invalid_request` | `num_images greater than 1 is not supported: send one request per image.` | `input` contains `num_images` greater than 1 — one job produces one image |
| 400 | `invalid_request` | text naming the model limit | the narration text is shorter or longer than the model's `text_limits`; no credits are reserved |
| 402 | `insufficient_credits` | — | not enough credits |
| 402 | `subscription_required` | — | an active subscription is required: both the Public API condition for live keys and credits frozen until the plan is renewed |
| 402 | `insufficient_quota` | — | this submit would take the key's spending over its budget, see [Managing keys](/en/docs/api-reference/keys) |
| 403 | `insufficient_scope` | — | the key lacks the `generate` permission |
| 404 | `not_found` | `model_not_found` | no model with this slug, or it isn't available |
| 409 | `request_in_progress` / `idempotency_key_reuse` | — | see [Idempotency](/en/docs/api-reference/idempotency) |
| 429 | `rate_limit_exceeded` / `concurrency_limit_exceeded` | — | see [Rate limits](/en/docs/api-reference/rate-limits) |
| 503 | `service_unavailable` | — | for HeyGen Video the server can't copy files from external URLs right now; no credits are reserved, retry after a pause |
