Create a task
The create endpoint shared by every model: parameters, request examples, response and every error.
https://api.nanoapi.ai/v1/tasksEvery generation is a task. Every model creates tasks with this endpoint, picked with model. Then retrieve the task or list tasks for the result, or wait for the webhook. Every response uses the { code, message, data } envelope, see Response format.
Freezes the cost from your available balance; the task is generated in the background and can't be cancelled. The endpoint usually responds in 1–3 seconds.
Parameters
The parameters below work the same for every model. The rest (such as mode, video_url and image_urls) depend on the model and are on its page: Genjutsu 1.0.
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | The model, such as genjutsu-1.0. Each model's page lists it; GET /v1/products returns them in models. |
num_outputs | integer | No | Only 1 for now (the default). |
webhook_url | string | No | Notified when the task succeeds or fails: https, up to 2048 characters, not a private, loopback or reserved address. See Webhooks. |
A missing or unknown model returns 400 invalid_request with param: "model", and so does any parameter the model doesn't accept. Every error is listed under Errors below.
Request example
Using Genjutsu 1.0's motion transfer as an example:
curl https://api.nanoapi.ai/v1/tasks \
-H "Authorization: Bearer $NANOAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "genjutsu-1.0",
"mode": "motion_control",
"image_urls": ["https://example.com/person.png"],
"video_url": "https://example.com/dance.mp4",
"resolution": "720p",
"webhook_url": "https://example.com/hooks/nanoapi"
}'Response
On success you get 200 with the task in short form as data, with only these fields. Get the parameters, results, duration and the rest by retrieving the task.
{
"code": 200,
"message": "success",
"data": {
"object": "task",
"id": "tk_01jb8z5q4w3e2r1t0y9u8i7o6p",
"model": "genjutsu-1.0",
"status": "queued",
"billing": {
"currency": "USD",
"frozen": "4.095",
"charged": "0.00",
"refunded": "0.00",
"billed_seconds": null
},
"error": null,
"created_at": "2026-10-22T08:30:00Z"
}
}data.status | Meaning | Next |
|---|---|---|
queued | All good: the task is queued and billing.frozen is the amount frozen. | Keep data.id; wait for the webhook or retrieve the task. |
failed | The task was created but rejected as soon as it was submitted to the generation service (usually content_policy). The frozen amount has been returned; error has the reason. | Handle it by data.error.type, see Failure reasons. |
Errors
A failed request returns 4xx / 5xx: no task was created and nothing was frozen. For example, not enough balance:
{
"code": 402,
"message": "Your available balance is not enough to create this task.",
"error": {
"type": "insufficient_balance",
"param": null,
"details": { "required": "4.095", "available": "3.00" }
},
"request_id": "rq_01jb8z9x2c4v6b8n0m1q3w5e7r"
}Branch on error.type; error.param names the parameter at fault and the fields of error.details are listed below.
error.type | Status | When | What to do |
|---|---|---|---|
invalid_request | 400 | A parameter is wrong: model is missing or doesn't exist (param is "model"); a required parameter is missing or has the wrong type or value, for example image_urls isn't 1–10 images or resolution isn't 480p / 720p / 1080p; or you sent a parameter the model doesn't accept. param names the parameter. | Fix it using param and message. Each model's parameters are on its page, for example Genjutsu 1.0. |
invalid_media | 400 | The source video can't be read: the link doesn't open or has expired, isn't a public address, isn't a video, or is in an unsupported format (mp4, mov, webm and mkv are supported). details.reason says why (in English). | Use a different, directly downloadable video URL. |
video_too_long | 400 | The source video is longer than 30 seconds. details.video_duration is its length in seconds, details.max_seconds the limit. | Trim it to 30 seconds or less and create the task again. |
video_too_short | 400 | The replace_object source video is shorter than 4 seconds. details.video_duration is its length in seconds, details.min_seconds the minimum. | Use a video of at least 4 seconds. |
invalid_api_key | 401 | The key is missing or malformed, doesn't exist, was deleted or has been reset. | Use a valid key, see Authentication. |
insufficient_balance | 402 | Your available balance can't cover the amount to freeze. details.required is what this task needs, details.available your available balance; details.required is null when your available balance is 0. | Top up, then create the task again. |
account_suspended | 403 | Your account has been suspended. | Contact support@nanoapi.ai. |
rate_limited | 429 | Too many creates: at most 20 tasks per 10 seconds per account, shared by all your keys (the website playground counts too). There's no limit on tasks in progress. | Retry after Retry-After seconds. |
internal_error | 500 | Something went wrong on our side. | Yes, retry later. |
service_unavailable | 503 | The price can't be calculated right now. No task was created. | Retry after Retry-After seconds (usually 10). |
If the source video can't be opened or is too long or too short, the create endpoint returns one of the 400s above: no task is created and nothing is frozen. Reference images are only read when generation runs: if one of them is a problem, the task is created and fails later with invalid_media, see Failure reasons.