PopNowDocs de la API
Referencia
Contenido principal

Errores

Cada código de negocio, qué significa y qué errores vale la pena reintentar.

Cuando una llamada falla, la respuesta te da un número (code) que dice qué tipo de problema es y una frase (msg) para que la lea una persona. Decide según el número; muestra o registra la frase.

El sobre

{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }
  • code es un código de negocio. 200 significa éxito; todo lo demás está abajo.
  • Si falla no hay campo data, salvo en algunos errores de la biblioteca de recursos, que añaden data.reason.
  • El estado HTTP también se establece (40201 llega con HTTP 402). Decide con uno de los dos, no con una mezcla; te recomendamos code, que es más concreto.
  • request_id también va en la cabecera X-Request-Id. Inclúyelo cuando escribas a soporte.

Si una respuesta ni siquiera es JSON —una página HTML o de texto de la red intermedia—, trátala según el estado HTTP y registra el principio del cuerpo.

Idioma de los mensajes de error

msg y el errorMessage de una tarea fallida vienen en inglés por defecto. Envía Accept-Language con en, ja, ko, de, fr, es, pt-BR, ru, it o pl, o configura el idioma de errores de la API en la consola, para recibirlos en ese idioma; el resto vuelve al inglés. Las respuestas de error llevan Content-Language. Los códigos nunca cambian con el idioma: otra razón para no analizar nunca msg.

Cuando un modelo da un motivo concreto de fallo (por ejemplo, una imagen de referencia ilegible), errorMessage lo transmite en inglés, sin detalles internos.

Códigos generales

codeHTTPSignificado¿Reintentar?
200200 / 202Éxito—
400400La solicitud o su input no son válidos; msg dice qué fallaNo: corrígelo
40003400Una subida no coincide con su ticket (tamaño, tipo o contenido)No: pide otro ticket y súbelo de nuevo
40004400Solicitud válida, pero el modelo no puede atender ahora esta combinación de ajustesNo: cambia el ajuste que nombra msg
401401Falta la clave, es desconocida o está revocada (detalles)No
403403No permitido (mira Permisos)No
404404No existe o pertenece a otra claveNo
409409Choca con el estado actual (cuándo reintentar)A veces
40901409Una cotización caducó o el precio cambióVuelve a cotizar
413413Cuerpo de la solicitud demasiado grandeNo: sube los archivos en vez de incrustarlos
429429Demasiadas solicitudesSí, tras Retry-After
500500Algo falló de nuestro ladoSí, con espera creciente
503503Un servicio de apoyo no está disponible un momentoSí, tras Retry-After

Saldo y topes

codeHTTPSignificadoQué hacer
40201402No hay créditos suficientes para la retención de esta tareaCompra créditos y vuelve a enviarla
40202402Se alcanzó un tope de gasto de la claveSúbelo en la consola de la API o espera a que se reinicie

Permisos

codeHTTPSignificadoQué hacer
40301403Esta clave no puede llamar a este modeloAñade el modelo a la lista permitida de la clave
40302403La solicitud vino de una dirección fuera de la lista de IP permitidasAñade la dirección o quita la restricción
40303403El servicio no se ofrece en esta regiónNo se puede sortear
40310403El correo de la cuenta aún no está verificadoAbre el enlace del correo de registro; la clave funciona en cuanto se verifica

Un 403 sin más con una clave válida significa que la cuenta no puede usar la API ahora mismo; contacta con soporte con el request_id.

Disponibilidad de modelos

codeHTTPSignificado¿Reintentar?
50301503El modelo no puede aceptar tareas ahoraSí, más tarde, o elige otro modelo
40004400El modelo funciona, pero no con estos ajustesNo: cambia el ajuste

Ninguno de los dos crea una tarea ni retiene créditos.

Códigos de fallo de tareas

Una tarea aceptada que luego falla tiene state: "failed" y uno de estos valores en errorCode. La lista es cerrada: trata cualquier otro valor como upstream_failed.

errorCodeSignificado¿Reintentar?
invalid_requestEl modelo no aceptó los parámetrosNo: corrígelos
unsupported_combinationEsta combinación de ajustes no se admite ahoraNo: cambia un ajuste
content_rejectedEl modelo rechazó este contenidoNo: cambia el prompt o la referencia
invalid_assetUn archivo de referencia no se pudo leer o ya no está disponibleSúbelo de nuevo y crea otra tarea
rate_limitedEl servicio de generación estaba saturadoSí, con espera creciente
upstream_unavailableLa generación no está disponible por un momentoSí, en breve
generation_failedLa generación se ejecutó pero no produjo nadaPuede ser: la misma entrada suele fallar igual
timeoutLa tarea se quedó sin tiempoPuede ser: prueba una salida más corta o con menos resolución
upstream_failedTodo lo demásSí, con espera creciente

Una tarea fallida libera su retención, así que no cuesta nada (unos pocos modelos cobran los rechazos por contenido y lo indican en su página). errorMessage explica el motivo con palabras; decide solo según errorCode.

Cuándo vale la pena reintentar un 409

Caso¿Reintentar?
Llegaron a la vez dos solicitudes con el mismo Idempotency-KeySí, con la misma clave, tras una pausa corta
El Idempotency-Key se usó para otra solicitud o con otra claveNo
Una subida o un resultado aún se está procesandoSí, al cabo de unos segundos
jobs/retry sobre una tarea que todavía no terminóSí, cuando sea final
jobs/retry sobre una tarea que salió bienNo: crea una tarea nueva
Caducó un ticket de subida o un archivo subidoNo: súbelo otra vez

Cómo escribir el reintento

Reintenta solo lo que puede salir bien a la segunda, espera Retry-After cuando venga y, si no, aumenta la espera de forma exponencial:

JavaScript
const RETRYABLE = new Set([429, 500, 503, 50301]);

async function call(path, init, attempts = 4) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(`https://api.popnow.ai/api/v1${path}`, init);
    if (!res.headers.get('content-type')?.includes('json')) {
      throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
    }
    const body = await res.json();
    if (body.code === 200) return body.data;
    if (!RETRYABLE.has(body.code)) throw new Error(`${body.code} ${body.msg} (${body.request_id})`);
    const wait = Number(res.headers.get('retry-after')) * 1000 || 2 ** i * 1000 + Math.random() * 500;
    await new Promise((r) => setTimeout(r, wait));
  }
  throw new Error('out of retries');
}
Python
import random, time, requests

RETRYABLE = {429, 500, 503, 50301}

def call(method, path, attempts=4, **kwargs):
    for i in range(attempts):
        res = requests.request(method, f"https://api.popnow.ai/api/v1{path}", timeout=60, **kwargs)
        try:
            body = res.json()
        except ValueError:
            raise RuntimeError(f"HTTP {res.status_code}: {res.text[:200]}")
        if body["code"] == 200:
            return body["data"]
        if body["code"] not in RETRYABLE:
            raise RuntimeError(f'{body["code"]} {body["msg"]} ({body.get("request_id")})')
        retry_after = res.headers.get("retry-after")
        time.sleep(float(retry_after) if retry_after else 2**i + random.random() / 2)
    raise RuntimeError("out of retries")

Al reintentar createTask, envía siempre el mismo Idempotency-Key, para que una solicitud que en realidad funcionó no se ejecute ni se cobre dos veces.

Depuración

  1. Apunta el request_id y el code.
  2. Busca la tarea en los logs de la consola de la API, por ID de tarea o request_id.
  3. ¿Sigues atascado? Contacta con soporte con el request_id, la hora (UTC) y el code. Nunca envíes tu clave de API.

En esta página