Web アプリケーションに画像生成や部分再描画(インペインティング)機能を組み込もうとする際、個人開発者や少人数のエンジニアチームはインフラ構成の選択において深刻なジレンマに直面します。オープンソースの拡散モデル(Stable Diffusion など)を AWS EC2 G5 や RunPod などの自前 GPU インスタンスでセルフホストする場合、CUDA ドライバの保守や vLLM/Diffusers のバージョン管理、コールドスタート対策に加え、月額 150〜400 ドル以上のアイドル時固定コストが発生します。一方で、大手 SaaS(Midjourney や Leonardo など)は月額課金のサブスクリプションを強制し、未使用クレジットの失効や柔軟な API 提供の欠如など、トラフィックの変動が大きいサービスには適合しにくい制約があります。
Dezgo は、この課題を解決する透明性の高い従量課金制(Pay-as-you-go)REST API を提供しています。最低 10 ドルからの前払いチャージ(有効期限なし)で利用でき、標準的な 512x512 解像度の Stable Diffusion 生成であれば 1 枚あたり約 0.0019 ドルという極めて低廉な単価で、解像度やサンプリングステップ数に応じた正確なマイクロトランザクション課金が行われます。
本ガイドでは、フルスタックエンジニアを対象に、Dezgo 公式 REST API の接入仕様、X-Dezgo-Key による認証、/text2image および /inpainting_sdxl エンドポイントの呼び出し方法、HTTP レスポンスヘッダーを用いたリアルタイム残高管理、そして Web アプリにおける非同期タスクキューの実装パターンを詳しく解説します。
アーキテクチャ比較:自前 GPU、商用 SaaS、Dezgo API
コード実装に入る前に、主要な 3 つのアプローチの特性を比較します。
| 評価項目 | 自前 GPU クラスタ (EC2 / RunPod) | 大手商用 SaaS (Midjourney 等) | Dezgo REST API |
|---|---|---|---|
| 初期・最低費用 | 高(月額 150〜500 ドル以上の固定費) | 中〜高(月額 10〜60 ドル/ユーザーの定額制) | 極めて低(10 ドルの一括前払い、月額基本料ゼロ) |
| 生成単価 | 稼働率に依存;トラフィック低迷時は割高 | パック料金;未消化分は月末に失効 | 1枚約 $0.0019 (512px SD);ステップ数比例 |
| 運用・保守負荷 | 極めて高(GPU ドライバ、自動スケール、監視) | 極めて低(完全ブラックボックス) | 極めて低(標準 HTTP 呼び出し、GPU 運用ゼロ) |
| モデルの柔軟性 | 最大(任意のチェックポイントや自作 LoRA) | 低(ベンダー独自モデルに固定) | 高(SD 1.5、SD 2.1、SDXL、Lightning、LoRA) |
| レスポンス形式 | 独自設計が必要 | Discord ボットや非同期ポーリング | 同期バイナリストリーム (image/png) 直返 |
Dezgo の大きな特徴は、画像生成リクエストに対して加工済み PNG のバイナリデータを同期的に直接返し、同時に消費金額と残高を HTTP レスポンスヘッダーで即座に通知する点にあります。
認証とセキュリティアーキテクチャ
Dezgo API のすべてのリクエストは、HTTP リクエストヘッダーに X-Dezgo-Key を付与することで認証されます。API キーは dezgo.com/account の管理コンソールから発行可能です。
鉄則:フロントエンドのブラウザから直接 Dezgo を呼び出さない
Dezgo の API キーは開発者の実資金アカウントに直結しており、エンドユーザー向けのパーミッション制限機能は備わっていません。そのため、ブラウザの通信やクライアント側 JavaScript に X-Dezgo-Key を露出させると、悪意のあるユーザーにキーを盗取され、短時間でプリペイド残高を枯渇させられる危険があります。
安全な Web アプリケーションでは、必ず信頼できるバックエンドサーバーを経由させる構成を採ります。
[ブラウザ / クライアント]
│
│ 1. POST /api/generate (セッション認証 / トークン消費)
▼
[自社バックエンド API]
│
│ 2. ユーザー権限・プロンプトの検証
│ 3. 秘匿された "X-Dezgo-Key" ヘッダーを付与
│ 4. POST https://api.dezgo.com/text2image
▼
[Dezgo REST サーバー]
│
│ 5. PNG バイナリストリーム + コスト情報ヘッダーを返却
▼
[自社バックエンド API]
│
│ 6. ヘッダーから消費額と残高を解析して DB に記録
│ 7. 画像を S3 / R2 に保存するかクライアントへストリーム中継
▼
[ブラウザ / クライアント]
ステップ 1:Text-to-Image エンドポイントの実装 (/text2image)
Dezgo の文生図エンドポイントは multipart/form-data または application/json(Base64 エンコード画像)を受け付け、生成結果のバイナリ画像を返します。
主なリクエストパラメータ
prompt(文字列, 必須):生成したい画像を描写するプロンプト。negative_prompt(文字列, 任意):除外したい要素(奇形、低品質、ぼやけなど)。model(文字列, 任意):使用するモデル識別子。GET https://api.dezgo.com/infoから利用可能なモデル一覧(stablediffusion_1_5、stablediffusion_2_1_768pxなど)を取得可能。guidance(浮動小数点数, 任意):プロンプトへの忠実度(CFG Scale、通常 6.0〜9.0、規定値 7.5)。steps(整数, 任意):サンプリングステップ数(通常 20〜50)。ステップ数が増えるほど品質が向上しますが、計算コストと所要時間は比例します。sampler(文字列, 任意):サンプラーアルゴリズム(euler_a、ddim、dpmpp_2mなど)。width/height(整数, 任意):出力画像のピクセルサイズ(デフォルト 512x512)。
Node.js / TypeScript によるバックエンド実装例
以下は Next.js API Route や Express でそのまま利用可能な実装です。
// server/services/dezgoService.ts
import { Buffer } from "node:buffer";
interface TextToImageParams {
prompt: string;
negativePrompt?: string;
model?: string;
guidance?: number;
steps?: number;
width?: number;
height?: number;
}
interface GenerationResult {
imageBuffer: Buffer;
contentType: string;
jobCostUsd: number;
remainingBalanceUsd: number;
}
export async function generateTextToImage(
params: TextToImageParams
): Promise<GenerationResult> {
const apiKey = process.env.DEZGO_API_KEY;
if (!apiKey) {
throw new Error("環境変数 DEZGO_API_KEY が設定されていません。");
}
const formData = new FormData();
formData.append("prompt", params.prompt);
if (params.negativePrompt) {
formData.append("negative_prompt", params.negativePrompt);
}
if (params.model) {
formData.append("model", params.model);
}
if (params.guidance) {
formData.append("guidance", params.guidance.toString());
}
if (params.steps) {
formData.append("steps", params.steps.toString());
}
if (params.width) {
formData.append("width", params.width.toString());
}
if (params.height) {
formData.append("height", params.height.toString());
}
const response = await fetch("https://api.dezgo.com/text2image", {
method: "POST",
headers: {
"X-Dezgo-Key": apiKey,
},
body: formData,
});
if (!response.ok) {
const errorText = await response.text();
handleDezgoHttpError(response.status, errorText);
}
// レスポンスヘッダーから消費額とアカウント残高を取得
const jobCostUsd = parseFloat(
response.headers.get("x-dezgo-job-amount-usd") || "0"
);
const remainingBalanceUsd = parseFloat(
response.headers.get("x-dezgo-balance-total-usd") || "0"
);
const arrayBuffer = await response.arrayBuffer();
const imageBuffer = Buffer.from(arrayBuffer);
const contentType = response.headers.get("content-type") || "image/png";
return {
imageBuffer,
contentType,
jobCostUsd,
remainingBalanceUsd,
};
}
function handleDezgoHttpError(status: number, message: string): never {
switch (status) {
case 400:
throw new Error(`Dezgo パラメータエラー (400): ${message}`);
case 401:
throw new Error("Dezgo 認証エラー (401): X-Dezgo-Key を確認してください。");
case 402:
throw new Error("Dezgo 残高不足 (402): プリペイド残高が枯渇しました。");
case 429:
throw new Error("Dezgo レートリミット (429): リクエスト頻度が高すぎます。");
default:
throw new Error(`Dezgo サーバーエラー (${status}): ${message}`);
}
}
ステップ 2:SDXL Inpainting エンドポイントの実装 (/inpainting_sdxl)
インペインティング(部分再描画)は、画像の既存の背景や構図を維持しながら、マスクで指定した領域のみをプロンプトに従って描き直す機能です。Dezgo は標準 SD 向けの /inpainting と、高品質・高解像度な /inpainting_sdxl を提供しています。
マスク画像の仕様
インペインティングには 2 つの画像データが必要です。
init_image:編集元となる元画像(PNG または JPEG)。mask_image:元画像と完全に同一ピクセル解像度の白黒マスク画像。白色(#FFFFFF)の部分が再描画対象、黒色(#000000)の部分が保護されて維持される領域です。
Python によるバックエンド実装例 (FastAPI / Requests)
# app/services/dezgo_inpainting.py
import os
import requests
from typing import Dict, Any, Tuple
DEZGO_API_KEY = os.environ.get("DEZGO_API_KEY")
INPAINTING_SDXL_URL = "https://api.dezgo.com/inpainting_sdxl"
def execute_sdxl_inpainting(
prompt: str,
init_image_bytes: bytes,
mask_image_bytes: bytes,
negative_prompt: str = "",
guidance: float = 7.5,
steps: int = 30,
) -> Tuple[bytes, Dict[str, Any]]:
"""
Dezgo SDXL インペインティングエンドポイントへ multipart 形式でリクエストを送信
"""
if not DEZGO_API_KEY:
raise ValueError("環境変数 DEZGO_API_KEY が設定されていません。")
headers = {
"X-Dezgo-Key": DEZGO_API_KEY
}
files = {
"init_image": ("init.png", init_image_bytes, "image/png"),
"mask_image": ("mask.png", mask_image_bytes, "image/png"),
}
data = {
"prompt": prompt,
"negative_prompt": negative_prompt,
"guidance": str(guidance),
"steps": str(steps),
}
response = requests.post(
INPAINTING_SDXL_URL,
headers=headers,
files=files,
data=data,
timeout=60
)
if response.status_code == 402:
raise RuntimeError("Dezgo プリペイド残高が不足しています。チャージしてください。")
elif response.status_code != 200:
raise RuntimeError(f"Dezgo インペインティング失敗 [{response.status_code}]: {response.text}")
metadata = {
"job_cost_usd": float(response.headers.get("x-dezgo-job-amount-usd", "0")),
"remaining_balance_usd": float(response.headers.get("x-dezgo-balance-total-usd", "0")),
"content_type": response.headers.get("Content-Type", "image/png"),
}
return response.content, metadata
ステップ 3:Web アプリにおける非同期タスクキューとタイムアウト対策
Dezgo の API は同期的(リクエスト送信後、画像生成が完了するまで HTTP 接続がオープンなまま保持される)仕様です。処理時間は通常 2〜8 秒程度ですが、Serverless 関数の制限時間(Vercel の無料枠ではデフォルト 10 秒)やエッジゲートウェイのタイムアウトに抵触する恐れがあります。
推奨構成:Redis タスクキューと SSE による非同期化
本番運用では、Web リクエストハンドラで直接生成を待つのではなく、Redis(BullMQ や Celery)を利用したバックグラウンドワーカーパターンを導入します。
[クライアント UI] ── 1. POST /api/jobs (プロンプト・マスク送信) ──► [Web API サーバー]
│
▼
[クライアント UI] ◄─ 3. 202 Accepted { jobId } 返却 ── 2. ジョブをキューへ追加
│ │
│ ▼
│ 4. SSE / WebSocket または定期ポーリングで待機 [Redis / BullMQ]
│ │
│ ▼ 5. タスク取得
│ [バックグラウンドワーカー]
│ │
│ ├── 6. Dezgo API 呼び出し
│ ├── 7. S3 / R2 へ保存
│ └── 8. DB に完了ステータス更新
◄── 9. 生成完了イベントと CDN 画像 URL を通知 ───────────────────┘
BullMQ ワーカーコードの実装例
// workers/renderWorker.ts
import { Worker, Job } from "bullmq";
import { generateTextToImage } from "../services/dezgoService";
import { uploadToS3 } from "../services/storageService";
import { db } from "../services/db";
export const renderWorker = new Worker("image-generation-queue", async (job: Job) => {
const { prompt, userId, jobId } = job.data;
// 1. Dezgo 呼び出し
const { imageBuffer, contentType, jobCostUsd, remainingBalanceUsd } =
await generateTextToImage({ prompt, steps: 30 });
// 2. オブジェクトストレージへの永続化
const cdnUrl = await uploadToS3({
key: `renders/${userId}/${jobId}.png`,
body: imageBuffer,
contentType,
});
// 3. データベースの更新とコストログの保存
await db.jobs.update({
where: { id: jobId },
data: {
status: "COMPLETED",
outputUrl: cdnUrl,
costUsd: jobCostUsd,
},
});
// 4. 残高アラート:5 ドルを下回った場合に通知
if (remainingBalanceUsd < 5.0) {
console.warn(`[残高警告] Dezgo API 残高低下: 残り $${remainingBalanceUsd.toFixed(2)} です。`);
}
return { cdnUrl, jobCostUsd };
});
プリペイド残高の管理と運用のベストプラクティス
Dezgo には自動課金(Auto-recharge)の仕組みがないため、残高が 0 になると即座に生成が停止します。レスポンスヘッダーの情報を監視し、運用アラートを整えることが安定稼働の要となります。
レスポンスヘッダーに含まれる財務情報
x-dezgo-job-amount-usd:当該リクエストで課金された金額(USD)。x-dezgo-balance-total-usd:リクエスト処理後のアカウント残り利用可能額(USD)。x-dezgo-balance-credits-usd:累計チャージ入金額。x-dezgo-balance-debits-usd:累計利用支出額。x-dezgo-balance-index:トランザクション台帳の最新インデックス番号。
生成を行わずに残高を確認するエンドポイント
画像生成を実行せずに定期ヘルスチェックを行いたい場合、直近のトランザクション確認エンドポイントが利用できます。
curl -s -H "X-Dezgo-Key: YOUR_API_KEY" https://api.dezgo.com/account/tx/last
このエンドポイントは最新の取引記録と現在の balance を JSON で返却するため、定期実行スクリプトや Datadog / Prometheus 等の監視エージェントと連携可能です。
エラーハンドリング指針
- HTTP 402 (Payment Required):プリペイド残高枯渇。システム管理者に即座に PagerDuty や Slack で緊急通知を送り、ユーザー側には「現在混雑のため順番待ちです」といった適切なフォールバックメッセージを返します。
- HTTP 429 (Too Many Requests):同時リクエスト上限超過。ワーカー側でジッターを付与した指数バックオフ(1秒、2秒、4秒後)を実装し、再試行します。
- HTTP 400 (Bad Request):マスク画像と元画像の解像度不一致が主な原因です。Dezgo への送信前にバックエンド側で画像サイズを厳密にバリデーションしてください。
まとめ
Dezgo は、GPU クラスタの運用コストを削減し、高額な定額制 SaaS から脱却したい開発者にとって理想的な選択肢です。堅牢なバックエンドプロキシの構築、Redis による非同期ワーカーの導入、レスポンスヘッダーを活用した残高監視を組み合わせることで、低コストかつスケーラブルな画像生成機能を自社サービスに短期間で実装できます。