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

# Asynchronous tasks

> Create and monitor long-running generation tasks.

Video, music, and some image models use asynchronous tasks. The create endpoint returns a task ID before the generated asset is ready.

<Steps>
  <Step title="Create a task">
    Call the generation endpoint and save the returned task ID. Depending on the provider, the field may be named `id`, `task_id`, or `taskId`.
  </Step>

  <Step title="Poll the task">
    Call the matching task endpoint until the task reaches a terminal state. Start with a two-to-five-second interval and use exponential backoff.
  </Step>

  <Step title="Read the result">
    Read the asset URL after the task succeeds. Download or copy the asset promptly because generated URLs may expire.
  </Step>
</Steps>

## Recommended polling strategy

```javascript theme={null}
const successStates = new Set(["succeeded", "success", "completed"]);
const failureStates = new Set(["failed", "failure", "cancelled", "canceled"]);
const retryableStatuses = new Set([429, 502, 503, 504]);

async function pollTask(taskUrl, timeoutMs = 5 * 60 * 1000) {
  const deadline = Date.now() + timeoutMs;
  let delay = 2000;

  while (Date.now() < deadline) {
    const response = await fetch(taskUrl, {
      headers: { Authorization: `Bearer ${process.env.MAXAPI_API_KEY}` },
    });

    if (!response.ok && !retryableStatuses.has(response.status)) {
      throw new Error(`Task query failed with HTTP ${response.status}`);
    }

    if (response.ok) {
      const task = await response.json();
      const status = String(task.status).toLowerCase();

      if (successStates.has(status)) return task;
      if (failureStates.has(status)) {
        throw new Error(task.fail_reason || `Task ended with status: ${status}`);
      }
    }

    const jitter = Math.floor(Math.random() * 500);
    await new Promise((resolve) => setTimeout(resolve, delay + jitter));
    delay = Math.min(Math.round(delay * 1.5), 10000);
  }

  throw new Error("Task polling timed out");
}
```

<Warning>
  Status fields and values differ between providers. Follow the schema for the create and query endpoints instead of sharing unadapted status logic across models.
</Warning>

## Production checklist

* Set a maximum polling duration.
* Retry `429`, `502`, `503`, and `504` responses with exponential backoff and jitter.
* Retry only the query request. Confirm idempotency before retrying a create request.
* Store the task ID, model ID, and creation time so an interrupted worker can resume.
* Download generated assets promptly instead of treating temporary URLs as permanent storage.

## Troubleshoot task failures

| Symptom                | Action                                                                           |
| ---------------------- | -------------------------------------------------------------------------------- |
| Query returns `404`    | Confirm that the task ID and query endpoint belong to the same API family        |
| Status does not change | Poll less often and check the endpoint page for queue states                     |
| Task fails             | Log the failure reason, request ID, model ID, and task ID, but never the API key |
| Result URL expired     | Query the task again and copy new results to permanent storage promptly          |
