チュートリアル

FLUX 1.1 Pro API Python入門:5分で画像生成を実装する方法

AI API Playbook · · 15 分で読めます
FLUX 1.1 Pro API Python入門:5分で画像生成を実装する方法

FLUX 1.1 Pro API Python チュートリアル:5分以内で画像生成を始める

3つの重要数字: 推論レイテンシ約8〜15秒/枚、価格$0.04/枚(1024×1024)、Prompthero/ELO評価でFLUX 1.0 Proより最大20%品質向上


前提条件

このチュートリアルを実行するには以下が必要です。

アカウント・APIキー

  • Black Forest Labs API または Replicate のアカウント
  • BFL直接API経由の場合:https://api.us1.bfl.ai エンドポイントを使用
  • Replicate経由の場合:black-forest-labs/flux-1.1-pro モデルID

Python環境

  • Python 3.9以上
  • 以下のパッケージをインストール:
# 必要パッケージのインストール
pip install requests python-dotenv Pillow

# Replicate経由を使う場合はこちらも追加
pip install replicate

環境変数の設定

プロジェクトルートに .env ファイルを作成:

# .env ファイル(Gitにコミットしないこと)
BFL_API_KEY=your_bfl_api_key_here
REPLICATE_API_TOKEN=your_replicate_token_here

.gitignore.env を追加することを忘れずに。APIキーのハードコードは本番環境で絶対に避ける。


エンドポイントと認証の設定

FLUX 1.1 ProはBlack Forest Labs(BFL)の直接APIとReplicateの両方から利用できる。本チュートリアルではBFL直接APIをメインに扱い、Replicate経由のコードも補足する。

BFL APIは非同期ポーリング方式を採用している。リクエストを送るとrequest_idが返り、その後別のエンドポイントで結果をポーリングする。これはタイムアウトを防ぐための設計上の選択であり、長時間実行ジョブに適している。

# auth_check.py — 認証とエンドポイント接続を確認するスクリプト
import os
import requests
from dotenv import load_dotenv

# .envファイルから環境変数を読み込む
load_dotenv()

API_KEY = os.getenv("BFL_API_KEY")
BASE_URL = "https://api.us1.bfl.ai/v1"

def check_auth() -> bool:
    """
    APIキーが有効かどうかを確認する。
    実際のリクエストを送らずに接続を検証するための関数。
    """
    if not API_KEY:
        raise ValueError("BFL_API_KEY が環境変数に設定されていません")
    
    # 認証ヘッダーの構成
    # BFL APIはBearer方式ではなく x-key ヘッダーを使用する点に注意
    headers = {
        "x-key": API_KEY,
        "Content-Type": "application/json"
    }
    
    # テスト用に最小パラメータでリクエストを試みる
    test_payload = {
        "prompt": "test",
        "width": 256,
        "height": 256,
        "steps": 1  # ステップ数を最小にしてコストを抑える
    }
    
    response = requests.post(
        f"{BASE_URL}/flux-pro-1.1",
        headers=headers,
        json=test_payload
    )
    
    if response.status_code == 200:
        print("✓ 認証成功")
        print(f"  Request ID: {response.json().get('id')}")
        return True
    elif response.status_code == 401:
        print("✗ 認証失敗:APIキーを確認してください")
        return False
    else:
        print(f"✗ 予期しないエラー:HTTP {response.status_code}")
        print(f"  レスポンス:{response.text}")
        return False

if __name__ == "__main__":
    check_auth()

基本実装:最初の画像を生成する

以下のコードは動作する最小実装である。コピーしてそのまま実行できる。

# generate_basic.py — FLUX 1.1 Pro で画像を生成する基本実装
import os
import time
import requests
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("BFL_API_KEY")
BASE_URL = "https://api.us1.bfl.ai/v1"

