xai-grok-imagine-image について
xai-grok-imagine-image は、xAI の画像 API で画像を生成・編集するためのスキルです。画像処理を制作工程に組み込む開発者やクリエイター向けに、同期リクエスト、モデル選択、自然言語編集、素材管理を説明します。
主な機能
- 01画像生成・編集エンドポイントの利用
- 02モデル、アスペクト比、解像度の選択
- 03単一または複数の参照画像による編集
- 04生成素材の保存と追跡
- 05リクエスト費用、キー、ファイルの安全管理
活用シーン
- 01ウェブサイトのメイン画像やアイコン案の生成
- 02参照画像を使った製品やシーンの編集
- 03承認済み画像を保持しながらの反復作業
- 04制作ワークフローへの画像生成の統合
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ calesthio/ generative-media-skills --skill xai-grok-imagine-imageインストール後、Claude Code で/xai-grok-imagine-imageを入力して読み込まれたか確認します。
同梱ファイルEVAL.md
詳細
xAI Grok Imagine Image
xAI のファーストパーティ画像 API を、プロンプトのおもちゃではなく、本番システムとして運用します。画像ワークフローは Grok Imagine 動画とは分けてください。直接 REST API では画像の生成と編集は同期処理ですが、動画には別の非同期リクエスト/ポーリング契約があります。
変動する情報は、2026-07-09 に xAI のファーストパーティドキュメントで確認済みです。デプロイ前に、モデルのエンドポイント、コンソール、料金、レート制限、リリースノート、法的条件を再確認してください。
エビデンスの用語
- ドキュメントで明記: 現行の xAI API ドキュメント、モデルページ、または利用規約に記載されています。
- プロバイダーの主張: xAI の製品資料またはローンチ資料で使われている定性的な表現です。個別に検証してください。
- 本番環境での経験則: 契約と一般的なメディア運用に基づく運用上の推奨であり、xAI による保証ではありません。
- 不明: 確認したファーストパーティの情報源では確立されていません。推測で補わないでください。
インターフェースとモデルを固定する
ドキュメントに記載された推論用のベース URL とルートのみを使用してください。
POST https://api.x.ai/v1/images/generations
POST https://api.x.ai/v1/images/edits
Authorization: Bearer {XAI_API_KEY}
Content-Type: application/json
必要最小限のエンドポイント/モデル ACL を設定した、チームスコープのキーを作成してください。シークレットマネージャーまたは XAI_API_KEY から読み込み、ソースコード、ログ、プロンプト、アセットのメタデータ、ブラウザーコード、URL には絶対に含めないでください。
現行の画像モデル
| モデル | 現行のエイリアス | 1K 出力 | 2K 出力 | 入力画像 | 用途 |
|---|---|---|---|---|---|
grok-imagine-image-quality | grok-imagine-image-quality-20260403、grok-imagine-image-quality-latest。廃止済みの grok-imagine-image-pro はこちらにリダイレクトされます | $0.05 | $0.07 | 1 枚あたり $0.01 | 高品質を求める本番候補です。より強いリアリズム、テキスト表現、制御性はプロバイダーの主張です |
grok-imagine-image | grok-imagine-image-2026-03-02 | $0.02 | $0.02 | 1 枚あたり $0.002 | 低コストの候補であり、大量の試行にも適しています |
これらの価格は USD 表示で、2026-07-09 に xAI が掲載していたものです。料金は生成出力ごと、および各入力画像ごとに発生します。これらの画像モデルページでは、テキストプロンプトの長さに別途料金はかかりません。請求書と最新のモデルページを確認してください。
公開されているモデルページには両モデルが us-east-1 と us-west-2 に掲載されており、公開 REST ベース URL は引き続き api.x.ai です。汎用ホスト名やマーケティング上の記述から、データレジデンシーを推測しないでください。xAI はエンタープライズ向けに、リージョン処理/データレジデンシーのオプションを宣伝していますが、確認した公開画像 API の契約には、セルフサービスで指定できるリージョンパラメーターはありません。データレジデンシーが重要な場合は、リージョンに関する確約を書面で取得してください。
grok-imagine-image-pro は 2026-05-15 12:00 PT に廃止され、現在は grok-imagine-image-quality にリダイレクトされます。エイリアスとして引き続き表示されていても、新しいコードでは廃止済みのスラッグを使用しないでください。回帰テストを管理する場合は日付付きのエイリアスを優先し、プロバイダーによる更新を受け入れる場合に限り、日付なしの正式モデル名を使用してください。取得できる場合は、実際のモデル/フィンガープリントを記録してください。
起動前に、実際のキーで利用可能なモデルを照会してください。
: "${XAI_API_KEY:?set XAI_API_KEY}"
curl --fail-with-body --silent --show-error \
https://api.x.ai/v1/image-generation-models \
-H "Authorization: Bearer ${XAI_API_KEY}"
モデル一覧のレスポンスには、ID、エイリアス、モダリティ、バージョン/フィンガープリント、max_prompt_length が含まれます。現行の参考例では値は 1024 ですが、確認したスキーマでは、その単位が文字数なのかトークン数なのか説明されていません。実際のフィールドはプロバイダー定義のものとして扱い、単位を決めつけずにリクエストを検証してください。
直接リクエストの契約
テキストから画像を生成
prompt と明示的な model を指定します。省略可能なフィールドは次のとおりです。
n:バリエーションの数です。Imagine の概要では、1 回の生成リクエストにつき最大 10 枚の画像が記載されています。生成に成功した出力はすべて課金対象です。aspect_ratio:1:1、3:4、4:3、9:16、16:9、2:3、3:2、9:19.5、19.5:9、9:20、20:9、1:2、2:1、またはautoです。resolution:小文字の1kまたは2kです。response_format:urlまたはb64_jsonです。storage_options:出力を xAI Files API に保存します。user:不正利用の監視に使用する、一意のエンドユーザー識別子です。生のメールアドレス、氏名、その他不要な個人情報ではなく、安定した仮名値を送信してください。
現在の REST 画像スキーマには、seed、ネガティブプロンプトのフィールド、ガイダンススケール、マスク、出力形式のセレクター、品質のスカラー値、ストリーミングフラグはありません。存在しないものを作り出さないでください。storage_options のファイル名拡張子によって、生成されたバイト列が変換されることはありません。
自然言語による画像編集
/v1/images/edits を使用し、prompt、model、および次のいずれか一方を指定します。
image:ソースオブジェクト 1 つ、またはimages:ソースオブジェクトの配列(最大 3 つ)。
各ソースオブジェクトには、次のいずれかを含めます。
url:公開 HTTPS URL または Base64 データ URL、またはfile_id:xAI Files API に完全にアップロード済みの非公開画像。
オブジェクト内で url と file_id を同時に指定することはできません。編集入力でサポートされている形式は JPEG、PNG、WebP です。現在の直接画像スキーマでは、ソースの最大バイトサイズは公開されていません。アプリケーション側で保守的な上限を設け、稼働中のサービスでテストしてください。Batch API のリクエストあたり 25 MB というペイロード上限は、直接ルートの上限を示す根拠にはなりません。
複数のソースを使用する場合は、リクエスト順に <IMAGE_0>、<IMAGE_1>、<IMAGE_2> として参照してください。複数画像の編集では aspect_ratio を上書きできます。デフォルトでは最初の入力に従います。単一画像の編集では入力のアスペクト比が維持されるため、aspect_ratio によって形状が変わると約束しないでください。
編集スキーマには n がありますが、現在の概要で明示されている「最大 10」という記述は画像生成に限定されています。編集では出力 1 件をデフォルトとし、同じ上限が適用されると想定せずに、より大きな編集数を機能テストしてください。
OpenAI SDK の images.edit() ヘルパーはマルチパートフォームデータを送信するため、互換性がありません。xAI の編集ルートには JSON が必要です。直接 HTTP、xAI SDK、または互換性のある Vercel AI SDK の経路を使用してください。OpenAI SDK から /images/generations を呼び出すことは引き続き可能です。aspect_ratio や resolution などの xAI 固有フィールドには、Python で extra_body が必要になる場合があります。
プロンプトを本番用仕様書として扱う
ファーストパーティーのユースケースガイドでは、被写体、スタイル、照明、構図、雰囲気を具体的に指定することが推奨されています。それを監査可能なブリーフに展開します。
Deliverable and placement; subject and action; environment and era;
camera/viewpoint and composition; light and palette; materials and rendering medium;
exact visible text and hierarchy; must-preserve details; exclusions; variation intent.
生成の実践
- 成果物を最初に述べます。「16:9 の Web サイト用ヒーロー画像」「3:2 のエディトリアル写真」「1:1 のアイコン案」などです。
- 雰囲気やスタイルより先に、主要な被写体を 1 つと、その動作を指定します。
- 空間的な位置関係、カメラの高さ、トリミング、レンズの印象、余白を指定します。
- 光の方向と質、および簡潔な言葉による少数の色調を説明します。色の値は目標であり、保証ではありません。
- 製品制作では、物理的な素材と表面の性質を説明します。
- 必要なコピーは引用符で囲み、配置、大文字・小文字、改行を指定します。xAI の「より優れたテキストレンダリング」はプロバイダー側の主張として扱い、OCR と人による校正を行ってください。
- 余計な文字が不具合になる場合は、「テキスト/ロゴを表示しない」と指定します。ネガティブな制約は文章であり、独立した API パラメーターではありません。
編集の実践
操作と不変条件を指定します。
Use <IMAGE_0> as the base composition. Replace only the chair fabric with the
navy herringbone material from <IMAGE_1>. Keep camera, crop, room geometry,
person, face, hands, lighting direction, shadows, wall art, and all text unchanged.
Do not add objects.
各参照画像に 1 つずつ役割を割り当てます。どの画像が同一性/形状を担うのか、どの画像がスタイル/素材を提供するのか、何を引き継いではならないのかを明示します。マスク入力は文書化されていないため、ピクセル単位で局所的にインペインティングできると約束しないでください。局所性が重要な場合は、範囲を厳密に記述した編集を依頼して結果を確認し、必要に応じて別の決定論的なコンポジター/マスキングツールを使用してください。
ドリフトのない反復
xAI は、各出力を次のリクエストに入力することで複数ターンの編集を行う方法を説明しています。これは独立した画像呼び出しを連鎖させるものであり、サーバー側の会話ではありません。承認済みの各状態とそのプロンプトを保存してください。
- 自動的に最新の状態からではなく、最後に承認された状態から分岐します。
- アイデンティティやレイアウトが重要な場合は、編集ごとに意味上の変更を1つだけ行います。
- 毎ターン、不変条件を繰り返します。API はそれらを隠れた状態として保持しません。
- 数回編集した後、元の画像と比べてトリミング、アイデンティティ、テキスト、ロゴ、背景にドリフトがないか確認します。
- ドリフトが蓄積する場合は、統合したプロンプトを使って承認済みのベースからやり直します。
幅広く試す場合は、低コストのモデルや1Kを使用します。構図が安定してから、候補に絞ったプロンプトをQuality/2Kに引き上げます。これは本番運用上のヒューリスティックであり、品質を保証するものではありません。
レスポンスとアーティファクトのライフサイクル
直接画像ルートは、完了したdata[]とusageを同期的に返します。動画のようなrequest_idは返しません。非同期 SDK クライアントは、単に複数の同期呼び出しを並行して実行します。サーバー側でキューに入れて画像を処理するには、別の Batch API を使用します。
各data項目には、次の内容が含まれる場合があります。
- URL 形式のレスポンスでは
url、またはデータ URI プレフィックスのないb64_json。 mime_type。- 永続化が要求された場合の
file_output。 - 生成には成功したものの、Files への永続化に失敗した場合の
storage_error。
非公開の永続化に成功した場合、file_outputにはfile_id、filename、および有効期限に関するオプションのフィールドが含まれます。public_urlまたはpublic_url_errorは公開リンクを要求した場合にのみ該当し、通常の非公開保存のみではどちらも想定されません。公開 URL の作成や Files への永続化に失敗しても、生成された一時的なアーティファクトは有効なままの場合があります。まずそのアーティファクトを回収し、再生成して費用を再度支払うことなく、Files API を通じて保存や公開リンクの状態を修復してください。
usage.cost_in_usd_ticksはリクエストの正確な費用です。xAI では、100,000,000 ticks を米ドルの1セント、10,000,000,000 ticks を1米ドルと定義しています。
4種類の有効期間
- デフォルトの
imgen.x.aiURL: 一時的/短期間のみ有効と説明されています。直接の画像レスポンスについて、正確な有効期間は公開されていません。すぐにダウンロードしてください。 - Batch 画像 URL: 1時間後に期限切れになると記載されています。速やかに取得してコピーしてください。
- 非公開の Files 出力:
storage_options.expires_afterを省略すると削除するまで保持されるか、3,600~2,592,000秒(1時間~30日)後に期限切れになります。 - Files の公開 URL: 認証不要で、取り消し可能です。恒久的に有効にすることも、1時間~30日で期限切れにすることもできますが、ファイルより長く有効にすることはできません。チームごとに有効な公開 URL は最大1,000件です。
出力はデフォルトで非公開にしてください。反復作業では、public_urlを指定せずにstorage_options: {"filename": "..."}を使用します。共有が必要な場合は、有効期間を短く設定し、納品後に取り消してください。カスタム拡張子が影響するのは公開 URL のパスであり、保存される MIME タイプではありません。
アーティファクトのセキュリティ規則
imgen.x.aiまたはfiles-cdn.x.aiからのダウンロードにAuthorizationを付加しないでください。トークンを送信するのは xAI API/Files エンドポイントのみです。- 返された URL は信頼できないものとして扱ってください。HTTPS を必須とし、埋め込み認証情報やフラグメントを拒否し、ホストを制限して、リダイレクトを再確認します。
- 一時ファイルにストリーミングし、バッファリングや昇格の前に厳格なバイト数上限を設けます。
- 宣言された MIME タイプ、マジックバイト、デコード後の寸法/面積、スキャンポリシーを検証し、ファイル名を決して信用しないでください。
- 最終バイト列のハッシュを計算し、管理されたストレージにアトミックに昇格します。
- リクエストハッシュ、プロンプトのバージョン、参照情報と権利記録、モデル/エイリアス、レスポンスの MIME タイプ、寸法、費用、ファイル ID/有効期限、アーティファクトのハッシュ、モデレーション/レビュー結果、公開判断を記録します。
例:厳密な Base64 処理を伴う同期生成
この Python の完全な例は、必須の定型ではなく、あくまで一例です。費用が発生するため、実行していません。まず Pillow をインストールしてください。デフォルトでは機密情報を伏せたドライランを実行し、明示的にオプトインした場合に限り、品質設定「Quality」の 2K 候補を 2 つ生成します。また、レスポンスとデコード後のバイト数に上限を設け、署名、MIME タイプ、ピクセルを検証してからアトミックに書き込み、請求額を正確に記録します。
import base64
import binascii
import hashlib
import io
import json
import os
import pathlib
import tempfile
import urllib.error
import urllib.request
from PIL import Image, UnidentifiedImageError
API_URL = "https://api.x.ai/v1/images/generations"
MAX_RESPONSE_BYTES = 80 * 1024 * 1024
MAX_IMAGE_BYTES = 30 * 1024 * 1024
MAX_IMAGE_PIXELS = 20_000_000
OUT = pathlib.Path("grok-imagine-candidates")
Image.MAX_IMAGE_PIXELS = MAX_IMAGE_PIXELS
prompt = (
"16:9 website hero for a premium outdoor tea brand. A brushed steel kettle "
"and two matte ceramic cups on a dark slate ledge at dawn in the mountains. "
"Camera at tabletop height, generous clear negative space on the left, soft "
"mist, warm sunrise rim light against cool blue shadows, realistic metal and "
"ceramic textures, restrained editorial photography. No people, logos, labels, "
"letters, or watermarks. Create distinct composition variations."
)
payload = {
"model": "grok-imagine-image-quality",
"prompt": prompt,
"n": 2,
"aspect_ratio": "16:9",
"resolution": "2k",
"response_format": "b64_json",
"user": os.getenv("XAI_END_USER_ID", "internal-demo-user"),
}
body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
request = urllib.request.Request(
API_URL,
data=body,
method="POST",
headers={
"Authorization": "Bearer " + os.environ.get("XAI_API_KEY", ""),
"Content-Type": "application/json",
"Accept": "application/json",
},
)
print({
"model": payload["model"], "n": payload["n"],
"resolution": payload["resolution"],
"request_sha256": hashlib.sha256(body).hexdigest(),
"estimated_usd_snapshot_2026_07_09": 0.14,
})
if os.environ.get("SEND_XAI_REQUEST") != "1":
raise SystemExit("Dry run only; set SEND_XAI_REQUEST=1 after approving the paid call")
if not os.environ.get("XAI_API_KEY"):
raise SystemExit("Set XAI_API_KEY before the paid call")
try:
with urllib.request.urlopen(request, timeout=240) as response:
zdr = response.headers.get("x-zero-data-retention")
raw = response.read(MAX_RESPONSE_BYTES + 1)
except urllib.error.HTTPError as exc:
detail = exc.read(64 * 1024).decode("utf-8", "replace")
raise SystemExit(f"xAI HTTP {exc.code}: {detail}") from exc
if len(raw) > MAX_RESPONSE_BYTES:
raise SystemExit("Response exceeded configured cap")
document = json.loads(raw)
items = document.get("data")
if not isinstance(items, list) or len(items) != payload["n"]:
raise SystemExit(f"Expected {payload['n']} outputs, received {len(items or [])}")
OUT.mkdir(parents=True, exist_ok=True)
artifacts = []
for index, item in enumerate(items):
encoded = item.get("b64_json")
if not encoded:
raise SystemExit(f"Output {index} has no base64 image")
if len(encoded) > ((MAX_IMAGE_BYTES + 2) // 3) * 4 + 8:
raise SystemExit(f"Output {index} exceeds encoded-size cap")
try:
image = base64.b64decode(encoded, validate=True)
except (binascii.Error, ValueError) as exc:
raise SystemExit(f"Output {index} is not strict base64") from exc
if len(image) > MAX_IMAGE_BYTES:
raise SystemExit(f"Output {index} exceeds decoded-size cap")
if image.startswith(b"\xff\xd8\xff"):
suffix, expected_mime = ".jpg", "image/jpeg"
elif image.startswith(b"\x89PNG\r\n\x1a\n"):
suffix, expected_mime = ".png", "image/png"
elif image.startswith((b"RIFF",)) and image[8:12] == b"WEBP":
suffix, expected_mime = ".webp", "image/webp"
else:
raise SystemExit(f"Output {index} has an unexpected signature")
declared = item.get("mime_type")
if declared and declared != expected_mime:
raise SystemExit(f"Output {index} MIME/signature mismatch")
try:
with Image.open(io.BytesIO(image)) as probe:
dimensions = probe.size
if dimensions[0] * dimensions[1] > MAX_IMAGE_PIXELS:
raise SystemExit(f"Output {index} exceeds pixel cap: {dimensions}")
decoded_format = probe.format
probe.verify()
with Image.open(io.BytesIO(image)) as probe:
probe.load()
except (UnidentifiedImageError, OSError, Image.DecompressionBombError) as exc:
raise SystemExit(f"Output {index} failed bounded pixel decode") from exc
expected_format = {"image/jpeg": "JPEG", "image/png": "PNG", "image/webp": "WEBP"}[expected_mime]
if decoded_format != expected_format:
raise SystemExit(f"Output {index} decoder/signature mismatch")
destination = OUT / f"candidate-{index + 1:02d}{suffix}"
fd, temporary = tempfile.mkstemp(dir=OUT, prefix=".grok-imagine-")
try:
with os.fdopen(fd, "wb") as stream:
fd = None
stream.write(image)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, destination)
except Exception:
if fd is not None:
os.close(fd)
try:
os.unlink(temporary)
except FileNotFoundError:
pass
raise
artifacts.append({
"path": str(destination),
"bytes": len(image),
"mime_type": expected_mime,
"dimensions": list(dimensions),
"sha256": hashlib.sha256(image).hexdigest(),
})
manifest = {
"model_requested": payload["model"],
"request_sha256": hashlib.sha256(body).hexdigest(),
"zero_data_retention_header": zdr,
"usage": document.get("usage"),
"artifacts": artifacts,
"review_status": "pending",
}
(OUT / "manifest.json").write_text(
json.dumps(manifest, indent=2, ensure_ascii=False), encoding="utf-8"
)
print(json.dumps(manifest, indent=2))
本番コードではさらに、保守されている画像ライブラリでピクセルをデコードして寸法と面積を制限し、アプリケーションのマルウェアスキャナーやコンテンツスキャナーを実行してください。
例:プライベートな Files 永続化を使った複数リファレンス編集
この完全な例は、必須の定型ではなく、あくまで一例です。実行していません。まず Pillow をインストールしてください。デフォルトでは機密情報を伏せたドライランを実行します。アップロード済みのプライベート画像ファイル ID を 2 つ指定し、文書化されたリファレンスタグを使って、24 時間のプライベートな永続化を要求します。Files への永続化に失敗した場合でも、API キーを転送せずに一時的な結果を救出します。リファレンスを Files に保存しておけば、公開ソース URL を使わずに済みます。
import hashlib
import ipaddress
import json
import os
import pathlib
import socket
import tempfile
import urllib.error
import urllib.parse
import urllib.request
from PIL import Image, UnidentifiedImageError
API_URL = "https://api.x.ai/v1/images/edits"
ALLOWED_ASSET_HOSTS = {"imgen.x.ai", "files-cdn.x.ai"}
MAX_DOWNLOAD_BYTES = 30 * 1024 * 1024
MAX_RESPONSE_BYTES = 4 * 1024 * 1024
MAX_IMAGE_PIXELS = 20_000_000
Image.MAX_IMAGE_PIXELS = MAX_IMAGE_PIXELS
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
def validate_asset_url(url):
parsed = urllib.parse.urlsplit(url)
if (
parsed.scheme != "https" or parsed.hostname not in ALLOWED_ASSET_HOSTS
or parsed.username or parsed.password or parsed.fragment
):
raise ValueError("Unexpected artifact URL")
addresses = {
info[4][0] for info in socket.getaddrinfo(parsed.hostname, parsed.port or 443)
}
if not addresses or any(not ipaddress.ip_address(value).is_global for value in addresses):
raise ValueError("Artifact host did not resolve exclusively to public addresses")
def atomic_write(path, data):
path.parent.mkdir(parents=True, exist_ok=True)
handle, temporary = tempfile.mkstemp(dir=path.parent, prefix=".manifest-")
try:
with os.fdopen(handle, "wb") as output:
handle = None
output.write(data)
output.flush()
os.fsync(output.fileno())
os.replace(temporary, path)
except Exception:
if handle is not None:
os.close(handle)
try:
os.unlink(temporary)
except FileNotFoundError:
pass
raise
payload = {
"model": "grok-imagine-image-quality",
"prompt": (
"Use <IMAGE_0> as the base product photograph. Replace only its background "
"with the pale sandstone studio texture and warm side-lighting from "
"<IMAGE_1>. Preserve the product geometry, camera angle, crop, label layout, "
"label spelling, colors, reflections, and shadow contact exactly. Do not add "
"props, text, people, or logos."
),
"images": [
{"file_id": os.environ.get("XAI_REFERENCE_FILE_ID_0", "<required-file-id-0>")},
{"file_id": os.environ.get("XAI_REFERENCE_FILE_ID_1", "<required-file-id-1>")},
],
"n": 1,
"aspect_ratio": "4:3",
"resolution": "2k",
"response_format": "url",
"storage_options": {"filename": "product-sandstone.jpg", "expires_after": 86400},
}
body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
request = urllib.request.Request(
API_URL,
data=body,
method="POST",
headers={
"Authorization": "Bearer " + os.environ.get("XAI_API_KEY", ""),
"Content-Type": "application/json",
"Accept": "application/json",
},
)
print({
"model": payload["model"], "references": len(payload["images"]),
"resolution": payload["resolution"],
"request_sha256": hashlib.sha256(body).hexdigest(),
"estimated_usd_snapshot_2026_07_09": 0.09,
})
if os.environ.get("SEND_XAI_REQUEST") != "1":
raise SystemExit("Dry run only; set SEND_XAI_REQUEST=1 after approving the paid call")
if not os.environ.get("XAI_API_KEY"):
raise SystemExit("Set XAI_API_KEY before the paid call")
if any(value["file_id"].startswith("<required-") for value in payload["images"]):
raise SystemExit("Set both XAI_REFERENCE_FILE_ID environment variables")
try:
with urllib.request.urlopen(request, timeout=240) as response:
raw = response.read(MAX_RESPONSE_BYTES + 1)
except urllib.error.HTTPError as exc:
detail = exc.read(64 * 1024).decode("utf-8", "replace")
raise SystemExit(f"xAI HTTP {exc.code}: {detail}") from exc
if len(raw) > MAX_RESPONSE_BYTES:
raise SystemExit("Edit response exceeded configured JSON cap")
try:
document = json.loads(raw)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SystemExit("Edit response was not valid bounded JSON") from exc
items = document.get("data") or []
if len(items) != 1:
raise SystemExit(f"Expected one edit output, received {len(items)}")
item = items[0]
storage_error = item.get("storage_error")
file_output = item.get("file_output") or {}
if not storage_error and not file_output.get("file_id"):
raise SystemExit("Missing persisted file_id")
def download_xai_asset(url: str, destination: pathlib.Path) -> dict:
opener = urllib.request.build_opener(NoRedirect())
current = url
response = None
for _ in range(6):
validate_asset_url(current)
# Deliberately omit XAI_API_KEY on every ephemeral/public asset hop.
asset_request = urllib.request.Request(
current, headers={"Accept": "image/jpeg,image/png,image/webp"}
)
try:
response = opener.open(asset_request, timeout=90)
break
except urllib.error.HTTPError as exc:
if exc.code not in {301, 302, 303, 307, 308}:
raise
location = exc.headers.get("Location")
exc.close()
if not location:
raise ValueError("Artifact redirect omitted Location")
current = urllib.parse.urljoin(current, location)
else:
raise ValueError("Too many artifact redirects")
total = 0
prefix = b""
hasher = hashlib.sha256()
destination.parent.mkdir(parents=True, exist_ok=True)
fd = None
temporary = None
try:
with response:
mime = response.headers.get_content_type()
if mime not in {"image/jpeg", "image/png", "image/webp"}:
raise ValueError(f"Unexpected artifact MIME: {mime}")
declared = response.headers.get("Content-Length")
if declared and int(declared) > MAX_DOWNLOAD_BYTES:
raise ValueError("Artifact exceeds configured cap")
fd, temporary = tempfile.mkstemp(dir=destination.parent, prefix=".download-")
with os.fdopen(fd, "wb") as output:
fd = None
while True:
chunk = response.read(64 * 1024)
if not chunk:
break
total += len(chunk)
if total > MAX_DOWNLOAD_BYTES:
raise ValueError("Artifact exceeded cap while streaming")
prefix += chunk[: max(0, 12 - len(prefix))]
hasher.update(chunk)
output.write(chunk)
output.flush()
os.fsync(output.fileno())
valid = (
prefix.startswith(b"\xff\xd8\xff")
or prefix.startswith(b"\x89PNG\r\n\x1a\n")
or (prefix.startswith(b"RIFF") and prefix[8:12] == b"WEBP")
)
if not valid:
raise ValueError("Unexpected artifact signature")
expected_mime = (
"image/jpeg" if prefix.startswith(b"\xff\xd8\xff")
else "image/png" if prefix.startswith(b"\x89PNG\r\n\x1a\n")
else "image/webp"
)
if mime != expected_mime:
raise ValueError("Artifact MIME/signature mismatch")
try:
with Image.open(temporary) as probe:
dimensions = probe.size
if dimensions[0] * dimensions[1] > MAX_IMAGE_PIXELS:
raise ValueError(f"Artifact exceeds pixel cap: {dimensions}")
decoded_format = probe.format
probe.verify()
with Image.open(temporary) as probe:
probe.load()
except (UnidentifiedImageError, OSError, Image.DecompressionBombError) as exc:
raise ValueError("Artifact failed bounded pixel decode") from exc
expected_format = {"image/jpeg": "JPEG", "image/png": "PNG", "image/webp": "WEBP"}[mime]
if decoded_format != expected_format:
raise ValueError("Artifact decoder/signature mismatch")
os.replace(temporary, destination)
temporary = None
except Exception:
if fd is not None:
os.close(fd)
try:
if temporary is not None:
os.unlink(temporary)
except FileNotFoundError:
pass
raise
return {"bytes": total, "sha256": hasher.hexdigest(), "mime_type": mime,
"dimensions": list(dimensions)}
if not item.get("url"):
raise SystemExit("Missing ephemeral artifact URL")
path = pathlib.Path("grok-imagine-edit") / "product-sandstone.bin"
artifact = download_xai_asset(item["url"], path)
manifest = {
"request_sha256": hashlib.sha256(body).hexdigest(),
"file_id": file_output.get("file_id"),
"file_expires_at": file_output.get("expires_at"),
"storage_error": storage_error,
"usage": document.get("usage"),
"artifact": {"path": str(path), **artifact},
"review_status": "pending",
}
atomic_write(path.with_suffix(".json"), json.dumps(manifest, indent=2).encode("utf-8"))
print(json.dumps(manifest, indent=2))
if storage_error:
print("Generation succeeded; retry Files persistence from the saved bytes without regenerating")
本番環境でピクセルをデコードした後は、.bin ではなく MIME タイプから導出した拡張子を使ってください。xAI が文書化しているアセットホストを変更した場合は、検証してから許可リストを更新してください。
非同期 Batch API
対話型の処理には REST を直接使ってください。キューに入れた一括処理には Batch API を使います。
- 名前を指定して
POST /v1/batchesを呼び出し、batch_idを保持します。 - 一意の
batch_request_id値と、image_generationまたはimage_editのリクエストオブジェクトを指定して、POST /v1/batches/{batch_id}/requestsを呼び出します。 num_pending == 0になるまでGET /v1/batches/{batch_id}をポーリングします。GET /v1/batches/{batch_id}/resultsで結果をページごとに取得します。すべての結果を対応するbatch_request_idに照合し、成功、失敗、キャンセルされた項目を処理します。- 成功した画像 URL はすぐにダウンロードしてください。Batch のメディア URL は 1 時間後に期限切れになります。
Batch 処理は通常 24 時間以内に完了しますが、xAI はこれをベストエフォートとしており、SLA ではありません。Batch の画像・動画リクエストには、Batch の概要に記載されている一般的な割引料金の説明にかかわらず、標準料金が適用されます。Batch リクエストは通常のリアルタイムレート制限を消費しません。1 つの Batch リクエストのペイロード上限は 25 MB です。Batch はキャンセルまたは期限切れになることがあります。完了した結果も、Batch の有効期限までしか利用できません。
Batch リクエスト本文の例(送信すると課金されます):
{
"batch_requests": [
{
"batch_request_id": "catalog-hero-0001",
"batch_request": {
"image_generation": {
"model": "grok-imagine-image-quality",
"prompt": "1:1 catalog hero of a cobalt ceramic vase on warm white paper, soft overhead light, no text or logo",
"aspect_ratio": "1:1",
"resolution": "1k",
"n": 1
}
}
}
]
}
batch_request_id には決定論的なビジネスキーを使い、送信前に永続化してください。xAI は、Batch 内での冪等性と結果の関連付けに役立つと説明しています。直接の画像エンドポイントでは、冪等性ヘッダーやキーは文書化されていません。クライアントの再試行が重複排除されると想定しないでください。
失敗、再試行、照合
xAI では、HTTP ステータスを以下のように大まかに分類しています。
| ステータス | 意味 | 対応 |
|---|---|---|
| 400/422 | 引数またはフィールド形式が無効です。400 はキーの誤りを示す場合もあります | リクエストを修正します。変更せずに再試行しないでください |
| 401/403 | 認証情報の欠落または無効、権限の欠落、キーまたはチームのブロック | ID/ACL/アカウントの状態を修正します |
| 404/405/415 | モデルまたはルートが存在しない、メソッドが誤っている、JSON の Content-Type または本文が欠落している | 統合またはモデルのライフサイクルを修正します |
| 429 | レート制限 | キューに入れ、上限付きの指数バックオフとジッターを使って再試行します |
| 5xx/ネットワーク | 一時的なサービスまたは転送の障害 | 上限を設けたポリシーの範囲内でのみ再試行し、重複のリスクを照合します |
現在のモデルページには、両方の画像モデルについて、すべてのティアで毎秒 5 リクエストと記載されています。TPM 値は公開されていません。チームに適用される実効上限については、コンソールを正としてください。Imagine の上限引き上げは、xAI の営業またはサポートに依頼してください。実測した上限を下回るよう同時実行数を制限し、1 回のリクエストで最大 10 件の出力が課金対象になり得ることに注意してください。
429 または一時的な障害が発生した場合、Retry-After があればそれに従い、なければ完全ジッターを用いた指数バックオフと全体の期限を設定してください。コンテンツモデレーション、無効なプロンプトや参照データ、認証、ストレージ設定に関する失敗を、一時的な障害として再試行しないでください。
送信前に、リクエストハッシュ、予定件数、試行回数、状態を含むクライアントジョブ台帳を作成してください。サーバーが処理を受け付けた後のタイムアウトは、結果が不明確です。直接リクエストを繰り返すと、2 セット目が生成され、課金される可能性があります。手動で再実行する前に、あらゆるレスポンス、正確な usage、アーティファクト/ファイルの記録、アカウントの請求情報を照合してください。決定論的に再生成できるとは主張しないでください。現在の API ではシードが公開されていません。
storage_error、public_url_error、各 data メンバー、想定件数をそれぞれ個別に確認してください。ストレージまたは公開リンクに関する部分的な失敗は、画像を再生成せず、Files エンドポイントを通じて修復してください。
コスト管理
各リクエストの前に最大コストを見積もってください。
generation = n_outputs × output_price(model, resolution)
edit = input_image_count × input_price(model) + n_outputs × output_price(model, resolution)
たとえば、参照画像 3 枚を使う Quality 編集で 2K 出力を 1 件生成する場合、現時点での上限は、税金や契約による調整を除き、3 × $0.01 + 1 × $0.07 = $0.10 です。Quality 2K で画像を 10 枚生成する場合は $0.70 です。顧客に見積もりを提示する直前に、料金を再確認してください。
ジョブごとに、出力件数、モデル、解像度、試行回数、合計 cost_in_usd_ticks の上限を設定してください。見積もりと使用量に差がある場合はアラートを出してください。現在、画像のバッチ処理による割引はありません。参照画像、n、2K Quality、再試行によってコストが増加することを隠してはいけません。
Files の料金は別に発生します。現在公開されている料金は、ストレージが $0.025/GiB/日、ダウンロードが $0.20/GiB です。予想バイト数 × 保持日数に取得量を加えて予算を設定し、実用上必要な最短の TTL を設定してください。配信後は非公開ファイルを削除し、公開 URL を無効にしてください。Files の料金は推論の使用量ティックとは別に照合してください。
品質ゲート
サムネイルやプロバイダーの URL ではなく、デコードしたピクセルを確認してください。リリース候補は次の項目に合格する必要があります。
- ブリーフ: 成果物、アスペクト比、トリミング、安全領域、被写体の数、要求されたバリエーションが正しいこと。
- 参照/編集: すべてのソースと照合し、ベースの形状、アイデンティティ、ポーズ、ラベル、ロゴ、背景、照明、「変更しない」領域を確認すること。
- テキスト: OCR に加えて、スペル、句読点、改行、読みやすさを人が確認すること。
- ビジュアル: 手や顔、繰り返し構造、反射、影、素材の連続性、エッジのハロー、不要な記号、圧縮による欠陥を確認すること。
- ブランド/権利: カラーパレット、マーク、ライセンス対象のアセット、肖像の使用許諾、商標の文脈、禁止されている類似表現を確認すること。
- 技術: 実際の MIME タイプ/シグネチャ、デコード後の寸法、カラーモード/プロファイル、ファイルサイズ、ハッシュ、ストレージ/公開 URL のライフサイクル、マニフェストの完全性を確認すること。
- 安全性: モデレーションの結果に加え、アプリケーションのポリシーと人によるレビューを確認すること。
写実表現、イラスト、タイポグラフィ、製品ラベル、1 件の参照画像を使った編集、3 件の参照画像を使った合成、極端なアスペクト比、1K/2K、n=10、base64、一時ダウンロード、Files の部分的な失敗、429、タイムアウト、モデレーション、Batch の有効期限切れを網羅する固定の回帰テストスイートを維持してください。移行前に、正規のエイリアスと日付付きエイリアスをブラインド比較してください。
データ、権利、安全性、来歴
現在の API セキュリティ FAQ によると、明示的な許可がない限り、xAI は API への入力や出力を学習に使用しません。標準 API のリクエストとレスポンスは、不正利用の監査のために 30 日間保持された後、削除されます。エンタープライズ向けの ZDR を利用でき、x-zero-data-retention レスポンスヘッダーでその状態を確認できます。現在のエンタープライズ規約では、個人データは ZDR が有効な API を通じてのみ送信する必要があります。PHI については、適用される BAA と ZDR の両方が必要です。これは、個人を特定できる情報を含む入力に対する契約上の必須条件として扱い、任意の最適化と見なさないでください。規約には、法律、安全性、セキュリティ、モデレーション、不正利用防止、調査に関する例外もあるため、署名済みの契約が優先されます。
ユーザーコンテンツと派生データは区別してください。ZDR の適用を除き、エンタープライズ規約では、xAI がサービスの利用から匿名化または集計されたデータを作成し、サービスの維持・改善、新製品・機能の開発、研究、ベンチマーク、その他の適法な目的に使用することを認めています。一方で、許可がない限り、ユーザーコンテンツを xAI の AI システムの学習に使用しないとも定めています。両方のルールを正確に開示してください。
監査目的でのデータ保持と、意図的に永続化される Files の出力を混同しないでください。TTL のない非公開ファイルは、削除されるまで残る可能性があります。公開 URL は、期限切れまたは取り消しまでアクセス可能な場合があります。確認したドキュメントには、storage_options が ZDR とどのように連動するかは記載されていません。両方が必要な場合は、書面で回答を得たうえで、互換性があると決めつけずにヘッダーと Files の動作をテストしてください。
現在のエンタープライズ規約では、xAI と顧客の間において、顧客は入力に関する権利を保持し、出力を所有します。xAI は、法令で認められる最大限の範囲で、出力に関する権利を顧客に譲渡します。ただし、これによって著作権保護の対象となること、独占性、正確性、または権利侵害がないことが保証されるわけではありません。出力はユーザー間で類似する場合があります。顧客は、入力に必要な権利と同意を得ている必要があります。また、出力を評価し、人間が生成したものと偽ってはならず、自社またはプロバイダーの ML/AI モデルの学習に出力を使用してはなりません。
個人データ、顔や肖像、未成年者、私有地、著作物、商標、認証情報、規制対象コンテンツ、誤解を招くおそれのある状況について、事前確認のゲートを設けてください。xAI の AUP では、特に、知的財産権・プライバシー権・パブリシティ権の侵害、実在人物を非同意で性的に加工すること、欺瞞的ななりすまし、児童の性的搾取、名誉毀損・誤った印象の流布、詐欺・偽造、ドクシング・ストーキング、重大な影響を伴う自動意思決定、安全対策の回避、埋め込み済みの来歴メタデータや透かしの削除・回避を禁止しています。
肖像や参照画像の使用について、記録に残る同意を取得してください。AI が関与していることを開示し、埋め込み済みの来歴情報や透かしを保持したうえで、アプリケーションレベルの署名付きマニフェストを追加してください。確認した画像 API の契約では、C2PA 認証情報や視認可能な透かしは保証されていないため、いずれも保証されていると主張しないでください。一般公開、商用、事実に関するもの、政治、医療、法律、金融、本人確認に関わるもの、またはその他重大な結果を伴う用途では、人間によるレビューが必須です。
ファーストパーティの情報源
2026-07-09確認:
- 画像 REST スキーマ:https://docs.x.ai/developers/rest-api-reference/inference/images
- 画像生成:https://docs.x.ai/developers/model-capabilities/images/generation
- 画像編集:https://docs.x.ai/developers/model-capabilities/images/editing
- 複数画像の編集:https://docs.x.ai/developers/model-capabilities/images/multi-image-editing
- Imagine の概要:https://docs.x.ai/developers/model-capabilities/imagine
- 品質モデル:https://docs.x.ai/developers/models/grok-imagine-image-quality
- 標準モデル:https://docs.x.ai/developers/models/grok-imagine-image
- 画像モデルの確認:https://docs.x.ai/developers/rest-api-reference/inference/models
- Files の入力:https://docs.x.ai/developers/model-capabilities/imagine/files/inputs
- Files の出力とライフサイクル:https://docs.x.ai/developers/model-capabilities/imagine/files/outputs
- 公開 URL:https://docs.x.ai/developers/files/public-urls
- Batch API:https://docs.x.ai/developers/advanced-api-usage/batch-api
- 料金とコスト追跡:https://docs.x.ai/developers/pricing and https://docs.x.ai/developers/cost-tracking
- レート制限とエラー:https://docs.x.ai/developers/rate-limits and https://docs.x.ai/developers/debugging
- 廃止ガイド:https://docs.x.ai/developers/migration/may-15-retirement
- API セキュリティ/ZDR:https://docs.x.ai/developers/faq/security
- エンタープライズ利用規約と DPA:https://x.ai/legal/terms-of-service-enterprise and https://x.ai/legal/data-processing-addendum
- 許容される利用に関するポリシー:https://x.ai/legal/acceptable-use-policy
- プロンプト/ユースケースのガイダンス:https://x.ai/grok/use-cases/image-generation
- Quality Mode の発表:https://x.ai/news/grok-imagine-quality-mode
日付付きの相違点と矛盾点
- 直接取得する一時的な画像 URL の有効期間は「短期間」としか説明されていません。1時間という記載があるのは Batch URL であり、直接取得する URL ではありません。
- 直接編集におけるソース画像のサイズ上限、編集時の
nの最大値、直接リクエストの冪等性、決定論的なシード、マスク/インペイント入力、出力形式の選択、保証された来歴証明情報については、文書化されていません。 - Imagine の概要の見出しでは Quality が「$0.05/image」とされていますが、現在の詳細な料金/モデル表では Quality 2K の料金は「$0.07」です。詳細に記載された現在の料金を使用してください。
- Batch の概要では一般的に料金の割引について説明されていますが、現在の料金ページには、画像/動画の Batch リクエストには標準料金が適用されると明記されています。画像には標準料金を使用してください。
- 公開ページでは、エンタープライズ向けにリージョン/データレジデンシーの選択肢が案内されていますが、一般公開されている画像リクエストのリージョン選択機能はありません。データレジデンシーについては契約で確認してください。
- Files と ZDR の併用可否は確立されていません。プロバイダーの確認なしに、両方が利用できると約束しないでください。