PopNowAPI 문서
시작하기
본문

빠른 시작

API 키를 만들고, 첫 요청을 보내고, 결과를 내려받기까지 약 5분이면 됩니다.

이 페이지는 키 하나와 요청 하나로 첫 이미지를 만드는 데까지 안내합니다. 그다음의 영상, 오디오, 웹훅, 파일 업로드도 model과 input만 바꾼 같은 요청입니다.

시작하기 전에

  • PopNow 계정. popnow.ai에서 가입하세요. 가입할 때 받는 무료 크레딧은 API에서도 그대로 쓸 수 있고, 더 필요하면 크레딧 팩을 구매하면 됩니다. API 전용 요금제는 따로 없습니다.
  • curl을 쓸 수 있는 터미널. 예제는 bash 기준입니다. Windows에서는 WSL이나 Git Bash를 사용하세요.

1. API 키 만들기

API 콘솔을 열고 Create key를 누르세요. 키는 바로 복사해 두세요. 전체 값은 이때 한 번만 보여 줍니다. 코드에 들어가지 않도록 환경 변수에 넣어 둡니다.

export POPNOW_API_KEY="sk-popnow-..."

모든 키는 sk-popnow-로 시작하고 계정의 크레딧을 사용합니다. 키는 서버에만 두세요. 사용 범위 제한, 교체, 유출 시 대처법은 인증에 정리돼 있습니다.

2. 작업 보내기

같은 터미널에 아래 내용을 붙여 넣으세요. 빠른 이미지 모델에 한 장을 요청하며, 비용은 1~2크레딧입니다.

POST /api/v1/jobs/createTask?wait=60
export POPNOW_API_KEY="sk-popnow-..."   # your key from the API console

curl -X POST "https://api.popnow.ai/api/v1/jobs/createTask?wait=60" \
  -H "Authorization: Bearer $POPNOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "model": "alibaba/z-image-turbo/text-to-image",
  "input": {
    "prompt": "A small red fox curled up in a teacup, soft morning light, studio photo"
  }
}'

각 부분의 역할:

  • **model**은 모델 ID입니다. 어떤 모델이 어떤 일을 할지 정합니다. 모든 모델의 ID는 모델에 있습니다.
  • **input**은 해당 모델의 입력 스키마로 검증됩니다. 모르는 필드는 거부되므로 필드 이름의 오타가 조용히 무시되지 않고 바로 오류로 드러납니다.
  • **Idempotency-Key**를 보내면 재시도가 안전해집니다. 타임아웃 뒤 같은 키로 다시 보내면 두 번 과금되는 대신 원래 작업이 돌아옵니다. 멱등성을 참고하세요.
  • **?wait=60**은 연결을 최대 60초 동안 유지합니다. 금방 끝나는 이미지라면 이 응답에 결과가 바로 담겨 옵니다.

3. 결과 읽기

완료된 작업은 이렇게 생겼습니다.

200 · recordInfo / createTask?wait=60
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "job_01JH8Z4Q5W2N7R3K9M6P1C0VXD",
    "model": "alibaba/z-image-turbo/text-to-image",
    "state": "succeeded",
    "output": {
      "assets": [
        {
          "url": "https://…signed-link…",
          "expiresAt": "2026-10-16T09:20:00Z",
          "mime": "image/png",
          "width": 1024,
          "height": 1024
        }
      ]
    },
    "credits": 2,
    "cost": "0.02",
    "settled": true
  },
  "request_id": "req_…"
}
  • state가 succeeded입니다. 끝내지 못한 작업은 failed가 되고 errorCode가 붙습니다. 오류를 참고하세요.
  • output.assets[0].url은 파일의 서명된 링크입니다. 약 20분 동안 유효하고 내려받을 때 API 키가 필요 없습니다. 만료되면 작업을 다시 읽어 새 링크를 받으세요.
  • credits는 이 작업에 실제로 차감된 정수 크레딧(1크레딧 = $0.01)이고, cost는 같은 금액을 미국 달러 문자열로 나타낸 값입니다.

모델이 60초보다 오래 걸리면(영상 모델은 모두 그렇습니다) 응답은 202이고 state: "queued"와 taskId가 담겨 옵니다. 작업이 끝날 때까지 조회하세요.

curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=job_..." \
  -H "Authorization: Bearer $POPNOW_API_KEY"

state가 succeeded, failed, expired 중 하나가 될 때까지 몇 초마다 확인하세요. 실제 서비스에서는 callBackUrl을 넘기고 웹훅으로 알림을 받는 것이 좋습니다.

같은 요청을 코드로

JavaScript (Node.js 18+)
const API = 'https://api.popnow.ai/api/v1';
const headers = {
  Authorization: `Bearer ${process.env.POPNOW_API_KEY}`,
  'Content-Type': 'application/json',
};

const created = await fetch(`${API}/jobs/createTask`, {
  method: 'POST',
  headers: { ...headers, 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({
    model: 'alibaba/z-image-turbo/text-to-image',
    input: { prompt: 'A small red fox curled up in a teacup, soft morning light' },
  }),
}).then((r) => r.json());
if (created.code !== 200) throw new Error(`${created.code} ${created.msg}`);

// Poll until the task reaches a final state.
let task;
do {
  await new Promise((r) => setTimeout(r, 3000));
  const res = await fetch(`${API}/jobs/recordInfo?taskId=${created.data.taskId}`, { headers });
  task = (await res.json()).data;
} while (task.state === 'queued' || task.state === 'running');

console.log(task.state, task.output?.assets?.[0]?.url);
Python (requests)
import os, time, uuid, requests

API = "https://api.popnow.ai/api/v1"
headers = {"Authorization": f"Bearer {os.environ['POPNOW_API_KEY']}"}

created = requests.post(
    f"{API}/jobs/createTask",
    headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
    json={
        "model": "alibaba/z-image-turbo/text-to-image",
        "input": {"prompt": "A small red fox curled up in a teacup, soft morning light"},
    },
    timeout=30,
).json()
if created["code"] != 200:
    raise RuntimeError(f'{created["code"]} {created["msg"]}')

# Poll until the task reaches a final state.
while True:
    time.sleep(3)
    task = requests.get(
        f"{API}/jobs/recordInfo",
        headers=headers,
        params={"taskId": created["data"]["taskId"]},
        timeout=30,
    ).json()["data"]
    if task["state"] not in ("queued", "running"):
        break

print(task["state"], task.get("output", {}).get("assets", [{}])[0].get("url"))

응답 형식

모든 JSON 응답에는 같은 네 필드가 있습니다.

{ "code": 200, "msg": "success", "data": { }, "request_id": "req_…" }

code는 HTTP 상태가 아니라 업무 코드입니다. 200이 성공이고 나머지는 오류에 정리돼 있습니다. 분기는 code로 하고 msg 문구에 의존하지 마세요. 고객 지원에 문의할 때는 request_id를 함께 보내 주세요.

다음 단계

이 페이지에서