Python で画像を生成する
Python 標準ライブラリで MaxAPI の画像タスクを 1 件送信し、状態を確認しながら重複生成を避けて復旧します。
技術確認日:
1. サーバー用キーを準備
Python 3.10 以降を使用します。配布ファイルは json、time、urllib のみを使い、pip パッケージは不要です。Console でキーを作成し、許可モデル、ルート、利用可能残高を確認してください。MAXAPI_KEY はサーバー環境またはシークレット管理に置き、公開ノートブックやブラウザー、リポジトリには含めません。
2. 小さなリクエストから開始
下の maxapi_image.py を保存し、MAXAPI_KEY を安全に設定して実行します。/v1/images/generations に google/gemini-3.1-flash-lite-image、resolution 1K、aspect_ratio 1:1、n 1、async true を 1 回送信します。この Lite 画像モデルは 1K 用です。4K を使う場合は対応モデルを選び直してください。
python3 maxapi_image.py3. 受付と完了を区別
画像は自動保存しません。タスク ID と確認 URL を stderr に出すので、自分のジョブ記録に保存してください。成功後は結果 URL を表示します。HTTP 202 は受付であり完了ではなく、後の HTTP 200 でも実行中や失敗を表す場合があります。status を確認し、非公開画像の URL は共有せず、別ホストから画像を取得する際に MaxAPI キーを付けないでください。
4. 再送信ではなく再開
各通信には 30 秒のソケットタイムアウトがあり、標準では 5 秒間隔で最大 120 回 GET します。通信時間も加わるため厳密な 10 分制限ではありません。確認回数の上限や Ctrl+C ではタスクはキャンセルされません。保存した URL を MAXAPI_POLL_URL に設定すれば同じアカウントのキーで確認を再開でき、POST は再送しません。初回 POST で ID を受信する前にタイムアウトした場合は、履歴を確認してから再生成を判断してください。
MAXAPI_POLL_URL="/v1/images/generations/YOUR_TASK_ID" python3 maxapi_image.py完全なサンプルをダウンロード
maxapi_image.py
"""Python 3.10+. Standard library only. Server-side; local-fixture tested, not a live model test."""
import json
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
EXAMPLE_PAYLOAD = {
"model": "google/gemini-3.1-flash-lite-image",
"prompt": "A single blue ceramic cup on a plain cream background, soft studio light.",
"resolution": "1K", "aspect_ratio": "1:1", "n": 1, "async": True,
}
ACTIVE = {"submitted", "queued", "pending", "running", "processing"}
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
def safe_url(base, path):
import re
origin = urllib.parse.urlsplit(base)
url = urllib.parse.urlsplit(urllib.parse.urljoin(base + "/", path))
valid_scheme = origin.scheme == "https" or (origin.scheme == "http" and origin.hostname in {"127.0.0.1", "::1"})
def address(value):
return (value.scheme, value.hostname, value.port or (443 if value.scheme == "https" else 80))
if (not valid_scheme or origin.path not in {"", "/"} or origin.query or origin.fragment
or origin.username or origin.password or url.username or url.password or url.query or url.fragment
or address(url) != address(origin) or not re.fullmatch(r"/v1/images/(generations|edits)(/[^/?]+)?", url.path)):
raise RuntimeError("Unsafe API or polling URL; no credentials sent.")
return urllib.parse.urlunsplit(url)
def run_image(key, payload=None, base="https://api.maxapi.dev", poll_url=None,
interval=5, max_polls=120, timeout=30, on_task=lambda task: None):
if not key:
raise RuntimeError("Set MAXAPI_KEY in your server environment.")
if not isinstance(max_polls, int) or not 1 <= max_polls <= 720 or interval < 0 or timeout <= 0:
raise RuntimeError("Invalid polling limits.")
opener = urllib.request.build_opener(NoRedirect())
def request(path, body=None):
import re
headers = {"Authorization": "Bearer " + key}
if body is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(safe_url(base, path), headers=headers,
data=json.dumps(body).encode() if body is not None else None,
method="POST" if body is not None else "GET")
try:
response = opener.open(req, timeout=timeout)
except urllib.error.HTTPError as error:
response = error
except (urllib.error.URLError, TimeoutError, OSError) as error:
raise RuntimeError("Network or timeout error; outcome may be unknown. No automatic resubmission.") from error
with response:
status = response.code
try:
result = json.load(response)
except (ValueError, UnicodeError) as error:
raise RuntimeError(f"HTTP {status}: non-JSON response; inspect history before resubmitting.") from error
if not isinstance(result, dict):
raise RuntimeError("Invalid JSON response object.")
if not 200 <= status < 300 or result.get("error"):
detail = result.get("error")
code = detail.get("code", "api_error") if isinstance(detail, dict) else "api_error"
code = code if isinstance(code, str) and re.fullmatch(r"[a-zA-Z0-9_-]{1,80}", code) else "api_error"
raise RuntimeError(f"HTTP {status}: {code}; no automatic retry.")
return result
current = request(poll_url) if poll_url else request("/v1/images/generations", EXAMPLE_PAYLOAD if payload is None else payload)
path = poll_url or current.get("poll_url")
if path:
path = safe_url(base, path)
on_task({"id": current.get("id"), "poll_url": path})
count = 0
while True:
if current.get("status") == "succeeded" or (not current.get("status") and isinstance(current.get("data"), list) and current["data"]):
return current
if current.get("status") not in ACTIVE:
raise RuntimeError("Task failed, cancelled or returned an unknown state. Check history; no resubmission.")
if not path:
raise RuntimeError("Pending task has no polling URL. Check history; do not resubmit automatically.")
if count >= max_polls:
raise RuntimeError("Polling limit reached; task was NOT cancelled. Resume using the saved polling URL.")
time.sleep(interval)
current = request(path)
count += 1
if __name__ == "__main__":
try:
result = run_image(os.environ.get("MAXAPI_KEY"), poll_url=os.environ.get("MAXAPI_POLL_URL"),
on_task=lambda task: print("Save to resume:", json.dumps(task), file=sys.stderr))
print(json.dumps({"id": result.get("id"), "status": result.get("status", "succeeded"),
"urls": [item["url"] for item in result.get("data", []) if item.get("url")]}, indent=2))
except (RuntimeError, ValueError) as error:
print(str(error), file=sys.stderr)
sys.exit(1)
モデル別パラメーター
公開設定のスナップショットであり、現在の稼働状況ではありません。パラメーター、ルート、参照画像の上限は各モデルページを確認してください。
- Gemini 3.1 Flash Lite Image
google/gemini-3.1-flash-lite-image - Gemini 3.1 Flash Image Preview
google/gemini-3.1-flash-image-preview