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

# Async jobs

> How generation jobs are created, polled, and completed.

Generation is **asynchronous**. You submit a job, receive an id immediately, then
poll until the job reaches a terminal state.

## Lifecycle

A job moves through these statuses:

```
pending → starting → processing → completing → succeeded
                                              ↘ failed
                                              ↘ timeout
```

`succeeded`, `failed`, and `timeout` are **terminal** — stop polling once you see
one. On `succeeded`, the result is available on the status response (an `images`
array for image jobs, a `video_url` for video jobs).

## Create, then poll

Image and video jobs are both created at per-model endpoints
(`POST /v1/images/{model-name}` and `POST /v1/videos/{model-name}`), then polled
at the matching status endpoint.

<CodeGroup>
  ```bash Image theme={null}
  # 1. Create — endpoint is the model slug
  curl -X POST https://api.budgetpixel.com/v1/images/flux-2-pro \
    -H "Authorization: Bearer $BUDGETPIXEL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"prompt":"...","num_images":1}'
  # -> { "id": "img_a1b2c3d4e5f6", "status": "pending" }

  # 2. Poll
  curl https://api.budgetpixel.com/v1/images/img_a1b2c3d4e5f6 \
    -H "Authorization: Bearer $BUDGETPIXEL_API_KEY"
  ```

  ```bash Video theme={null}
  # 1. Create — endpoint is the model slug
  curl -X POST https://api.budgetpixel.com/v1/videos/seedance-1.5-pro \
    -H "Authorization: Bearer $BUDGETPIXEL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"prompt":"...","length_seconds":5}'
  # -> { "id": "...", "status": "pending" }

  # 2. Poll
  curl https://api.budgetpixel.com/v1/videos/{id} \
    -H "Authorization: Bearer $BUDGETPIXEL_API_KEY"
  ```
</CodeGroup>

<Note>
  Job ids are opaque — treat them as strings and poll each at its own type's status
  endpoint (`/v1/images/{id}` or `/v1/videos/{id}`). A reasonable polling interval
  is every few seconds — images typically finish in seconds, video in a few minutes.
</Note>

## Errors

All errors share one envelope so you can branch on `type` and `code`:

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "model_not_available",
    "message": "Model 'xyz' is not available via the API."
  }
}
```

| Status | When                                                     |
| ------ | -------------------------------------------------------- |
| 400    | Malformed request, missing/`model_not_available` model.  |
| 401    | Missing or invalid API key.                              |
| 403    | Plan not enabled, ownership, or restriction.             |
| 404    | Job not found (or not yours).                            |
| 429    | Rate or queue limit reached — retry after a short delay. |