def generate_image(
    prompt: str,
    width: int = 1024,
    height: int = 1024,
    steps: int = 25,
    guidance: float = 3.5,
    output_path: str = "output.jpg"
) -> str:
    """
    FLUX 1.1 Pro API で画像を生成してローカルに保存する。
    
    Returns:
        保存した画像ファイルのパス
    """
    headers = {
        "x-key": API_KEY,
        "Content-Type": "application/json"
    }
    
    # ステップ1:生成リクエストを送信して request_id を取得
    payload = {
        "prompt": prompt,
        "width": width,
        "height": height,
        "steps": steps,
        "guidance": guidance,
        # safety_tolerance: 0=最も制限、6=最も緩い。デフォルト2が推奨
        "safety_tolerance": 2,
        "output_format": "jpeg"  # "jpeg" または "png"
    }
    
    print(f"生成リクエスト送信中...")
    response = requests.post(
        f"{BASE_URL}/flux-pro-1.1",
        headers=headers,
        json=payload,
        timeout=30  # ネットワーク接続タイムアウト(生成完了とは別)
    )
    response.raise_for_status()
    
    request_id = response.json()["id"]
    print(f"Request ID: {request_id}")
    
    # ステップ2:結果をポーリングする
    # BFL APIは非同期方式のため、完了まで繰り返し確認が必要
    result_url = f"{BASE_URL}/get_result"
    
    for attempt in range(60):  # 最大60回 = 約120秒待機
        time.sleep(2)  # 2秒間隔でポーリング(過剰なリクエストを避ける)
        
        result_response = requests.get(
            result_url,
            headers=headers,
            params={"id": request_id},
            timeout=10
        )
        result_response.raise_for_status()
        result_data = result_response.json()
        
        status = result_data.get("status")
        print(f"  状態確認 [{attempt + 1}/60]: {status}")
        
        if status == "Ready":
            # 画像URLが返ってくるので、バイナリとしてダウンロード
            image_url = result_data["result"]["sample"]
            image_response = requests.get(image_url, timeout=30)
            
            with open(output_path, "wb") as f:
                f.write(image_response.content)
            
            print(f"✓ 画像を保存しました: {output_path}")
            return output_path
        
        elif status == "Error":
            raise RuntimeError(f"生成エラー: {result_data}")
        
        # "Pending" または "Processing" の場合はループを継続
    
    raise TimeoutError(f"120秒以内に生成が完了しませんでした (request_id: {request_id})")


if __name__ == "__main__":
    result = generate_image(
        prompt="A photorealistic mountain landscape at golden hour, "
               "sharp focus, 8K, cinematic lighting",
        width=1024,
        height=1024,
        output_path="mountain_test.jpg"
    )
    print(f"完了: {result}")

このコードを実行すると、通常8〜15秒で mountain_test.jpg がカレントディレクトリに保存される。


APIパラメータ リファレンス

パラメータ名デフォルト有効範囲影響する要素
promptstring必須最大10,000文字生成される画像の内容・スタイル全般
widthinteger1024256〜1440(64の倍数)出力解像度の横幅、コストに影響
heightinteger1024256〜1440(64の倍数)出力解像度の縦幅、コストに影響
stepsinteger251〜50生成品質と推論時間のトレードオフ
guidancefloat3.52.0〜5.0promptへの忠実度。高いほど指示に従うが多様性が下がる
safety_toleranceinteger20〜6コンテンツフィルタの厳格度
seedintegernull0〜2^32-1同一seedで再現可能な出力を得る
output_formatstring”jpeg""jpeg”, “png”出力ファイル形式。pngは透過対応だがファイルサイズ大
prompt_upsamplingbooleanfalsetrue/falseBFL側でpromptを自動強化する。品質向上するが一貫性が下がる

注意: width × height が大きくなるほどレイテンシとコストが増加する。1024×1024を基準とした場合、1440×1440では約2倍の推論時間が必要になる。


本番向け実装

基本実装をそのまま本番に使うのは危険である。以下のコードはリトライロジック、ログ、バッチ処理、エラーハンドリングを追加した本番対応版である。

# flux_client.py — 本番環境対応の FLUX 1.1 Pro クライアント
import os
import time
import logging
import requests
from pathlib import Path
from dataclasses import dataclass
from typing import Optional
from dotenv import load_dotenv

load_dotenv()

# structuredログを設定(本番ではJSONフォーマットに変更推奨)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger(__name__)


