runway-image について
runway-imageは、Runway公式APIで静止画を制作するスキルです。クリエイティブチームや開発者向けに、モデル選択、参照画像を使った編集、費用承認、出力確認を案内します。
主な機能
- 01テキストからの画像生成
- 02参照画像による生成と編集
- 03RunwayネイティブGen-4モデルの選択
- 04非同期タスクのポーリングとキャンセル
- 05費用・権利・出力の確認ガイド
活用シーン
- 01文章の要件から画像を作成
- 02商品やスタイルの参照画像で反復
- 03指定した特徴を保ちながら画像を編集
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ calesthio/ generative-media-skills --skill runway-imageインストール後、Claude Code で/runway-imageを入力して読み込まれたか確認します。
同梱ファイルEVAL.md
詳細
Runway Image
Runway の公式 API を使用して静止画像を作成します。Runway ネイティブの gen4_image と gen4_image_turbo を優先し、パートナーの画像モデルは明確にラベル付けしてください。このスキルの対象には、Runway の動画エンドポイント、ウェブアプリの UI、非公式のラッパー、サードパーティのゲートウェイは含まれません。
最初の応答
コードを書く前、または課金対象のタスクを開始する前に、次の内容を確認してください。
- 希望する出力:被写体、環境、構図、スタイル、アスペクト比/解像度、枚数、受け入れ基準。
- モード:テキストのみの生成、参照画像に基づく生成、または元画像を参照画像として使用する自然言語による編集。
- 参照画像:パス/URL、権利と同意、各画像の役割、同一性を維持する必要があるかどうか。
- 納品:保存先、命名規則、メタデータ/マニフェスト、予算の上限。
- 実行環境:直接 REST、Python SDK、Node SDK。通常のアプリケーションコードでは公式 SDK を優先してください。
必要なクリエイティブの詳細が不足している場合は、具体的なデフォルトを提案し、コスト、権利、出力に大きく影響する質問だけをしてください。明示的な承認なしに有料の API 呼び出しを行ってはいけません。
現在の仕様スナップショット
2026-07-09 時点の、公式ドキュメントおよび公式 Python SDK に基づく確認済みの情報です。本番環境へのデプロイ前に、API リファレンス、モデルカタログ、価格、制限、変更履歴、利用規約、利用ポリシーを再確認してください。これらは変更される可能性があります。
エンドポイントと認証
| 目的 | メソッドとエンドポイント | 結果 |
|---|---|---|
| 画像タスクを作成 | POST https://api.dev.runwayml.com/v1/text_to_image | { "id": "..." } |
| タスクを取得 | GET https://api.dev.runwayml.com/v1/tasks/{id} | 識別可能なタスクオブジェクト |
| タスクをキャンセルまたは削除 | DELETE https://api.dev.runwayml.com/v1/tasks/{id} | 実行中のタスクをキャンセルするか、終了済みのタスクを削除 |
| 一時アップロードを開始 | POST https://api.dev.runwayml.com/v1/uploads | マルチパートアップロードの手順と runwayUri |
API 呼び出しには次のヘッダーを指定してください。
Authorization: Bearer $RUNWAYML_API_SECRET
Content-Type: application/json
X-Runway-Version: 2024-11-06
/v1 は URL の名前空間であり、API バージョンではありません。Runway は日付形式のヘッダーで動作をバージョン管理しており、新しいバージョンの導入後、古いバージョンを 4 か月間サポートするとしています。ヘッダーを固定してテストし、省略しないでください。
組織に紐付くキーは、シークレットマネージャーでサーバー側に保管してください。Runway がキーを表示するのは一度だけです。組織のメンバーを削除しても、そのメンバーのキーは無効にならないため、キーは別途無効化またはローテーションしてください。ベアラートークンを参照先のホストや出力 CDN の URL に送信してはいけません。
Runway ネイティブのモデル選択
| モデル | 入力 | 用途 | コストのスナップショット |
|---|---|---|---|
gen4_image | テキスト。任意で参照画像 1~3 枚 | テキストのみの作業、最高品質のネイティブ生成、編集、スタイル/同一性/構図の制御 | 720p 画像 1 枚あたり 5 クレジット、1080p 画像 1 枚あたり 8 クレジット |
gen4_image_turbo | テキストと必須の参照画像 1~3 枚 | 高速で低コストな、参照画像に基づく反復作業 | 対応するすべての解像度で、画像 1 枚あたり 2 クレジット |
1 クレジットは、適用される税金を除いて $0.01 です。モデレーションされた生成の料金は、成功した生成と同じです。画像の寸法だけから、特定のアスペクト比に適用される 720p/1080p の料金区分を推測しないでください。現在の料金ページまたは開発者ポータルで確認してください。
Turbo の速度、相対的な品質、ベンチマークスコアに関するプロバイダーのローンチ時の主張は、マーケティング上の主張であり、保証ではありません。代表的なプロンプトと参照画像を使ってベンチマークしてください。
正確なネイティブリクエストスキーマ
どちらのネイティブモデルも、次の内容を受け付けます。
model:gen4_imageまたはgen4_image_turboのいずれかに限ります。promptText:必須で、空でないこと。最大1,000 UTF-16コード単位です。ratio:必須で、次のいずれかに限ります。
1024:1024 1080:1080 1168:880 1360:768
1440:1080 1080:1440 1808:768 1920:1080
1080:1920 2112:912 1280:720 720:1280
720:720 960:720 720:960 1680:720
referenceImages:必須のuriと任意のtagを持つオブジェクトです。gen4_imageでは0~3個、Turboでは1~3個が必須です。seed:任意の整数0..4294967295です。同一のリクエストとシードを指定すると、似た結果になりますが、ビット単位で完全に一致するとは限りません。contentModeration.publicFigureThreshold:任意で、autoまたはlowを指定します。lowは、認識可能な著名人に対する制限を緩和しますが、安全対策の回避、同意の付与、権利の確認を意味するものではありません。
現在のAPIスキーマでは、参照タグは小文字3~16文字に制限され、^[a-z][a-z0-9_]+$ に一致する必要があります。promptText では、@product_ref のように指定してください。検証日時点で、公式のスタートガイドの例には大文字のタグも記載されており、ドキュメントに不整合があるため、タグはこの保守的な形式に正規化してください。
Gen-4の静止画に対する、個別のマスク/インペイント/編集用ルートはドキュメントに記載されていません。編集する場合は、元画像を参照として送信し、不変条件を改めて明示しながら、変更後の結果を説明してください。マスク、ノイズ除去の強度、ネガティブプロンプト、ControlNet、重み、画像強度の各フィールドを作り出してはいけません。
Runwayが公開するパートナーモデル
公開モデルカタログには現在、同じルート上に gemini_image3_pro、gpt_image_2、gemini_2.5_flash が記載されています。これらはパートナーモデルであり、RunwayネイティブのGen-4ではありません。スキーマとプロバイダーの動作はそれぞれ異なります。
| モデル | 主な仕様 | 現在の価格 |
|---|---|---|
gemini_image3_pro | 最大5,500 UTF-16コード単位、参照画像は最大14個(human は最大5個、object は最大9個)、outputCount は1または4、1K/2K/4Kの比率 | 1K/2Kでは20クレジット、4Kでは40クレジット |
gpt_image_2 | 最大32,000文字、参照画像は最大16個、outputCount は1~10、quality は low/medium/high/auto、不透明/autoの背景(透過には非対応) | 品質と解像度に応じて、出力1件あたり1~41クレジット。解像度を auto にすると4Kとして課金されます |
gemini_2.5_flash | 現在のAPIスキーマでは最大5,500 UTF-16コード単位、参照画像は最大3個、ドキュメントに記載された比率は10種類 | 画像1枚あたり5クレジット |
公式Python SDKのスキーマには、2026-07-09時点で gemini_image3.1_flash が公開されていましたが、公開モデルカタログと料金ページには記載されていませんでした。現在のAPIスキーマとモデルカタログには seedream5_pro が記載されていますが、料金ページと現在のPython SDKには記載されていません。リクエストスキーマ、カタログ、料金、SDK/アカウントのアクセス状況に整合性が確認できるか、Runwayサポートから不足している仕様の確認が取れるまでは、どちらもコスト管理が必要な本番リクエストでは利用できないものとして扱ってください。あるモデルのフィールドを別のモデルに流用してはいけません。
入力メディア
参照用の uri には、次の形式を指定できます。
- HTTPS URL。
data:image/...;base64,...形式のデータ URI。- 一時アップロードで返される
runway://...URI。
対応している静止画形式は JPEG(image/jpeg または image/jpg)、PNG、WebP です。GIF はサポートされていません。現在の制限は次のとおりです。
| トランスポート | 画像の上限 | 運用上の注意 |
|---|---|---|
| HTTPS URL | 16 MB | URL は 2,048 文字以下。ホストには IP ではなくドメインを指定。リダイレクト不可。HEAD と Content-Length が必要。メディアの Content-Type は正確に指定。レスポンスは 200 |
| データ URI | エンコード後 5 MB | Base64 によるサイズ増加を考慮すると、バイナリは約 3.3 MB 以下にする必要があります |
| 一時アップロード | 200 MB | 最小 512 バイト。購入済みの API クレジットが必要。レート制限あり。runway:// URI の有効期間は 24 時間 |
Runway の入力ガイドでは、参照画像はおおむね 640px から 4K の範囲を推奨していますが、Gen-4 の参照画像に対する 8,000 ピクセルの寸法ルールは公開されていません。鮮明で圧縮を最小限に抑えた画像を優先し、被写体は 1 つに絞り、照明は均一でニュートラルにし、透かしや重ね合わせたテキストがない画像を使用してください。Runway は入力画像をリサイズする場合があります。このエンドポイントに、アップスケーラーの別エンドポイントに適用される 8,000 ピクセルの上限を転用しないでください。
顧客がホストする参照 URL には、リダイレクトせずに HEAD と GET に応答し、正確な Content-Type と Content-Length を返し、RunwayML API/ というプレフィックスを持つ Runway の User-Agent を許可する、専用の署名付きアセットエンドポイントを実装してください。プライベートネットワーク上のアセットを公開しないでください。
プロンプトと参照画像の設計
テキストのみの生成
表示したいものを、肯定的かつ視覚的に表現してください。
[medium/style] of [subject and defining details] in [scene].
[composition and angle]. [lighting]. [palette]. [focus/lens/material/mood].
例:
Editorial product photograph of a matte cobalt travel mug on pale limestone.
Three-quarter view, centered with generous negative space on the right. Soft north-window
light, crisp rim highlight, restrained blue and warm gray palette, 85mm lens, shallow depth.
完全な文章と具体的な視覚表現を使用してください。Gen-4 は個別のネガティブプロンプト用フィールドをサポートしていません。否定的な表現は肯定的な形に書き換えてください(髪のない人物 ではなく bald person)。「please generate」のような会話的な前置きは避けてください。描画する正確なテキストを引用符で指定し、スペルを手動で確認してください。
参照画像と編集
各参照画像に 1 つの役割を割り当て、安定したスネークケースの小文字タグを付けてください。
{
"referenceImages": [
{"uri": "https://assets.example.com/mug.jpg", "tag": "product_ref"},
{"uri": "https://assets.example.com/light.jpg", "tag": "style_ref"}
],
"promptText": "@product_ref unchanged in shape, logo placement, and cobalt color, photographed with the soft window lighting and pale stone setting of @style_ref. Three-quarter product view with clear negative space on the right."
}
タグのない参照画像は全体的なスタイルに影響する場合があります。タグ付きの参照画像では、役割がより明確になります。キャラクターの一貫性を保つには、均一な照明で顔の特徴が隠れていない、すっきりとしたニュートラルな画像を使用してください。スケッチや注釈を付けた構図も別の参照画像として使用できますが、API には通常の画像として渡されます。スケッチ専用の制御フィールドはありません。
編集では、対象画像、変更内容、維持する要素を説明してください。
@source_ref in the same camera position and crop. Replace the background with a warm,
minimal studio wall and add soft late-afternoon side light. Preserve the person's identity,
facial expression, hairstyle, clothing silhouette, and hand position.
複雑な変更は一度に 1 つだけ行ってください。承認済みの結果を次の参照画像として再び入力してください。これは経験則に基づくワークフローであり、同一性を確実に固定するものではありません。
反復と QA
プロンプト、モデル、アスペクト比、シード、参照画像のハッシュ/URI、タスク ID、費用の見積もり、出力のハッシュ、レビュー担当者の判定を記録するブランチ台帳を使用してください。ブランチごとに変更する要素は 1 つにしてください。
- 構図と被写体を固定します。
- 照明と配色を調整します。
- スタイル、素材、レンズを調整します。
- 新しい参照画像ベースの編集で、局所的な意味上の問題を 1 つ修正します。
- 承認済みのソースハッシュを固定し、納品用アスペクト比でレンダリングします。
すべての出力を原寸で確認し、プロンプトへの適合性、人体の構造/形状、同一性/製品の忠実性、テキスト/ロゴの正確さ、エッジのアーティファクト、意図しない人物/マーク、プライバシー/権利上の懸念、納品寸法をチェックしてください。類似したシードは探索の補助であり、再現性の証明にはなりません。
非同期ライフサイクル
作成時に返されるのはタスク ID のみです。GET /v1/tasks/{id} をポーリングしてください。5 秒に 1 回より頻繁に有意な更新が得られることは期待しないでください。
| ステータス | アクション |
|---|---|
PENDING | 5 秒以上待ってからポーリングします |
THROTTLED | キューに入った有効なタスクです。待ってからポーリングし、再送信しないでください |
RUNNING | progress を記録し、待ってからポーリングします |
SUCCEEDED | すべての出力を直ちにダウンロードし、マニフェストを保存します |
FAILED | failureCode と人が読める形式の failure を確認し、失敗時のポリシーを適用します |
CANCELLED | 停止します。成功として扱わないでください |
一時的な非 200 応答には、ジッターを加えたポーリングと指数バックオフを使用してください。公式 SDK の待機ヘルパーのタイムアウトはデフォルトで 10 分です。タイムアウトや中断によってローカルのポーリングは停止しますが、リモートタスクはキャンセルされません。実際にキャンセルする場合に限り、DELETE /v1/tasks/{id} を呼び出してください。終了状態のタスクを削除すると、そのタスクデータと出力は API から利用できなくなります。
出力 URL は 24~48 時間以内に有効期限が切れます。保持されている成功済みタスクを再取得すると新しい URL を発行できますが、これは永続的なストレージではありません。速やかにダウンロードして、自分で管理するバケットに保存し、プロバイダー署名付き URL をエンドユーザーに直接公開しないでください。
REST を直接使う完全な例
この Python の例では、テキストのみの gen4_image タスクを 1 つ作成し、ポーリング前にその ID を記録して、すべてのタスクステータスを処理し、API 認証情報を転送せずに出力をダウンロードします。事前に Pillow をインストールしてください。デフォルトでは機密情報を伏せたドライランが実行され、SEND_RUNWAY_REQUEST=1 を設定した場合にのみ課金対象になります。まずモデル、比率、プロンプト、許可リスト、予算を確認してください。
#!/usr/bin/env python3
import hashlib, ipaddress, json, os, random, socket, tempfile, time
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse
from urllib.request import HTTPRedirectHandler, Request, build_opener
from PIL import Image, UnidentifiedImageError
BASE = "https://api.dev.runwayml.com"
VERSION = "2024-11-06"
KEY = os.environ.get("RUNWAYML_API_SECRET", "")
OUT = Path(os.environ.get("RUNWAY_OUTPUT_DIR", "runway-output")).resolve()
MAX_BYTES = 25 * 1024 * 1024
MAX_API_BYTES = 4 * 1024 * 1024
MAX_IMAGE_PIXELS = 20_000_000
Image.MAX_IMAGE_PIXELS = MAX_IMAGE_PIXELS
ALLOWED_HOSTS = frozenset(
x.strip().lower().rstrip(".")
for x in os.environ.get("RUNWAY_OUTPUT_HOSTS", "").split(",") if x.strip()
)
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
opener = build_opener(NoRedirect)
def api(method, path, body=None, timeout=60):
data = None if body is None else json.dumps(body).encode()
req = Request(BASE + path, data=data, method=method, headers={
"Authorization": f"Bearer {KEY}",
"X-Runway-Version": VERSION,
"Content-Type": "application/json",
"Accept": "application/json",
})
with opener.open(req, timeout=timeout) as response:
raw = response.read(MAX_API_BYTES + 1)
if len(raw) > MAX_API_BYTES:
raise ValueError("Runway API response exceeded configured JSON cap")
return json.loads(raw)
def atomic_json(path, value):
fd, tmp = tempfile.mkstemp(dir=path.parent, prefix=path.name, suffix=".tmp")
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
fd = None
json.dump(value, f, indent=2, sort_keys=True)
f.flush(); os.fsync(f.fileno())
os.replace(tmp, path)
finally:
if fd is not None: os.close(fd)
if os.path.exists(tmp): os.unlink(tmp)
def task_snapshot(task):
# Signed output URLs are bearer credentials. Never log or persist them.
return {
key: task[key] for key in (
"id", "status", "createdAt", "progress", "failureCode", "failure"
) if key in task
} | {"outputCount": len(task.get("output") or [])}
def wait_for_task(task_id, timeout_s=900):
deadline, delay = time.monotonic() + timeout_s, 5.0
while time.monotonic() < deadline:
try:
task = api("GET", f"/v1/tasks/{task_id}")
delay = 5.0
except HTTPError as e:
if e.code not in (429, 502, 503, 504): raise
delay = min(30.0, max(5.0, delay * 2))
time.sleep(delay + random.uniform(0, delay * 0.5)); continue
except URLError:
delay = min(30.0, max(5.0, delay * 2))
time.sleep(delay + random.uniform(0, delay * 0.5)); continue
atomic_json(OUT / "task.json", task_snapshot(task))
status = task["status"]
if status == "SUCCEEDED": return task
if status == "FAILED":
raise RuntimeError(f"task failed: {task.get('failureCode')}: {task.get('failure')}")
if status == "CANCELLED": raise RuntimeError("task cancelled")
if status not in ("PENDING", "THROTTLED", "RUNNING"):
raise RuntimeError(f"unknown task status: {status}")
time.sleep(5.0 + random.uniform(0, 1.0))
raise TimeoutError("local wait expired; remote task was NOT cancelled")
def checked_output_url(url):
p = urlparse(url)
host = (p.hostname or "").lower()
if (p.scheme != "https" or p.username or p.password or p.fragment or not host
or p.port not in (None, 443)):
raise ValueError("unsafe output URL")
if host.rstrip(".") not in ALLOWED_HOSTS:
raise ValueError(f"output host not allowlisted: {host}")
for info in socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM):
ip = ipaddress.ip_address(info[4][0])
if not ip.is_global: raise ValueError(f"non-public output address: {ip}")
return url
def download(url, index):
# Deliberately no Authorization header and no redirects.
req = Request(checked_output_url(url), headers={"Accept": "image/*"})
with opener.open(req, timeout=60) as response:
ctype = response.headers.get_content_type()
ext = {"image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp"}.get(ctype)
if not ext: raise ValueError(f"unexpected media type: {ctype}")
declared = int(response.headers.get("Content-Length", "0") or 0)
if declared and declared > MAX_BYTES: raise ValueError("output too large")
fd, tmp = tempfile.mkstemp(dir=OUT, suffix=".part")
digest, total = hashlib.sha256(), 0
try:
with os.fdopen(fd, "wb") as f:
fd = None
while chunk := response.read(1024 * 1024):
total += len(chunk)
if total > MAX_BYTES: raise ValueError("output exceeded byte cap")
digest.update(chunk); f.write(chunk)
f.flush(); os.fsync(f.fileno())
head = Path(tmp).read_bytes()[:12]
valid = (head.startswith(b"\xff\xd8\xff") if ext == ".jpg" else
head.startswith(b"\x89PNG\r\n\x1a\n") if ext == ".png" else
head[:4] == b"RIFF" and head[8:12] == b"WEBP")
if not valid: raise ValueError("media signature mismatch")
try:
with Image.open(tmp) as probe:
dimensions = probe.size
if dimensions[0] * dimensions[1] > MAX_IMAGE_PIXELS:
raise ValueError(f"output exceeds pixel cap: {dimensions}")
decoded_format = probe.format
probe.verify()
with Image.open(tmp) as probe:
probe.load()
except (UnidentifiedImageError, OSError, Image.DecompressionBombError) as exc:
raise ValueError("output failed bounded pixel decode") from exc
expected_format = {".jpg": "JPEG", ".png": "PNG", ".webp": "WEBP"}[ext]
if decoded_format != expected_format:
raise ValueError("decoder and MIME/signature disagree")
final = OUT / f"image-{index:02d}{ext}"
os.replace(tmp, final)
tmp = None
return {"path": str(final), "bytes": total, "sha256": digest.hexdigest(),
"mediaType": ctype, "dimensions": list(dimensions)}
finally:
if fd is not None: os.close(fd)
if tmp is not None and os.path.exists(tmp): os.unlink(tmp)
request_body = {
"model": "gen4_image",
"ratio": "1920:1080",
"promptText": "Editorial product photograph of a matte cobalt travel mug on pale limestone. Three-quarter view, soft north-window light, crisp rim highlight, restrained blue and warm gray palette, 85mm lens.",
}
request_bytes = json.dumps(request_body, separators=(",", ":")).encode()
print({"model": request_body["model"], "ratio": request_body["ratio"],
"request_sha256": hashlib.sha256(request_bytes).hexdigest(),
"estimated_credits_snapshot_2026_07_10": 8})
if os.environ.get("SEND_RUNWAY_REQUEST") != "1":
raise SystemExit("Dry run only; set SEND_RUNWAY_REQUEST=1 after approving the paid call")
if not KEY or not ALLOWED_HOSTS:
raise SystemExit("Set RUNWAYML_API_SECRET and exact RUNWAY_OUTPUT_HOSTS")
OUT.mkdir(parents=True, exist_ok=True)
# Do not blindly retry this POST after a timeout: its outcome may be ambiguous and a retry may duplicate cost.
try:
created = api("POST", "/v1/text_to_image", request_body)
except HTTPError as e:
raise SystemExit(f"create rejected with definite HTTP {e.code}; fix or classify before retry")
except (URLError, TimeoutError) as e:
raise SystemExit(f"create outcome unknown; reconcile before retrying: {e}")
task_id = created["id"]
atomic_json(OUT / "job.json", {"taskId": task_id, "request": request_body, "createdAt": time.time()})
task = wait_for_task(task_id)
artifacts = [download(url, i) for i, url in enumerate(task["output"], 1)]
atomic_json(OUT / "manifest.json", {"taskId": task_id, "artifacts": artifacts})
print(json.dumps({"taskId": task_id, "artifacts": artifacts}, indent=2))
RUNWAY_OUTPUT_HOSTS には、アカウントで確認し、セキュリティレビューを済ませた正確な出力ホスト名を設定してください。.cloudfront.net のような共有テナンシーのサフィックスを許可しないでください。標準ライブラリを使ったこの例では接続前にパブリック DNS を確認しますが、名前解決から接続までの間に DNS が変わる可能性があります。悪意のあるマルチテナント入力を扱う場合は、TLS SNI と証明書の検証を維持しつつ、検証済みアドレスを固定するエグレスプロキシ経由でダウンロードしてください。
公式 SDK を使った完全なリファレンス例
runwayml をインストールし、API キーと承認済みの HTTPS/runway:// 参照先を 2 つ設定してから、この Turbo の例を使用してください。デフォルトでは機密情報を伏せたドライランが実行され、SEND_RUNWAY_REQUEST=1 を設定した場合にのみ課金対象になります。Runway は公開された作成時の冪等性契約を文書化していないため、max_retries=0 は意図的な設定です。
import os
import hashlib
import json
import random
import time
from pathlib import Path
from runwayml import RunwayML
product_uri = os.environ.get("RUNWAY_PRODUCT_URI", "<required-product-uri>")
style_uri = os.environ.get("RUNWAY_STYLE_URI", "<required-style-uri>")
request_summary = {
"model": "gen4_image_turbo", "ratio": "1920:1080",
"reference_hashes": [hashlib.sha256(value.encode()).hexdigest()
for value in (product_uri, style_uri)],
"estimated_credits_snapshot_2026_07_10": 2,
}
print(json.dumps(request_summary, indent=2))
if os.environ.get("SEND_RUNWAY_REQUEST") != "1":
raise SystemExit("Dry run only; set SEND_RUNWAY_REQUEST=1 after approving the paid call")
if not os.environ.get("RUNWAYML_API_SECRET") or product_uri.startswith("<required-") or style_uri.startswith("<required-"):
raise SystemExit("Set RUNWAYML_API_SECRET and both approved reference URIs")
create_client = RunwayML(api_key=os.environ["RUNWAYML_API_SECRET"], max_retries=0)
created = create_client.text_to_image.create(
model="gen4_image_turbo",
ratio="1920:1080",
prompt_text=(
"@product_ref unchanged in shape, cobalt color, and logo placement, photographed "
"with the soft window light and pale stone setting of @style_ref. Three-quarter "
"product view with clear negative space on the right."
),
reference_images=[
{"uri": product_uri, "tag": "product_ref"},
{"uri": style_uri, "tag": "style_ref"},
],
)
Path("runway-task-id.txt").write_text(created.id + "\n", encoding="utf-8")
print("persisted task id:", created.id)
# Use a separate retrying client only for idempotent reads. Poll manually because
# the synchronous SDK wait helper in the reviewed version did not terminate on CANCELLED.
read_client = RunwayML(api_key=os.environ["RUNWAYML_API_SECRET"], max_retries=2)
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
task = read_client.tasks.retrieve(created.id)
status = task.status
print(created.id, status)
if status == "SUCCEEDED":
print("outputs ready; pass in-memory URLs to the secure downloader above")
break
if status == "FAILED":
raise RuntimeError(f"generation failed: {getattr(task, 'failure_code', None)}")
if status == "CANCELLED":
raise RuntimeError("generation cancelled")
if status not in ("PENDING", "THROTTLED", "RUNNING"):
raise RuntimeError(f"unknown task status: {status}")
time.sleep(5 + random.uniform(0, 1))
else:
raise TimeoutError("local polling timed out; the provider task was NOT cancelled")
本番環境で使用する前に、待機する前に created.id を永続的に保存し、署名付き URL を表示または返すのではなく、上記のセキュアなダウンローダー/マニフェストのパターンを使用してください。
エラー、再試行、重複課金の防止
HTTP レイヤー
| レスポンス | 再試行するか | アクション |
|---|---|---|
| 400、401、404、405 | いいえ | リクエスト、認証情報、ID、またはメソッドを修正します |
| 429 | はい、回数を制限 | クォータ/予算を守り、最大 50% のジッターを加えた指数バックオフを行います |
| 502、503、504 | はい、回数を制限 | ジッターを加えてバックオフします |
公式 SDK は、デフォルトで接続エラー、408、409、429、および 5xx を 2 回再試行します。読み取りには有用ですが、サーバーに到達した作成リクエストの応答が失われた場合、再試行によって重複する可能性があります。2026-07-09 時点で、エンドポイント固有の冪等性キーや照合 API は、ファーストパーティの公開ドキュメントでは確認できませんでした。そのため、次の点に注意してください。
- 重複課金が問題となる場合は、作成時に
max_retries=0を優先してください。 - 成功応答を受け取ったら、直ちにタスク ID を保存してください。
- アプリケーションのジョブキーと台帳を使い、ローカルでの重複送信を防いでください。
GETは安全に再試行できますが、THROTTLEDのタスクを再送信しないでください。- 作成結果が不明な場合は、推測せずに処理を停止し、クレジットまたはサポート窓口を通じて照合してください。
タスク失敗レイヤー
| 失敗の種類 | ポリシー |
|---|---|
SAFETY.INPUT.*、INPUT_PREPROCESSING.SAFETY.TEXT | 入力を変更せずに再試行しないでください。入力を修正または消去してください。入力の安全性に関する失敗は返金されません。 |
SAFETY.OUTPUT.* | 繰り返し実行しないでください。プロンプトや参照情報、ポリシーを見直してください。モデレーションされた生成にも料金が発生します。 |
ASSET.INVALID | URL、MIME タイプ、寸法、サイズ、またはエンコーディングを修正してください。 |
INPUT_PREPROCESSING.INTERNAL | 上限を設けたバックオフで、後ほど再試行してください。 |
THIRD_PARTY.UNAVAILABLE | 再試行する前に待機してください。パートナーモデルへの依存関係に適用されます。 |
INTERNAL.BAD_OUTPUT.* | 透かしや重ねて表示されたテキストを削除し、モデルにプロンプトを書かせるよう求めることを避け、明示的なテキストのリクエストを簡潔にしてから、リクエストを変更して再試行してください。 |
INTERNAL またはコードなし | 間隔を空けて回数を制限して再試行し、証拠とタスク ID を保持してください。 |
コンテキストを示さずに、生の failure テキストをエンドユーザーに直接表示しないでください。これは診断文字列であり、変更される可能性があります。
制限とスケジューリング
制限は組織およびティアごとに適用されます。すべての画像生成モデルは、同じ画像同時実行枠を共有します。受け付けられたタスクが上限を超えると THROTTLED になり、作成順にほぼ沿ってキューに追加されます。1 日あたりの生成制限は直近 24 時間のローリングウィンドウで適用され、超過すると HTTP 429 が返されます。Runway は現在、1 日あたりの制限内であれば、1 分あたりのリクエスト数の上限を公開していませんが、これは無制限にバーストリクエストを作成してよいという意味ではありません。
公開されているティア表は頻繁に変わり、アカウントによって異なる場合があります。開発者ポータルを信頼できる情報源として参照し、プロバイダーの制限より低い値で、内部の同時実行数上限と 1 日・1 か月あたりのクレジット予算を設定してください。本番環境の SLA に必要なカスタム容量または保証付き容量については、Runway にお問い合わせください。
権利、プライバシー、安全性、および来歴
これは運用上のガイダンスであり、法的助言ではありません。
- 入力と出力については、お客様が責任を負います。著作権、商標権、プライバシー権、パブリシティ権、および生体情報/肖像に関する許諾を取得し、個人を特定できる人物については、十分な説明に基づく同意を得てください。公人であることをもって権利が生じるわけではありません。
- Standard Terms(最終更新日:2026-05-11)では、Runway は入力や出力の所有権を主張せず、規約に準拠している限り、出力の商用利用を制限しないとされています。ただし、出力が固有のものとは限らず、権利も保証されません。また、API アプリケーションには、
runwayml.comへのリンクを付けて Powered by Runway と表示すること、およびエンドユーザーを保護する規約を設けることが求められています。 - 現在の API マーケティングページでは、「No training on your data」と「Zero-data retention ... as default」の両方がうたわれていますが、Standard Terms では、広範なライセンスに基づいて入力と出力をシステムのトレーニング/改善に使用できるとされています。マーケティングページには、API 固有の削除範囲や SLA の定義がありません。これらの記述は、公開資料の中で矛盾しています。署名済みの注文書、エンタープライズ規約/DPA、および Runway からの書面による確認を優先してください。マーケティング文言だけを根拠に、トレーニングを行わないことやゼロ保持を約束しないでください。
- 公開資料には、このエンドポイントに一般的に適用される API の保持期間、削除 SLA、処理地域の一覧、または顧客が選択可能なデータレジデンシーが記載されていません。24~48 時間有効な出力リンクや、24 時間有効な
runway://URI はアクセス可能な期間であり、バックエンドでの完全な保持についての説明ではありません。規制対象または機微なワークロードで使用する前に、これらの不明点を契約で解決してください。 - パートナーモデルを使用する場合、サブプロセッサが追加されます。Runway のエンタープライズ FAQ では、エンタープライズ規約/DPA が選択されたサードパーティモデルを対象とし、プロバイダーは契約上、顧客コンテンツをトレーニングに使用できないと説明されています。ご利用のアカウントについて、対象となる API モデル、プロバイダー、処理地域、サブプロセッサ一覧、および契約内容を確認してください。
- Runway は、自動システムと、場合によっては人による確認を通じて、テキスト、メディア入力、および出力をモデレーションします。エンドユーザーのコンテンツは事前にモデレーションしてください。モデレーション対象となるリクエストを繰り返すと、利用停止につながる可能性があります。
- 児童の性的虐待/性的対象化、同意のない親密なコンテンツ、深刻な暴力、詐欺/欺瞞、違法行為、プライバシー侵害、憎悪的/嫌がらせ的な虐待、および安全対策を回避しようとする行為に関する禁止事項を含め、最新の Usage Policy に従ってください。ポリシー全文を確認してください。この要約は網羅的なものではありません。
- Runway によると、生成された出力には C2PA の来歴メタデータが含まれます。元のバイト列とメタデータを保持し、取り込み時にハッシュを計算して、タスク/モデル/プロンプトの来歴を記録してください。変換、再エンコード、スクリーンショット、または後続のツールによってメタデータが削除される可能性があるため、実際に配信されたファイルを確認してください。C2PA が真実や同意を証明するとは主張しないでください。
- 適用される契約とセキュリティレビューで許可されていない限り、プロンプトや参照データに秘密情報、機密性の高い個人データ、または規制対象データを含めないでください。最小権限のアセット URL、短い有効期限、暗号化、監査ログ、および削除ワークフローを使用してください。
事前確認と引き継ぎチェックリスト
提出前に確認してください。
- 課金対象となる生成、モデル、比率、出力数、最大支出額について、ユーザーの承認を得ています。
- プロンプトはネイティブの Gen-4 で 1,000 UTF-16 単位以内に収まり、肯定的な視覚表現を使用しています。
- Turbo の参照画像は 1~3 件、標準の Gen-4 の参照画像は 3 件以下です。
- タグは重複がなく、小文字で 3~16 文字であり、正しく言及されています。
- 参照画像の転送方法、MIME、サイズ、寸法、権利、同意、保持期間を確認済みです。
- バージョンヘッダーを固定し、キーはサーバー側で管理し、タスク ID とジョブキーを保存する予定です。
- 同時実行数、タイムアウト、キャンセル時の動作、再試行の上限が明確です。
- 出力ホストの許可リスト、バイト数の上限、MIME/マジックナンバー検証、ハッシュ計算、アトミックな保存、マニフェストの準備ができています。
- このアカウントについて、利用ポリシー、帰属表示、エンドユーザー規約、トレーニング/保持、リージョン、サブプロセッサーを確認済みです。
引き継ぎ時には、モデルと正確なリクエストパラメーター、タスク ID、コストの見積もり/実際のクレジット数、成果物のパスと SHA-256 ハッシュ、QA 結果、却下した分岐、権利/同意の既知の根拠、未解決のプロバイダーポリシーに関する前提を提示してください。API キーや署名付き出力 URL は公開しないでください。
一次情報
参照日:2026-07-09
- API リファレンス:https://docs.dev.runwayml.com/api/
- モデルカタログ:https://docs.dev.runwayml.com/guides/models/
- はじめにと画像の例:https://docs.dev.runwayml.com/guides/using-the-api/
- バージョン管理:https://docs.dev.runwayml.com/api-details/versioning/
- SDK の動作:https://docs.dev.runwayml.com/api-details/sdks/
- 公式 Python SDK/OpenAPI 型定義:https://github.com/runwayml/sdk-python
- 入力と出力:https://docs.dev.runwayml.com/assets/inputs/ および https://docs.dev.runwayml.com/assets/outputs/
- 一時アップロード:https://docs.dev.runwayml.com/assets/uploads/
- 料金と上限:https://docs.dev.runwayml.com/guides/pricing/ および https://docs.dev.runwayml.com/usage/tiers/
- HTTP とタスクの失敗:https://docs.dev.runwayml.com/errors/errors/ および https://docs.dev.runwayml.com/errors/task-failures/
- モデレーションと帰属表示:https://docs.dev.runwayml.com/api-details/moderation/ および https://docs.dev.runwayml.com/usage/attribution/
- 変更履歴:https://docs.dev.runwayml.com/api-details/api_changelog/
- プロンプトと参照画像:https://help.runwayml.com/hc/en-us/articles/35694045317139-Gen-4-Image-Prompting-Guide および https://help.runwayml.com/hc/en-us/articles/40042718905875-Creating-with-Gen-4-Image-References
- 参照メディアの準備:https://docs.dev.runwayml.com/recipes/reference-media/
- 利用規約、プライバシー、ポリシー:https://runwayml.com/terms-of-use、https://runwayml.com/privacy-policy、および https://runwayml.com/safety/usage-policy
- API およびエンタープライズに関する説明:https://runwayml.com/api および https://help.runwayml.com/hc/en-us/articles/51248305153683-Enterprise-FAQ-Third-party-Models-in-Runway