NezhaGateNezhaGate

Cómo enviar trabajos de imagen y vídeo, consultarlos o recibirlos por webhook, descargar el resultado, entender cada estado y campo, y cómo funcionan los reembolsos.

Qué endpoints son asíncronos

La generación de imágenes y vídeo funciona con trabajos asíncronos: la llamada devuelve al momento un id de trabajo y la generación continúa en segundo plano. El chat es síncrono y nunca usa trabajos.

MétodoRutaQué hace
POST/v1/images/generationsTexto a imagen. Devuelve al momento un id de trabajo (HTTP 202).
POST/v1/images/editsImagen a imagen. Pasar image a la llamada anterior hace lo mismo.
GET/v1/images/jobs/{id}Consulta un trabajo de imagen: estado y resultado.
POST/v1/videos/generationsGenera un vídeo. Devuelve al momento un id de trabajo (HTTP 202).
GET/v1/videos/jobs/{id}Consulta un trabajo de vídeo: estado y resultado.

Cómo funciona

  1. Envío: llama a un endpoint de generación y recibirás al momento HTTP 202 con el id del trabajo (tiene la forma img_...; los trabajos de vídeo usan el mismo prefijo).
  2. Espera: consulta el endpoint del trabajo con ese id, o añade un callback_url al enviar para que el resultado te llegue automáticamente.
  3. Fin: status pasa a succeeded y el enlace del resultado está en data[0].url; si pasa a failed, el motivo está en error y los créditos retenidos para el trabajo vuelven a tu saldo.
  4. Descarga: los enlaces de los resultados se guardan 60 días; copia en tu propio almacenamiento todo lo que necesites conservar.
curl · Enviar un trabajo
curl https://nezhagate.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "seedance-2.0", "prompt": "a paper boat drifting down a rainy street", "duration": 5, "size": "16:9"}'

# HTTP 202
{"id": "img_9c1e...7a", "object": "video.generation.job", "status": "queued", "created": 1791199400, "model": "seedance-2.0"}
curl · Consultar el trabajo
curl https://nezhagate.com/v1/videos/jobs/img_9c1e...7a -H "Authorization: Bearer YOUR_API_KEY"

Estados del trabajo

EstadoSignificado
queuedEn cola. Incluye queue_position (trabajos por delante del tuyo) y eta_seconds (espera estimada).
processingGenerándose.
succeededTerminado. El resultado está en data[0].url y el detalle del cobro en usage.
failedFalló. El motivo está en error; los créditos reservados se devuelven íntegros.

succeeded y failed son estados finales; después, el trabajo ya no cambia.

Campos de la respuesta

CampoSignificado
idEl id del trabajo; se usa para consultarlo y en los webhooks.
objectimage.generation.job para imágenes, video.generation.job para vídeo.
statusqueued / processing / succeeded / failed.
createdCuándo se envió el trabajo, en segundos Unix.
modelEl ID del modelo con el que se envió.
queue_positionSolo mientras está en queued: cuántos trabajos hay por delante del tuyo.
eta_secondsSolo mientras está en queued: cuántos segundos faltan, aproximadamente, para que empiece.
data[].urlSolo con succeeded: el enlace al resultado (una imagen o un mp4), que se guarda 60 días.
usageSolo con succeeded: lo que se cobró. En imágenes indica images (imágenes entregadas) y resolution; en vídeo, clips.
errorSolo con failed: message, type, code y param; decide según code.
Respuesta al terminar · video
{
  "id": "img_9c1e...7a",
  "object": "video.generation.job",
  "status": "succeeded",
  "created": 1791199400,
  "model": "seedance-2.0",
  "data": [{"url": "https://img.nezhagate.com/i/5d41402abc....mp4"}],
  "usage": {"clips": 1, "model": "seedance-2.0"}
}
failed
{
  "id": "img_9c1e...7a",
  "object": "video.generation.job",
  "status": "failed",
  "created": 1791199400,
  "model": "seedance-2.0",
  "error": {"message": "...", "type": "invalid_request_error", "code": "content_policy", "param": null}
}

Consejos para consultar

  • Consulta las imágenes cada 2–3 segundos y los vídeos cada 5–10 segundos. Las consultas son gratis.
  • Deja de consultar en cuanto veas succeeded o failed.
  • Ponle a tu programa un tiempo máximo de espera total: las imágenes suelen tardar desde decenas de segundos hasta unos minutos, y los vídeos desde uno o dos minutos hasta más de una hora (los clips más largos y las colas tardan más); la documentación de cada modelo indica los tiempos habituales. Cuando se agote ese tiempo, no vuelvas a enviar el trabajo: sigue ahí; vuelve a consultarlo más tarde.
  • Un trabajo pertenece a la cuenta que lo envió: cualquier clave de esa cuenta puede consultarlo, y cualquier otra cuenta recibe 404 job_not_found. Los registros de trabajos se guardan 3 días; después, ese id también devuelve 404, aunque el enlace del resultado sigue funcionando durante sus 60 días.
  • Si el envío agota el tiempo de espera en tu lado antes de que recibas el id del trabajo, revisa en el registro de llamadas de la consola si el trabajo se creó antes de volver a enviarlo, para no pagar dos veces.

