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

# Poll Job Status

> Learn how to check the status of a Whisul generation job, handle all three job states, and retrieve your finished song and cover art.

Whisul generates music asynchronously. When you call `POST /generate`, the API immediately returns a `job_id` — but the song itself isn't ready yet. To get the finished track, you call `GET /jobs/{job_id}` repeatedly until the job reaches a terminal state. This guide explains the job lifecycle, shows you how to poll correctly, and tells you what to do with the result once generation is complete.

## How the job lifecycle works

Every generation job moves through one of three states:

* **running** — Whisul is actively generating your song. Keep polling.
* **completed** — Generation finished successfully. Your song is ready.
* **failed** — The job could not be completed. No audio was produced.

The `poll_url` field in the `POST /generate` response contains the exact path to poll — you can use it directly without constructing the URL yourself.

## Poll the job endpoint

Call `GET /jobs/{job_id}` with the same `Authorization` header you used to submit the job.

```bash cURL theme={null}
curl --request GET \
  --url https://whisul.com/api/jobs/81fa5ff7-6197-4c24-8062-c0ff8b62d58d \
  --header "Authorization: Bearer YOUR_API_KEY"
```

<Note>
  Poll every **5–10 seconds**. Polling more frequently than that won't speed up generation and may trigger rate limiting. Most tracks complete within a few minutes.
</Note>

## Job status responses

<Tabs>
  <Tab title="Running">
    While the song is being generated, the response looks like this:

    ```json Response theme={null}
    {
      "status": "running",
      "prompt": "a relaxing lo-fi hip hop beat",
      "result": null,
      "error": null,
      "started_at": "2026-02-17 15:39:34"
    }
    ```

    `result` is `null` and `error` is `null`. Continue polling until `status` changes.
  </Tab>

  <Tab title="Completed">
    When generation finishes, `status` becomes `"completed"` and the `result` object is populated:

    ```json Response theme={null}
    {
      "status": "completed",
      "prompt": "a relaxing lo-fi hip hop beat",
      "result": {
        "title": "Midnight Drizzle",
        "bpm": 85,
        "duration": 183,
        "song_url": "https://cdn.whisul.com/songs/81fa5ff7.mp3",
        "image_url": "https://cdn.whisul.com/art/81fa5ff7.png",
        "tags": "lo-fi, hip hop, relaxing, jazzy"
      },
      "error": null,
      "started_at": "2026-02-17 15:39:34",
      "finished_at": "2026-02-17 15:41:12"
    }
    ```

    The `result` object contains:

    | Field       | Type   | Description                                                    |
    | ----------- | ------ | -------------------------------------------------------------- |
    | `title`     | string | AI-generated title for the track.                              |
    | `bpm`       | number | Tempo of the generated song in beats per minute.               |
    | `duration`  | number | Length of the track in seconds.                                |
    | `song_url`  | string | Direct URL to the audio file.                                  |
    | `image_url` | string | Direct URL to the generated cover art.                         |
    | `tags`      | string | Comma-separated descriptive genre and mood tags for the track. |
  </Tab>

  <Tab title="Failed">
    If generation could not be completed, `status` is `"failed"` and the `error` field describes what went wrong:

    ```json Response theme={null}
    {
      "status": "failed",
      "prompt": "a relaxing lo-fi hip hop beat",
      "result": null,
      "error": "Generation failed due to an internal error.",
      "started_at": "2026-02-17 15:39:34",
      "finished_at": "2026-02-17 15:41:05"
    }
    ```

    A failed job does not produce audio. You can retry by submitting a new request to `POST /compose` with the same or a revised prompt. See the [error handling guide](/guides/error-handling) for more on failed jobs and when to retry.
  </Tab>
</Tabs>

## Using the song and cover art

Once a job reaches `"completed"` status, you can use the URLs in the `result` object directly:

* **`song_url`** — Link to or stream the generated audio file. You can download it, embed it in a player, or serve it to your users.
* **`image_url`** — Link to the generated cover art image. Use it alongside the audio as album artwork.

Both URLs are available immediately when the job completes. There is no additional request needed to retrieve the files.