@dataclass
class GenerationConfig:
    """
    生成パラメータをデータクラスで管理する。
    dictより型安全で、IDEの補完が効く。
    """
    prompt: str
    width: int = 1024
    height: int = 1024
    steps: int = 25
    guidance: float = 3.5
    safety_tolerance: int = 2
    seed: Optional[int] = None
    output_format: str = "jpeg"
    prompt_upsampling: bool = False


class FluxProClient:
    """
    FLUX 1.1 Pro API クライアント。
    コネクション管理・リトライ・エラーハンドリングを内包する。
    """
    
    BASE_URL = "https://api.us1.bfl.ai/v1"
    
    # リトライ対象のHTTPステータスコード
    # 429(レート制限)と5xx(サーバーエラー)のみリトライする
    RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
    
    def __init__(
        self,
        api_key: Optional[str] = None,
        max_retries: int = 3,
        poll_interval: float = 2.0,
        max_poll_attempts: int = 60
    ):
        self.api_key = api_key or os.getenv("BFL_API_KEY")
        if not self.api_key:
            raise ValueError("BFL_API_KEY が設定されていません")
        
        self.max_retries = max_retries
        self.poll_interval = poll_interval
        self.max_poll_attempts = max_poll_attempts
        
        # Sessionを再利用してTCPコネクションのオーバーヘッドを削減
        self.session = requests.Session()
        self.session.headers.update({
            "x-key": self.api_key,
            "Content-Type": "application/json"
        })
    
    def _build_payload(self, config: GenerationConfig) -> dict:
        """GenerationConfigをAPIペイロード dictに変換する。"""
        payload = {
            "prompt": config.prompt,
            "width": config.width,
            "height": config.height,
            "steps": config.steps,
            "guidance": config.guidance,
            "safety_tolerance": config.safety_tolerance,
            "output_format": config.output_format,
            "prompt_upsampling": config.prompt_upsampling
        }
        # seedはNoneの場合はペイロードから除外する(APIがnullを許可しないため)
        if config.seed is not None:
            payload["seed"] = config.seed
        return payload
    
    def _submit_request(self, config: GenerationConfig) -> str:
        """
        生成リクエストを送信してrequest_idを返す。
        リトライロジックを含む。
        """
        payload = self._build_payload(config)
        
        for attempt in range(self.max_retries):
            try:
                response = self.session.post(
                    f"{self.BASE_URL}/flux-pro-1.1",
                    json=payload,
                    timeout=30
                )
                
                if response.status_code == 200:
                    request_id = response.json()["id"]
                    logger.info(f"リクエスト送信成功: {request_id}")
                    return request_id
                
                elif response.status_code in self.RETRYABLE_STATUS_CODES:
                    wait_time = 2 ** attempt  # 指数バックオフ: 1s, 2s, 4s
                    logger.warning(
                        f"HTTP {response.status_code} — "
                        f"{wait_time}秒後にリトライ ({attempt + 1}/{self.max_retries})"
                    )
                    time.sleep(wait_time)
                    continue
                
                elif response.status_code == 422:
                    # バリデーションエラーはリトライしない
                    raise ValueError(
                        f"パラメータエラー (HTTP 422): {response.json()}"
                    )
                
                elif response.status_code == 402:
                    raise RuntimeError(
                        "クレジット不足 (HTTP 402): BFLアカウントに残高を追加してください"
                    )
                
                else:
                    response.raise_for_status()
            
            except requests.exceptions.Timeout:
                logger.warning(f"接続タイムアウト (試行 {attempt + 1}/{self.max_retries})")
                if attempt == self.max_retries - 1:
                    raise
        
        raise RuntimeError(f"{self.max_retries}回のリトライ後も失敗しました")
    
    def _poll_result(self, request_id: str) -> dict:
        """
        結果が返るまでポーリングする。
        Readyステータスを受け取ったら結果dictを返す。
        """
        for attempt in range(self.max_poll_attempts):
            time.sleep(self.poll_interval)
            
            response = self.session.get(
                f"{self.BASE_URL}/get_result",
                params={"id": request_id},
                timeout=10
            )
            response.raise_for_status()
            data = response.json()
            status = data.get("status")
            
            logger.debug(f"ポーリング [{attempt + 1}/{self.max_poll_attempts}]: {status}")
            
            if status == "Ready":
                return data["result"]
            
            elif status == "Error":
                error_detail = data.get("error", "詳細不明")
                raise RuntimeError(f"生成エラー: {error_detail}")
            
            # "Pending" / "Processing" はループを継続
        
        timeout_seconds = self.poll_interval * self.max_poll_attempts
        raise TimeoutError(
            f"{timeout_seconds}秒以内に完了しませんでした (id: {request_id})"
        )
    
    def generate(
        self,
        config: GenerationConfig,
        output_path: str = "output.jpg"
    ) -> dict:
        """
        画像を生成してファイルに保存する。
        成功時は画像URLとローカルパスを含むdictを返す。
        """
        start_time = time.time()
        
        # リクエスト送信
        request_id = self._submit_request(config)
        
        # 結果待機
        result = self._poll_result(request_id)
        
        # 画像ダウンロードと保存
        image_url = result["sample"]
        output_file = Path(output_path)
        output_file.parent.mkdir(parents=True, exist_ok=True)
        
        image_response = requests.get(image_url, timeout=60)
        image_response.raise_for_status()
        
        output_file.write_bytes(image_response.content)
        
        elapsed = time.time() - start_time
        logger.info(f"完了: {output_path} ({elapsed:.1f}秒)")
        
        return {
            "request_id": request_id,
            "image_url": image_url,
            "local_path": str(output_file),
            "elapsed_seconds": elapsed
        }
    
    def __del__(self):
        """オブジェクト破棄時にSessionを閉じる。"""
        if hasattr(self, "session"):
            self.session.close()


