메인 콘텐츠로 건너뛰기
여러 엔드포인트를 연결한 엔드투엔드 예시입니다. 모든 명령에서 $KEY를 본인의 API token으로, $HOST를 실제 플랫폼 도메인으로 바꾸세요.
export KEY="sk-your-api-key"
export HOST="https://api.apimart.ai"

플로우 A: 기본 텍스트→이미지 (imagine → upscale)

# 1. imagine으로 이미지 4장 생성
curl -sS -X POST "$HOST/v1/midjourney/generations/imagine" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "prompt": "a futuristic city at sunset, photorealistic, cinematic lighting",
    "version": "8.1", "size": "16:9", "speed": "fast", "stylize": 250
  }'
# → {"code":200,"data":[{"task_id":"task_01KQVZAPBW...","status":"submitted"}]}

# 2. SUCCESS가 될 때까지 폴링 조회(약 30~60s)
curl -sS "$HOST/v1/midjourney/task_01KQVZAPBW..." -H "Authorization: Bearer $KEY"
# → SUCCESS, grid_image_url + 이미지 4장 image_urls + buttons(U1-U4 / V1-V4 / 🔄) 포함

# 3. upscale로 2번째 이미지 선택(로컬 합성, 밀리초 단위 SUCCESS)
curl -sS -X POST "$HOST/v1/midjourney/generations/upscale" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"task_id": "task_01KQVZAPBW...", "index": 2}'
# → 조회하여 단일 이미지 image_urls[0] 획득

플로우 B: 참조 이미지 → 강한 변형 → 업스케일

# 1. 참조 이미지 imagine
curl -sS -X POST "$HOST/v1/midjourney/generations/imagine" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "prompt": "turn this product into a luxury studio photo",
    "image_urls": ["https://your-cdn.example.com/product.png"],
    "iw": 1.5, "size": "1:1"
  }'

# 2. 결과에 강한 변형 적용
curl -sS -X POST "$HOST/v1/midjourney/generations/high-variation" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"task_id": "task_01XXX...", "index": 1, "speed": "fast"}'

# 3. 변형 중 한 장에 upscale
curl -sS -X POST "$HOST/v1/midjourney/generations/upscale" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"task_id": "task_02_variant...", "index": 3}'

플로우 C: 부분 리페인트 (inpaint + modal 2단계)

전제: 먼저 imagine + upscale로 단일 이미지 작업을 획득(플로우 A 참고).
# 1. inpaint 제출 → MODAL 진입
curl -sS -X POST "$HOST/v1/midjourney/generations/inpaint" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"task_id": "task_02_upscaled..."}'
# → {"data":[{"task_id":"task_03_inpaint...","status":"modal"}]}
#   status=modal 주의, 작업이 mask 보완을 기다림; 30분 타임아웃 시 자동 CANCEL + 환불

# 2. 프런트엔드에서 mask 그리기(흰색=리페인트 영역, 투명=유지), 자체 OSS에 업로드하여 mask_url 획득(공개 접근 가능해야 함)

# 3. modal 제출하여 완료
curl -sS -X POST "$HOST/v1/midjourney/generations/modal" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "task_id": "task_03_inpaint...",
    "prompt": "replace the selected area with a red leather sofa",
    "mask_url": "https://your-oss.example.com/mask-abc.png"
  }'
# → 동일 task_id, status가 submitted로 전환; 4. 60~90s 폴링 후 SUCCESS, 부분 리페인트 후보 4장 포함

플로우 D: 확장 (Zoom Out)

# mask 없이 바로 이미지 생성(Outpaint / CustomZoom 모두 MODAL에 진입하지 않음)
curl -sS -X POST "$HOST/v1/midjourney/generations/zoom" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"task_id": "task_02_upscaled...", "zoom_ratio": 1.5, "speed": "fast"}'

플로우 E: 이미지→비디오 (i2v)

# 720p 고화질 + batch=4(4배 과금)
curl -sS -X POST "$HOST/v1/midjourney/generations/video" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "prompt": "city traffic at night, neon reflections, slow camera dolly",
    "image_urls": ["https://your-cdn.example.com/city.jpg"],
    "video_type": "vid_1.1_i2v_720", "batch_size": 4
  }'
# 실제 차감 = midjourney@video-720p × 4

# 시작/종료 프레임 transition(end_url은 자동으로 start_end로 업그레이드됨)
curl -sS -X POST "$HOST/v1/midjourney/generations/video" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{
    "prompt": "transition smoothly from sunrise to sunset",
    "image_urls": ["https://your-cdn.example.com/sunrise.jpg"],
    "end_url": "https://your-cdn.example.com/sunset.jpg",
    "video_type": "vid_1.1_i2v_720"
  }'

범용 도구: Python 클라이언트 래퍼

import time
import httpx

API_KEY = "sk-..."
HOST = "https://api.apimart.ai"

class MjClient:
    def __init__(self):
        self.client = httpx.Client(
            base_url=HOST,
            headers={"Authorization": f"Bearer {API_KEY}"},
            timeout=30,
        )

    def imagine(self, prompt, **params):
        r = self.client.post("/v1/midjourney/generations/imagine",
                             json={"prompt": prompt, **params})
        return r.json()["data"][0]["task_id"]

    def upscale(self, task_id, index):
        r = self.client.post("/v1/midjourney/generations/upscale",
                             json={"task_id": task_id, "index": index})
        return r.json()["data"][0]["task_id"]

    def query(self, task_id):
        return self.client.get(f"/v1/midjourney/{task_id}").json()

    def wait(self, task_id, timeout=180):
        deadline = time.time() + timeout
        while time.time() < deadline:
            t = self.query(task_id)
            if t["status"] in ("SUCCESS", "FAILURE"):
                return t
            if t["status"] == "MODAL":
                raise RuntimeError(f"task {task_id} /modal 호출이 필요합니다")
            time.sleep(3)
        raise TimeoutError(task_id)


mj = MjClient()
imagine_id = mj.imagine("a cat", version="8.1", speed="fast", size="16:9")
mj.wait(imagine_id)
upscale_id = mj.upscale(imagine_id, 2)
print(mj.wait(upscale_id)["image_urls"][0])

범용 도구: TypeScript 래퍼

const API_KEY = "sk-...";
const HOST = "https://api.apimart.ai";

async function mj(path: string, body: any) {
  const r = await fetch(`${HOST}${path}`, {
    method: "POST",
    headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  return r.json();
}

async function query(id: string) {
  const r = await fetch(`${HOST}/v1/midjourney/${id}`, {
    headers: { "Authorization": `Bearer ${API_KEY}` },
  });
  return r.json();
}

async function waitTask(id: string, timeoutMs = 180_000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const t = await query(id);
    if (t.status === "SUCCESS" || t.status === "FAILURE") return t;
    if (t.status === "MODAL") throw new Error(`/modal 호출이 필요합니다: ${id}`);
    await new Promise((r) => setTimeout(r, 3000));
  }
  throw new Error(`타임아웃: ${id}`);
}

const r = await mj("/v1/midjourney/generations/imagine",
                   { prompt: "a cat", version: "8.1", speed: "fast" });
const result = await waitTask(r.data[0].task_id);
console.log(result.image_urls);

상태 머신

submit → NOT_START(0%) → SUBMITTED(5-30%) → IN_PROGRESS(~99%) → SUCCESS(100%)
                                                              ↘ FAILURE(100%) → 자동 환불
inpaint / CustomZoom → MODAL(15%) ──POST /modal {mask_url, prompt}──▶ SUBMITTED → ...
                          └ 30min 타임아웃 → CANCEL + 환불