Webhooks en lugar de consultas

Añade callback_url al enviar y, en cuanto termine el trabajo, con éxito o con error, la pasarela enviará a esa URL exactamente lo mismo que devuelve el endpoint de consulta, firmado y con reintentos si la entrega falla. También puedes configurar una dirección para toda la cuenta en Configuración → Webhook, en la consola.

JSON · callback_url
{"model": "seedance-2.0", "prompt": "...", "duration": 5, "callback_url": "https://example.com/webhooks/nezhagate"}

Documentación de webhooks →

Facturación

  • Al enviar, se retiene de tu saldo el precio del trabajo; si el saldo no alcanza, recibes 402 y no se crea ningún trabajo.
  • Si sale bien, se liquida lo entregado: las imágenes según el número de imágenes entregadas y el vídeo según la duración o el clip que pediste (lo mismo que se retuvo); lo retenido de más se devuelve automáticamente. Si falla, se devuelve toda la retención.
  • Las consultas y los envíos de webhooks son gratis.

Cuánto tiempo se guardan los resultados

data[0].url apunta a nuestro almacenamiento y se borra automáticamente a los 60 días. Para seguir usando un resultado, descárgalo a tu propio almacenamiento o CDN.

Por qué falla un trabajo

Un trabajo fallido siempre se reembolsa íntegro. Estos son los códigos que verás con más frecuencia en un trabajo fallido:

codeCuándo apareceQué hacer
content_policyVídeo: el prompt, una referencia, o la imagen o el sonido generados no superaron la revisión de contenido (param es audio cuando lo que se marcó fue el sonido).Reescribe el prompt o cambia la referencia antes de volver a enviarlo; la misma entrada se rechaza otra vez.
moderation_blockedImagen: el sistema de seguridad del proveedor del modelo rechazó el prompt o una imagen de referencia (el código también puede ser content_policy_violation).Reescribe el prompt o cambia la referencia; si se reenvía sin cambios, normalmente se vuelve a rechazar.
render_failedVídeo: esta vez no salió ningún clip; suele ser algo puntual.Volver a enviar la misma petición suele funcionar.
invalid_materialVídeo: el proveedor encontró un problema en la entrada. Es un error genérico y a menudo puntual.Primero reenvíalo tal cual; si se repite, revisa las referencias y la longitud del prompt.
render_timeoutVídeo: la cola o el render tardaron demasiado y el proveedor abandonó el trabajo.Vuelve a enviarlo.
wait_timeoutVídeo: no llegó ningún clip dentro del tiempo máximo de espera que permite la línea.Vuelve a enviarlo.
upstream_unstableVídeo: la conexión se cortó durante el render.Vuelve a enviarlo.
result_fetch_failedEl vídeo se generó, pero su descarga o almacenamiento falló una y otra vez de nuestro lado.Vuelve a enviarlo.
invalid_requestVídeo: la línea no acepta esta combinación de parámetros (por ejemplo, duración, proporción y resolución).Ajusta los parámetros según indica message y vuelve a enviarlo.
upstream_errorCualquier otro fallo: se probaron todas las líneas, el trabajo esperó 15 minutos sin empezar, el proveedor lo rechazó sin dar motivo, etc.; message indica cuál.Volver a enviarlo suele funcionar.

Lista completa de códigos de error →

Ejemplo completo

Envío, consulta y descarga de una sola vez, con un tiempo máximo total y manejo de errores. Sustituye YOUR_API_KEY y ejecútalo.

Python
import time
import requests

BASE = "https://nezhagate.com/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}


def run_job(kind, payload, every=5, give_up=5400):
    """kind is "images" or "videos". Returns the finished job (succeeded or failed)."""
    job = requests.post(f"{BASE}/{kind}/generations", headers=HEADERS, json=payload, timeout=120)
    job.raise_for_status()                       # 4xx here: fix the request; nothing was billed
    job_id = job.json()["id"]
    deadline = time.time() + give_up
    while time.time() < deadline:
        time.sleep(every)
        r = requests.get(f"{BASE}/{kind}/jobs/{job_id}", headers=HEADERS, timeout=30).json()
        if r["status"] in ("succeeded", "failed"):
            return r
    raise TimeoutError(f"{job_id} still running; poll it again later, it is not lost")


job = run_job("videos", {"model": "seedance-2.0", "prompt": "a paper boat drifting down a rainy street",
                         "duration": 5, "size": "16:9"})
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    with open("clip.mp4", "wb") as f:            # keep your own copy: links expire after 60 days
        f.write(requests.get(url, timeout=300).content)
else:
    print(job["error"]["code"], job["error"]["message"])