Démarrage rapide
Créez une clé API, envoyez votre première requête et téléchargez le résultat, en cinq minutes environ.
Cette page vous mène de zéro à une première image générée, avec une clé et une requête. Tout le reste — vidéo, audio, webhooks, envois de fichiers — c’est la même requête avec un autre model et un autre input.
Avant de commencer
- Un compte PopNow. Inscrivez-vous sur popnow.ai. Les crédits offerts à l’inscription fonctionnent aussi avec l’API ; quand il vous en faut davantage, achetez un pack de crédits. Il n’y a pas d’offre API séparée.
- Un terminal avec
curl. Les exemples utilisent bash. Sous Windows, passez par WSL ou Git Bash.
1. Créer une clé API
Ouvrez la console API et choisissez Create key. Copiez la clé tout de suite : la valeur complète n’est affichée qu’une fois. Placez-la ensuite dans une variable d’environnement pour qu’elle n’apparaisse jamais dans votre code :
export POPNOW_API_KEY="sk-popnow-..."Chaque clé commence par sk-popnow- et consomme les crédits de votre compte. Gardez-la côté serveur ; les restrictions, la rotation et la marche à suivre en cas de fuite sont expliquées dans Authentification.
2. Envoyer une tâche
Collez ceci dans le même terminal. La requête demande une image à un modèle rapide et coûte un ou deux crédits :
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"
}
}'Le rôle de chaque partie :
modelest l’identifiant du modèle : quel modèle tourne et pour quelle tâche. Tous les identifiants figurent dans Modèles.inputest validé par le schéma d’entrée propre à ce modèle. Les champs inconnus sont refusés : une faute de frappe provoque une erreur immédiate au lieu d’être ignorée en silence.Idempotency-Keysécurise les nouvelles tentatives : renvoyez la même clé après un délai dépassé et vous récupérez la tâche d’origine, sans second débit. Voir Idempotence.?wait=60garde la connexion ouverte jusqu’à 60 secondes, si bien qu’une image rapide revient dans cette même réponse.
3. Lire le résultat
Une tâche terminée ressemble à ceci :
{
"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_…"
}statevautsucceeded. Une tâche qui n’a pas pu aboutir indiquefailedavec unerrorCode; voir Erreurs.output.assets[0].urlest un lien signé vers le fichier. Il reste valable une vingtaine de minutes, ne demande aucune clé API, et vous en obtenez un nouveau en relisant la tâche.creditsindique ce que la tâche a réellement coûté, en crédits entiers (1 crédit = 0,01 $) ;costdonne le même montant sous forme de chaîne en dollars US.
Si le modèle a besoin de plus de 60 secondes — c’est le cas de tous les modèles vidéo —, la réponse est un 202 avec state: "queued" et un taskId. Interrogez la tâche jusqu’à ce qu’elle soit terminée :
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=job_..." \
-H "Authorization: Bearer $POPNOW_API_KEY"Vérifiez toutes les quelques secondes, jusqu’à ce que state vaille succeeded, failed ou expired. En production, passez plutôt une callBackUrl et laissez un webhook vous prévenir.
La même requête dans votre code
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"))L’enveloppe de réponse
Toutes les réponses JSON ont les mêmes quatre champs :
{ "code": 200, "msg": "success", "data": { }, "request_id": "req_…" }code est un code métier, pas un statut HTTP : 200 signifie succès, toutes les autres valeurs sont listées dans Erreurs. Basez votre logique sur code, jamais sur le texte de msg. Indiquez le request_id quand vous contactez le support.

