Inicio rápido
Crea una clave de API, envía tu primera solicitud y descarga el resultado en unos cinco minutos.
Esta página te lleva de cero a tu primera imagen generada con una clave y una solicitud. Todo lo demás —video, audio, webhooks, subidas— es la misma solicitud con otro model y otro input.
Antes de empezar
- Una cuenta de PopNow. Regístrate en popnow.ai. Los créditos gratis del registro también sirven en la API; cuando necesites más, compra un paquete de créditos. No hay un plan aparte para la API.
- Una terminal con
curl. Los ejemplos usan bash. En Windows, usa WSL o Git Bash.
1. Crea una clave de API
Abre la consola de la API y elige Create key. Copia la clave en ese momento: el valor completo solo se muestra una vez. Luego guárdala en una variable de entorno para que nunca acabe en tu código:
export POPNOW_API_KEY="sk-popnow-..."Todas las claves empiezan por sk-popnow- y gastan los créditos de tu cuenta. Guárdala solo en tu servidor; las restricciones, la rotación y qué hacer si se filtra están en Autenticación.
2. Envía una tarea
Pega esto en la misma terminal. Le pide una imagen a un modelo rápido y cuesta uno o dos créditos:
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"
}
}'Qué hace cada parte:
modeles el ID del modelo: qué modelo se ejecuta y para qué tarea. Todos los IDs están en Modelos.inputse valida con el esquema de entrada de ese modelo. Los campos desconocidos se rechazan, así que una errata da error al instante en vez de ignorarse en silencio.Idempotency-Keyhace que reintentar sea seguro: si vuelves a enviar la misma clave tras un timeout, recibes la tarea original en lugar de un segundo cobro. Mira Idempotencia.?wait=60mantiene la conexión abierta hasta 60 segundos, así una imagen rápida vuelve en esta misma respuesta.
3. Lee el resultado
Una tarea terminada se ve así:
{
"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_…"
}stateessucceeded. Una tarea que no pudo terminar dicefailedy trae unerrorCode; mira Errores.output.assets[0].urles un enlace firmado al archivo. Funciona unos 20 minutos, no necesita clave de API y siempre puedes conseguir uno nuevo leyendo la tarea otra vez.creditses lo que costó la tarea, en créditos enteros (1 crédito = 0,01 USD);costtrae el mismo importe como cadena en dólares.
Si el modelo necesita más de 60 segundos —todos los de video lo necesitan—, la respuesta es un 202 con state: "queued" y un taskId. Consulta la tarea hasta que termine:
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=job_..." \
-H "Authorization: Bearer $POPNOW_API_KEY"Revisa cada pocos segundos hasta que state sea succeeded, failed o expired. En producción, mejor pasa un callBackUrl y deja que un webhook te avise.
La misma solicitud en código
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"))El sobre de respuesta
Todas las respuestas JSON tienen los mismos cuatro campos:
{ "code": 200, "msg": "success", "data": { }, "request_id": "req_…" }code es un código de negocio, no un estado HTTP: 200 significa éxito y el resto aparece en Errores. Decide según code, nunca según el texto de msg. Incluye el request_id cuando escribas a soporte.