# 使用例
if __name__ == "__main__":
    client = FluxProClient(max_retries=3)
    
    config = GenerationConfig(
        prompt="Professional product photography of a ceramic coffee mug, "
               "white background, studio lighting, sharp focus, commercial quality",
        width=1024,
        height=1024,
        steps=28,
        guidance=3.5,
        seed=42,          # 再現性のためにseedを固定
        output_format="jpeg"
    )
    
    result = client.generate(config, output_path="outputs/product_shot.jpg")
    print(f"画像URL: {result['image_url']}")
    print(f"生成時間: {result['elapsed_seconds']:.1f}秒")

エラーハンドリング:実際のエラーコードと対処法

エラーコード発生原因対処法
HTTP 401APIキーが無効または未設定.envBFL_API_KEY を確認。スペースや改行が混入していないか確認
HTTP 402アカウントのクレジット残高不足BFLダッシュボードでクレジットを追加
HTTP 422パラメータバリデーション失敗width/height が64の倍数か確認。steps が1〜50の範囲か確認
HTTP 429レート制限超過指数バックオフでリトライ。無料tierは1分あたり約10リクエスト
HTTP 500/503BFLサーバーエラー2〜8秒の指数バックオフでリトライ。継続する場合はBFLステータスページを確認
status: “Error”生成失敗(promptフィルタ等)safety_tolerance を確認。不適切なコンテンツがpromptに含まれていないか確認
TimeoutErrorポーリング上限到達max_poll_attempts を増やすか、サーバー負荷が高い時間帯を避ける
ConnectionErrorネットワーク断VPN/Proxyの確認。エンドポイント api.us1.bfl.ai への疎通確認

セーフガードとして追加すべきチェック:

# validation.py — リクエスト送信前のパラメータ検証
def validate_config(config: GenerationConfig) -> None:
    """
    APIに送る前にローカルでバリデーションする。
    無効なリクエストでAPIクレジットを消費しないようにするため。
    """
    errors = []
    
    # widthとheightは64の倍数である必要がある
    if config.width % 64 != 0:
        errors.append(f"width ({config.width}) は64の倍数である必要があります")
    if config.height % 64 != 0:
        errors.append(f"height ({config.height}) は64の倍数である必要があります")
    
    # 解像度の範囲チェック
    if not (256 <= config.width <= 1440):
        errors.append(f"width は256〜1440の範囲内である必要があります")
    if not (256 <= config.height <= 1440):
        errors.append(f"height は256〜1440の範囲内である必要があります")
    
    # stepsの範囲チェック
    if not (1 <= config.steps <= 50):
        errors.append(f"steps は1〜50の範囲内である必要があります")
    
    # promptが空でないことを確認
    if not config.prompt or len(config.prompt.strip()) == 0:
        errors.append("prompt は空にできません")
    
    if errors:
        raise ValueError(f"バリデーションエラー:\n" + "\n".join(f"  - {e}" for e in errors))

