在 Web 與行動應用程式中建置生成式影像與視訊服務,往往面臨龐大的維運成本:自建 GPU 叢集需要克服冷啟動延遲、多使用者尖峰流量並行、VRAM 顯存瓶頸以及繁複的硬體調度架構。像 Getimg.ai 這類全託管雲端推論平台,透過標準化的 REST API 提供隨取即用的生成能力,涵蓋 FLUX 系列、Stable Diffusion XL(SDXL)以及透過 Elements 實現的專屬風格與角色一致性引擎,讓開發者無需自行組建與維護實體 GPU 叢集。
然而,將概念驗證原型推進到面對大量線上使用者的生產環境時,工程團隊必須嚴格遵循平台服務配額限制。Getimg.ai 實施獨立的每分鐘請求數(RPM)配額、滾動並行連線上限、同步圖像與非同步視訊不同的生成生命週期,以及嚴格的暫存檔案保存週期。
本文將深入解析 Getimg.ai REST API 規範,指導系統架構師與後端工程師建立具備速率監控、並行任務佇列、模型參數動態驗證與永久儲存歸檔的高可用生產服務體系。
Getimg.ai API 核心架構概述
Getimg.ai 目前採用統一的 REST API 體系,基準服務端點為 https://api.getimg.ai/v2。所有請求皆須在 HTTP 標頭中提供 Bearer 權杖形式的 API 金鑰:
Authorization: Bearer sk_your_api_key_here
Content-Type: application/json
同步圖像與非同步視訊生命週期
平台依據媒體生成的運算時延特性,將任務劃分為兩種不同的執行模型:
- 圖像生成(
POST /v2/images/generations):採同步阻塞模式。HTTP 連線在推論計算期間維持開啟,完成後直接返回HTTP 200,並於回應主體中附帶生成元數據與具時效性的防盜鏈下載 URL。 - 視訊生成(
POST /v2/videos/generations):採非同步輪詢模式。送出生成請求後立即返回HTTP 202 Accepted與任務識別碼(req-...)。工作節點必須透過GET /v2/videos/generations/{id}輪詢任務狀態,直至其轉為completed或failed。
[業務應用服務端]
│
├─── (同步圖像) ───> POST /v2/images/generations ───> HTTP 200 [生成完成 + 簽署 URL]
│
└─── (非同步視訊) ──> POST /v2/videos/generations ───> HTTP 202 [任務擱置 ID]
│
▼ (輪詢迴圈)
GET /v2/videos/generations/{id} ─> HTTP 200 [狀態: completed / failed]
24 小時暫存儲存政策
所有產出的媒體檔案僅在 Getimg.ai 邊緣下載節點保留 24 小時。每個回應物件皆包含 ISO 8601 格式的 deletes_at 時間戳記。正式生產系統必須在檔案逾期銷毀前,將其串流轉存至企業自有物件儲存空間(如 Amazon S3、Google Cloud Storage 或 Cloudflare R2)。
速率限制與並行控制機制
Getimg.ai 在工作區(Workspace)層級實施多層限流控制,包含三個獨立的配額維度:
| 限制維度 | 目標端點 | 時間窗口 / 範疇 | 計數與扣減規則 |
|---|---|---|---|
| 圖像 RPM | POST /v2/images/generations | 60 秒固定/滾動窗口 | 僅於送出圖像任務時扣減;限制每分鐘請求次數 |
| 視訊 RPM | POST /v2/videos/generations | 60 秒固定/滾動窗口 | 僅於送出視訊任務時扣減 |
| 並行請求數(Concurrency) | 圖像與視訊生成端點 | 瞬時在途(in-flight)請求 | 兩類端點共享總額;未完成的圖像連線與擱置中的視訊任務皆佔用並行槽位 |
查詢端點如 GET /v2/videos/generations/{id} 屬於唯讀操作,不會消耗 RPM 配額。然而關鍵細節在於:尚未產出完畢的視訊任務會持續佔用並行槽位,直到其抵達終態(completed 或 failed)為止。
速率限制回應標頭
每次生成呼叫皆會在回應標頭中返回配額遙測指標:
X-RateLimit-Limit:當前 60 秒窗口內允許的最大請求次數。X-RateLimit-Remaining:當前窗口內剩餘可用的請求次數。X-RateLimit-Reset:當前限流窗口重設的 Unix 時間戳記(秒)。
若系統觸發 RPM 或並行上限,API 將拒絕請求並返回 HTTP 429 Too Many Requests。在 RPM 超額時,回應還會附帶 Retry-After 標頭,明確指出需要等待的秒數:
{
"error": {
"message": "Too many concurrent requests. Please wait for current requests to finish.",
"type": "invalid_request_error",
"param": null,
"code": "rate_limit"
}
}
錯誤分類處理:可重試與終態故障
生產端排程客戶端必須能精準判斷 HTTP 狀態碼與錯誤代碼(error.code):
| 狀態碼 | 錯誤代碼 (error.code) | 性質 | 建議應對策略 |
|---|---|---|---|
| 429 | rate_limit | 可重試 | 嚴格等待 Retry-After 指定秒數,或使用具隨機抖動的指數退避重試 |
| 500 | server_error | 可重試 | 執行具熔斷保護的指數退避重試 |
| 400 | invalid_parameter、parameter_missing | 終態故障 | 參數錯誤;應在上游對照模型規範先行本地校驗,切勿盲目重送 |
| 401 | invalid_api_key | 終態故障 | 憑證無效或過期;應立即觸發告警並檢視組態密鑰 |
| 402 | quota_exceeded | 終態故障 | 帳戶餘額不足;暫停派發並為工作區加值 |
| 404 | not_found | 終態故障 | 模型 ID 或任務 ID 不存在;核實資源名稱 |
| 422 | content_policy_violation | 終態故障 | 觸發內容安全審查過濾;向上游業務端與使用者回報政策違規 |
動態模型探索與專屬模型節點
過去 Getimg v1 介面常針對不同模型提供個別 URL 路徑(例如 /v1/stable-diffusion-xl/text-to-image 或 /v1/flux-schnell/text-to-image)。升級至 v2 標準後,包含 FLUX 系列、SDXL 與熱門社群模型均統一收攏在 /v2/images/generations,由請求體內的 model 欄位動態指派。
上線前置參數驗證(Pre-flight Validation)
硬編碼圖片解析度或長寬比,容易在平台模型版本升級時引發線上異常。穩健的後端服務應定時查詢模型目錄端點:
GET /v2/models:取得當前專案可存取的模型清單、支援參數與即時單價。GET /v2/models/{model}:查詢指定模型代號的詳細規格限制。
典型的模型規格回傳資料如下:
{
"id": "seedream-5-lite",
"name": "Seedream 5 Lite",
"type": "image",
"supported_aspect_ratios": ["1:1", "16:9", "9:16", "2:3", "3:2"],
"supported_resolutions": ["1K", "2K", "4K"],
"supports_sound": false,
"supported_references": {
"reference_image": 14
},
"pricing": {
"currency": "USD",
"rates": [
{
"resolution": "2K",
"sound": "not_applicable",
"billable_unit": "image",
"unit_price": "0.1400"
}
]
}
}
透過在應用程式開機時載入模型規格並設定快取(如 1 小時 TTL),系統能在發送收費推論前於本機直接校驗使用者輸入,將潛在的 HTTP 400 失敗在上游即時攔截。
風格與角色客製化:Elements 與參考圖像機制
針對企業級產品常見的特定角色或品牌視覺風格需求,Getimg.ai 提供了現代化的輕量替代方案:
- Elements 語法(
@ElementName):Getimg.ai 以 Elements 取代了傳統自託管 LoRA/Dreambooth 權重的複雜流程。在後台建立角色或商品 Element 後,開發者僅需在提示詞(prompt)中以@ElementName標記引用,基礎模型便會自然融入該特定視覺概念,無需在 GPU 主機間來回切換自訂模型權重。 - 多模態參考圖陣列(
images):支援以圖引導生成的模型最多可接收 20 張參考圖。輸入可為公開 HTTPS 網址或 base64 data URL(data:image/png;base64,...)。解碼後的單圖上限為 10 MiB,整個 JSON 請求大小必須控制在 25 MiB 內。
生產級高可用架構:並行佇列與速率調節設計
在多租戶高並行場景下,若由前端 API 伺服器直接向第三方發出 HTTP 請求,極易因突發流量觸發大量 429 錯誤與重試風暴。建議架構上採用基於分散式任務佇列(如 Redis + BullMQ 或 Celery)的解耦架構。
[Web 應用程式伺服器群]
│
▼
[Redis 任務排程佇列 (BullMQ)]
│
┌───────┴────────────────────────┐
▼ ▼
[工作節點 Worker 1] [工作節點 Worker 2]
│ 本地信號量與權杖桶 │ 本地信號量與權杖桶
└───────┬────────────────────────┘
│
▼
[Getimg.ai REST API v2] (速率限制與並行保護隔離)
│
▼ (HTTP 200 返回 data[0].url)
[靜態資產持久化模組] ──────────> [企業私有 S3 / R2 儲存貯體]
核心架構元件
- 分散式並行信號量(Semaphore):監控所有工作節點當前執行的推論作業總數,確保在途請求嚴格低於工作區並行上限。
- 權杖桶限流器(Token Bucket):平滑化請求節奏,確保發送頻率不超出帳戶 RPM 閾值。
- 具隨機抖動的指數退避(Jittered Backoff):遇到臨時性 429 或 500 錯誤時,參考
Retry-After加上隨機擾動進行延遲,避免大量 Worker 同步重送造成的群聚衝擊。 - 即時資產永久歸檔工作流:在收到成功回傳後,立即以串流方式下載圖像並上傳至自建儲存空間,更新資料庫資產網址。
程式碼實作:企業級調度客戶端與佇列 Worker
以下為採用 TypeScript、官方 getimg-ai SDK 與 Axios 封裝的完整企業級調度實作範例。
1. 穩健的服務包裝類別
import GetimgAI from "getimg-ai";
import axios, { AxiosInstance } from "axios";
export interface ModelMetadata {
id: string;
name: string;
type: "image" | "video";
supported_aspect_ratios: string[];
supported_resolutions: string[];
supported_references: Record<string, number>;
pricing: {
currency: string;
rates: Array<{ resolution: string; unit_price: string; billable_unit: string }>;
};
}
export class ProductionGetimgService {
private client: GetimgAI;
private http: AxiosInstance;
private modelCache: Map<string, { data: ModelMetadata; expiresAt: number }> = new Map();
constructor(private apiKey: string, private baseUrl: string = "https://api.getimg.ai") {
this.client = new GetimgAI({ apiKey, maxRetries: 0 }); // 重試由應用程式佇列集中控管
this.http = axios.create({
baseURL: this.baseUrl,
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
timeout: 60000,
});
}
/**
* 取得並快取模型定義,供本機前置檢查使用
*/
async getModelMetadata(modelId: string): Promise<ModelMetadata> {
const cached = this.modelCache.get(modelId);
if (cached && cached.expiresAt > Date.now()) {
return cached.data;
}
try {
const response = await this.http.get<ModelMetadata>(`/v2/models/${modelId}`);
this.modelCache.set(modelId, {
data: response.data,
expiresAt: Date.now() + 1000 * 60 * 60, // 快取 1 小時
});
return response.data;
} catch (error: any) {
if (error.response?.status === 404) {
throw new Error(`Getimg.ai 上查無模型 '${modelId}'`);
}
throw error;
}
}
/**
* 驗證生成參數是否與指定模型相容
*/
async validateGenerationPayload(
modelId: string,
aspectRatio?: string,
resolution?: string
): Promise<void> {
const meta = await this.getModelMetadata(modelId);
if (aspectRatio && !meta.supported_aspect_ratios.includes(aspectRatio)) {
throw new Error(
`模型 ${modelId} 不支援長寬比 '${aspectRatio}'。可用清單: ${meta.supported_aspect_ratios.join(", ")}`
);
}
if (resolution && !meta.supported_resolutions.includes(resolution)) {
throw new Error(
`模型 ${modelId} 不支援解析度 '${resolution}'。可用清單: ${meta.supported_resolutions.join(", ")}`
);
}
}
/**
* 執行同步圖像生成,並具備退避重試邏輯
*/
async generateImageWithRetry(params: {
model: string;
prompt: string;
aspectRatio?: string;
resolution?: string;
outputFormat?: "jpeg" | "png" | "webp";
images?: Array<{ url: string }>;
}, maxAttempts: number = 4): Promise<{ permanentUrl: string; usage: any }> {
await this.validateGenerationPayload(params.model, params.aspectRatio, params.resolution);
let attempt = 0;
while (attempt < maxAttempts) {
attempt++;
try {
const result = await this.client.images.generate({
model: params.model,
prompt: params.prompt,
aspect_ratio: params.aspectRatio,
resolution: params.resolution,
output_format: params.outputFormat ?? "jpeg",
images: params.images,
});
const generatedItem = result.data[0];
const permanentUrl = await this.persistToPermanentStorage(
generatedItem.url,
result.id,
params.outputFormat ?? "jpeg"
);
return { permanentUrl, usage: result.usage };
} catch (err: any) {
const status = err.status || err.response?.status;
const code = err.error?.code || err.response?.data?.error?.code;
// 終態錯誤不進行重試
if (status === 400 || status === 401 || status === 402 || status === 422) {
throw new Error(`Getimg.ai 終態錯誤 [${status} - ${code}]: ${err.message}`);
}
if (attempt >= maxAttempts) {
throw new Error(`超過最大重試次數。最後錯誤訊息: ${err.message}`);
}
// 計算指數退避並加入隨機抖動
let waitSeconds = Math.pow(2, attempt);
const retryAfterHeader = err.headers?.["retry-after"] || err.response?.headers?.["retry-after"];
if (retryAfterHeader) {
waitSeconds = parseInt(retryAfterHeader, 10);
}
const jitter = Math.random() * 1.5;
const totalDelayMs = (waitSeconds + jitter) * 1000;
console.warn(`[Getimg] 遇到可重試錯誤 (${status}),將於 ${totalDelayMs.toFixed(0)}ms 後重試...`);
await new Promise((resolve) => setTimeout(resolve, totalDelayMs));
}
}
throw new Error("圖像生成程序異常中斷。");
}
/**
* 下載暫存檔案並串流上傳至企業自有永久儲存空間
*/
private async persistToPermanentStorage(
temporaryUrl: string,
generationId: string,
format: string
): Promise<string> {
const downloadStream = await axios.get(temporaryUrl, { responseType: "arraybuffer" });
const buffer = Buffer.from(downloadStream.data);
// 請替換為企業實際的 S3 或 Cloudflare R2 上傳呼叫
const simulatedPermanentUrl = `https://cdn.yourapp.com/generations/${generationId}.${format}`;
console.log(`成功轉存 ${buffer.length} 位元組至永久位址: ${simulatedPermanentUrl}`);
return simulatedPermanentUrl;
}
}
2. BullMQ 佇列 Worker 設定
透過 BullMQ 的並行與速率限制配置,保證分散式環境下的穩定推論:
import { Queue, Worker, Job } from "bullmq";
import Redis from "ioredis";
import { ProductionGetimgService } from "./ProductionGetimgService";
const connection = new Redis(process.env.REDIS_URL || "redis://127.0.0.1:6379");
const getimgService = new ProductionGetimgService(process.env.GETIMG_API_KEY!);
export interface ImageJobPayload {
userId: string;
model: string;
prompt: string;
aspectRatio: string;
resolution: string;
}
// 建立任務佇列
export const imageQueue = new Queue<ImageJobPayload>("getimg-image-generation", {
connection,
defaultJobOptions: {
attempts: 3,
backoff: {
type: "exponential",
delay: 3000,
},
removeOnComplete: 1000,
removeOnFail: 5000,
},
});
// 啟動受控速率的 Worker
export const imageWorker = new Worker<ImageJobPayload>(
"getimg-image-generation",
async (job: Job<ImageJobPayload>) => {
console.log(`處理佇列作業 ${job.id},使用者: ${job.data.userId}`);
const result = await getimgService.generateImageWithRetry({
model: job.data.model,
prompt: job.data.prompt,
aspectRatio: job.data.aspectRatio,
resolution: job.data.resolution,
});
return result;
},
{
connection,
// 依專案購買的並行額度配置
concurrency: 5,
limiter: {
max: 60, // 最大請求數
duration: 60000, // 週期 60,000ms(圖像 RPM 限制)
},
}
);
維運最佳實務
- 解耦視訊輪詢與並行額度:透過
POST /v2/videos/generations建立視訊時,在途任務會持續佔用並行連線槽位。輪詢間隔建議設為 3 至 5 秒,並配合指數遞增,避免連線佔滿平台並行配額。 - 用量監控與費用對帳:每次生成皆會回傳
usage物件(包含total_cost、unit_price與quantity)。後端應將其記錄至稽核日誌,並定期調用GET /v2/billing/costs進行帳務對帳,預防因餘額耗盡而遭遇 402 中斷。 - 前置內容安全風控機制:HTTP 422
content_policy_violation代表提示詞或上傳圖觸發了平台安全審查。在系統前端整合輕量文字審核模型(如 Llama Guard),能有效在上游過濾不合規請求,節省雲端 API 開銷與重試成本。 - 模型層級降級容災:若某個特定基礎模型出現延遲激增或平台維護,架構應支援無縫降級至替代模型(例如降級至
seedream-5-lite或輕量版 FLUX),在請求格式完全相容的基礎上維持系統服務連續性。