開始使用
主要內容快速入門
建立 API 金鑰、送出第一個請求、下載生成結果,大約五分鐘。
這一頁帶你從零開始,用一把金鑰、一個請求生成第一張圖。之後的一切——影片、音訊、Webhook、上傳素材——都是同一個請求,只是換了 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. 送出任務
把下面這段貼到同一個終端機。它請一個出圖很快的模型生成一張圖,只花一兩點:
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會依這個模型自己的輸入 schema 驗證。不認得的欄位會直接拒絕,欄位名稱打錯會立刻報錯,不會被默默忽略。Idempotency-Key讓重試變得安全:逾時後用同一個鍵再送一次,拿回的是原本那個任務,不會重複扣點。請見冪等。?wait=60讓連線最多保持 60 秒,出圖快的任務就會在這一個回應裡直接回傳結果。
3. 讀取結果
完成的任務長這樣:
{
"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,讓 Webhook 通知你。
用程式碼送出同一個請求
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);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。