パフォーマンスとコストの実測値

以下の数値はBFL公式ドキュメントおよびコミュニティ計測値に基づく。

解像度おおよその生成時間コスト(BFL直接)コスト(Replicate経由)推奨用途
512×5124〜6秒$0.01/枚$0.02〜0.03/枚プロトタイプ・テスト
768×7686〜9秒$0.02/枚$0.03〜0.04/枚SNS・ブログ用途
1024×10248〜15秒$0.04/枚$0.055/枚標準的な本番用途
1024×144012〜20秒$0.06/枚$0.08/枚ポートレート・印刷
1440×144018〜30秒$0.08/枚$0.10/枚高解像度・商業印刷

バッチ処理のコスト試算(1024×1024の場合):

月間生成枚数BFL直接コストReplicate経由コスト
100枚$4.00$5.50
1,000枚$40.00$55.00
10,000枚$400.00$550.00
100,000枚$4,000.00$5,500.00(ボリューム割引要確認)

BFL直接API vs Replicate経由の選択基準:

  • BFL直接API:コストを最小化したい、低レイテンシが必要、大量バッチ処理を行う場合
  • Replicate経由:既存のReplicate基盤を使い回したい、モデル切り替えの柔軟性が必要、小規模プロトタイプの場合

FLUX 1.1 Pro を使うべきでないケース:

  • リアルタイム(1秒未満)のレスポンスが必要なユースケース — レイテンシ上8〜15秒は避けられない
  • 月間100万枚以上のスケールで$0.04/枚が予算に収まらない場合 — Stable Diffusion XLのセルフホストを検討
  • 厳密な再現性が必要で同一promptで完全一致した出力を要求する場合 — seedを固定しても微妙な差異が発生することがある

Replicate経由の実装(補足)

Replicateを既に使用している場合の最小実装:

# generate_via_replicate.py — Replicate経由でFLUX 1.1 Proを使う
import os
import replicate
from dotenv import load_dotenv

load_dotenv()

# REPLICATE_API_TOKEN は replicate ライブラリが自動で読み込む
os.environ["REPLICATE_API_TOKEN"] = os.getenv("REPLICATE_API_TOKEN", "")

def generate_with_replicate(prompt: str, output_path: str = "output_replicate.webp") -> str:
    """
    Replicate経由でFLUX 1.1 Proを実行する。
    BFLの直接APIと比べてセットアップが少ない反面、コストが約38%高い。
    """
    output = replicate.run(
        # モデルIDのバージョンは変わることがあるので最新IDを確認すること
        "black-forest-labs/flux-1.1-pro",
        input={
            "prompt": prompt,
            "width": 1024,
            "height": 1024,
            "steps": 25,
            "guidance": 3.5,
            "output_format": "webp",  # Replicateはwebpも対応
            "output_quality": 90,     # webp品質(1〜100)
            "safety_tolerance": 2,
            "prompt_upsampling": False
        }
    )
    
    # replicateのrunはイテレータを返す場合がある
    # 出力をバイナリとして保存する
    import requests
    if isinstance(output, list):
        image_url = output[0]
    else:
        image_url = str(output)
    
    response = requests.get(image_url, timeout=60)
    with open(output_path, "wb") as f:
        f.write(response.content)
    
    print(f"✓ 保存完了: {output_path}")
    return output_path


if __name__ == "__main__":
    generate_with_replicate(
        prompt="Aerial view of a dense forest in autumn, "
               "drone photography, golden and red foliage, misty morning",
        output_path="forest_replicate.webp"
    )

まとめ

