Skip to main content

Blog

Flux API First Call: Model Selection, Pricing, and the Async Result

Flux image generation guide cover

To call the Flux API through PiAPI, choose a runtime, send a task, and poll its ID until it completes or fails. This guide focuses on the first integration and the documented differences among Schnell, Dev, and Dev-advanced.

The request examples follow the public documentation checked on September 22, 2026. They were not executed for this article, and the guide includes no generated results or performance benchmark.

1. Choose the runtime and check its conditions

Runtime modelBase generation priceDocumented conditions
Qubico/flux1-schnell$0.0015 per imageApache 2.0; commercial use documented. Supports batches of 1–4 images.
Qubico/flux1-dev$0.015 per imageThe public PiAPI guide marks Dev as non-commercial.
Qubico/flux1-dev-advanced$0.02 per imageAdvanced controls include LoRA, ControlNet, inpaint, outpaint, and variation. Check the Creator-or-higher plan requirement for advanced features.

All three are listed in the current Flux configuration for text-to-image and image-to-image. Use the Flux model page and machine-readable guide to check current license and plan conditions. The prices above cover base generation; they do not establish a Dev super-resolution rate.

For Schnell, the guide bills batch_size × price. For example, four images at $0.0015 each give $0.006 in base generation charges. This arithmetic uses the listed rate; check the selected operation and your billing record for the final charge.

2. Send a text-to-image request

Get your key from Workspace API keys, configure PIAPI_API_KEY in your environment, and ensure your account has the required credits. The shell examples use curl and jq. Save the following body as flux-request.json:

{
  "model": "Qubico/flux1-schnell",
  "task_type": "txt2img",
  "input": {
    "prompt": "A quiet mountain lake at sunrise, editorial landscape photography",
    "width": 1024,
    "height": 1024
  }
}

The documented width × height limit is 1,048,576 pixels. This 1024 × 1024 request fits that limit. Keep batch_size at its default for the first request; the documented batch range of 1–4 applies to Schnell.

Submit once. This command reads the key from the environment and passes the header through curl configuration on stdin. The response goes into a new receipt file; the command refuses to overwrite an existing receipt.

set -eu
: "${PIAPI_API_KEY:?Set PIAPI_API_KEY in your environment}"
(set -C; : > flux-create.json)
printf 'header = "X-API-KEY: %s"
' "$PIAPI_API_KEY" |
  curl --config - --fail-with-body --silent --show-error     --connect-timeout 10 --max-time 30     --request POST 'https://api.piapi.ai/api/v1/task'     --header 'Content-Type: application/json'     --data-binary @flux-request.json --output flux-create.json
jq -er '.data.task_id | select(type == "string" and length > 0)' flux-create.json

Read data.task_id from the create response and save it. If submission is interrupted or no ID is returned, reconcile the task in your account before creating another request; a timeout does not establish that the server rejected it.

3. Poll the saved task with a bounded wait

The documented lifecycle is pending → processing → completed | failed. Save the following as poll-flux.sh and run sh poll-flux.sh YOUR_SAVED_TASK_ID. It performs at most 60 polls with a five-second interval and a 30-second limit for each HTTP request. Those are client limits, not service latency promises.

set -eu
: "${PIAPI_API_KEY:?Set PIAPI_API_KEY in your environment}"
flux_task_id=${1:?Pass the saved task ID}
case "$flux_task_id" in
  *[!A-Za-z0-9_-]*|'') echo 'Invalid task ID' >&2; exit 1 ;;
esac
flux_attempt=0
while [ "$flux_attempt" -lt 60 ]; do
  flux_response=$(printf 'header = "X-API-KEY: %s"
' "$PIAPI_API_KEY" |
    curl --config - --fail-with-body --silent --show-error       --connect-timeout 10 --max-time 30       "https://api.piapi.ai/api/v1/task/$flux_task_id")
  flux_status=$(printf '%s' "$flux_response" | jq -er '.data.status')
  case "$flux_status" in
    completed)
      printf '%s' "$flux_response" |
        jq -er '.data.output.image_url | select(type == "string" and length > 0)'
      exit 0 ;;
    failed) echo 'Task failed; inspect it using the saved task ID' >&2; exit 1 ;;
    pending|processing) ;;
    *) echo 'Unexpected task status; inspect the saved task' >&2; exit 1 ;;
  esac
  flux_attempt=$((flux_attempt + 1))
  if [ "$flux_attempt" -lt 60 ]; then sleep 5; fi
done
echo 'Polling budget expired; keep the task ID and resume later' >&2
exit 2

On completed, read data.output.image_url. A failed state is terminal. The script stops on an unexpected response, HTTP failure, or an exhausted polling budget; resume with the saved ID when appropriate, without creating another task.

4. Submit an image-to-image task

Use the same POST /api/v1/task endpoint with task_type: "img2img". The public wire format uses input.image for the reference URL:

{
  "model": "Qubico/flux1-dev",
  "task_type": "img2img",
  "input": {
    "prompt": "Turn the boat red",
    "image": "https://example.com/reference.jpg",
    "denoise": 0.7
  }
}

Replace the example URL with an accessible image you may process. This example selects Dev, so its documented non-commercial condition applies. Submit it with a new receipt filename and keep the reference image, prompt, runtime, and task ID together.

Choose by contract, then evaluate your own results

  • Use Schnell when its license, base controls, and listed rate meet your requirements.
  • Use Dev when your use fits its documented non-commercial condition.
  • Use Dev-advanced when you need its advanced controls and the required plan. Check the operation-specific documentation before adding LoRA or ControlNet fields.

You can explore the controls in the Flux workspace or continue with the Flux text-to-image documentation. Keep the chosen model and operation explicit in each request so you can compare your own outputs and costs.