FLUX 1.1 Pro API はBFL直接エンドポイント経由で$0.04/枚・8〜15秒の推論時間で動作し、非同期ポーリング方式のためBFL Client + 指数バックオフのリトライが本番実装の最低要件になる。月間1,000枚未満の小規模用途ならReplicateで十分だが、それ以上のスケールではBFL直接APIへの移行がコスト面で合理的な選択となる。本記事のコードはそのままコピーして使える状態にしてあるが、safety_tolerance とシード管理はプロダクトの要件に合わせて必ず調整すること。

メモ: 複数の AI モデルを一つのパイプラインで使う場合、AtlasCloud は Kling、Flux、Seedance、Claude、GPT など 300+ モデルへの統一 API アクセスを提供します。API キー一つで全モデル対応。新規ユーザーは初回チャージで 25% ボーナス(最大 $100)。

AtlasCloudでこのAPIを試す

AtlasCloud

よくある質問

FLUX 1.1 Pro APIの料金はいくらですか?他のモデルと比較してコスパはどうですか?

FLUX 1.1 ProのBFL直接API価格は1024×1024画像1枚あたり$0.04(約6円)です。比較すると、DALL-E 3は1024×1024で$0.04〜$0.08/枚、Stable Diffusion XL(Replicate経由)は約$0.0023/秒です。FLUX 1.1 Proは推論レイテンシ約8〜15秒/枚のため、Replicate経由では実質$0.02〜$0.04相当となり、品質面ではFLUX 1.0 Proよりプロンプトヒーロー/ELOベンチマークで最大20%向上しているため、品質重視の商用プロジェクトでは競争力のある価格帯と言えます。

FLUX 1.1 Pro APIの画像生成レイテンシはどのくらいですか?タイムアウト設定はどう実装すべきですか?

FLUX 1.1 ProのAPIは非同期ポーリング方式を採用しており、平均推論レイテンシは約8〜15秒/枚(1024×1024)です。実装時のポーリング間隔は1〜2秒推奨で、タイムアウトは最低60秒以上に設定してください。Pythonでの実装例としては`requests`の`timeout=30`をポーリングリクエストごとに設定し、最大リトライ回数を30回(合計最大60秒)とするのが安全です。ネットワーク遅延を含めると本番環境では最大30秒のバッファを見込み、SLAクリティカルな処理では非同期キュー(Celery等)との組み合わせを推奨します。

FLUX 1.1 ProとFLUX 1.0 Proの品質差はベンチマークで具体的にどう違いますか?

FLUX 1.1 ProはFLUX 1.0 ProよりPrompthero評価およびELOスコアベースで最大20%の品質向上が確認されています。具体的にはプロンプト追従性(prompt adherence)と画像の細部再現性が主な改善点です。ELOスコアはユーザー比較投票に基づく相対評価で、20%向上は実運用での体感差として明確に現れるレベルとされています。また生成速度もFLUX 1.0 Proと比較して約6倍高速化(BFL公式発表)されており、同価格帯$0.04/枚で品質・速度の両面が向上しているため、既存FLUX 1.0 Proユーザーは移行メリットが大きいです。

FLUX 1.1 Pro APIをPythonで実装する際、BFL直接APIとReplicate経由ではどちらを選ぶべきですか?

用途によって使い分けが推奨されます。BFL直接API(エンドポイント: https://api.us1.bfl.ai)は価格$0.04/枚で最も安価ですが、非同期ポーリング実装が必要でインフラ管理コストがかかります。Replicate経由(モデルID: black-forest-labs/flux-1.1-pro)は秒課金(約$0.0023/秒×8〜15秒=実質$0.018〜$0.035/枚)となりわずかに割安になるケースもありますが、Replicateの管理費・レイテンシオーバーヘッドが加算されます。必要パッケージはBFL側が`requests`, `python-dotenv`, `Pillow`の3つ、Replicate側は追加で`replicate`パッケージが必要です。月間1,000枚以下のプロトタイプはReplicate、1万枚以上の本番運用はBFL直接APIが費用対効果で有利

タグ

Flux FLUX 1.1 Pro Python Image Generation API 2026

関連記事