HiAPI
Blog

Tutorial

Expiración de las URL de salida de tareas de HiAPI (expireAt): descarga y transfiere los resultados antes de que caduquen

output[].url en estado terminal lleva expireAt; al expirar devuelve 404: cómo detectarlo, cómo descargar a tiempo y cómo recuperarse tras la expiración.

Oct 22, 2026
Expiración de las URL de salida de tareas de HiAPI (expireAt): descarga y transfiere los resultados antes de que caduquen

Usas POST /v1/tasks para generar una imagen o un vídeo, y cuando la tarea termina obtienes de data.output[0].url un enlace de temp.hiapi.ai que en ese momento abre bien, pero al acceder días después se convierte en una página HTML de «Not Found»:

HTTP/2 404
content-type: text/html

Lo que devuelve no son bytes de imagen/vídeo, sino una página 404 de Cloudflare. Solo hay una razón: la URL de salida de la tarea es un enlace temporal con validez limitada; en cuanto llega el expireAt (marca de tiempo Unix) del estado terminal, el objeto en temp.hiapi.ai se elimina y el hotlink deja de funcionar.

Primero, entiende los campos del estado terminal

Cuando el sondeo de la tarea alcanza el estado terminal (GET /v1/tasks/{taskId}) con éxito, los campos clave tienen este aspecto:

{
  "code": 200,
  "data": {
    "taskId": "tk-hiapi-xxxxxxxx",
    "status": "success",
    "storage": "temp",
    "created": 1791828181,
    "completed": 1791828201,
    "output": [
      {
        "artifactId": "...",
        "type": "image",
        "url": "https://temp.hiapi.ai/....png",
        "expireAt": 1792432981
      }
    ]
  },
  "message": "success"
}
  • data.status == "success" es el éxito en estado terminal (el fallo es "fail"; en cola/procesando es queued / handling / archiving).
  • data.output es un array, cada elemento lleva su propio url y expireAt.
  • expireAt es una marca de tiempo Unix entera (no una cadena ISO). Para saber si ha expirado solo mira eso: now >= expireAt significa expirado.
  • El valor de data.storage es "temp"; esto ya es una pista: es almacenamiento temporal, no para que lo referencies a largo plazo.

¿Cuánto dura la validez? En una muestra medida, expireAt - completed = 604800 segundos (exactamente 7 días). Pero no codifiques «7 días» a fuego en el código: guíate por el expireAt de cada salida; distintos modelos/estrategias de almacenamiento pueden variar.

Causas habituales (ordenadas por probabilidad de ocurrencia)

  1. Guardar el hotlink en la base de datos para referencia a largo plazo: al persistir se guarda la URL temporal temp.hiapi.ai/..., y tras pasar expireAt todo devuelve 404 en masa. Es el error más común.
  2. No descargar inmediatamente al obtener el estado terminal: se tarda mucho (más que el TTL) en ir a buscar los bytes tras terminar la tarea.
  3. Creer que volver a sondear «refresca» el enlace: si haces otra GET con el mismo taskId, devuelve la misma url y el mismo expireAt; los metadatos persisten, pero el objeto en sí no se regenera tras expirar; volver a hacer GET no te da un enlace nuevo.
  4. Confundir el 404 con un fallo de la tarea: la tarea en realidad ya estaba en success hace tiempo, solo que el activo de salida ha expirado; son dos cosas distintas.

Pasos de corrección

  1. En cuanto llegue el estado terminal, descarga los bytes de inmediato. Tras sondear hasta data.status == "success", haz una GET a cada output[].url para bajar el binario enseguida; no lo encoles ni lo aplaces.
  2. Transfiérelo a tu propio almacenamiento de objetos (R2 / S3 / disco local) y sustitúyelo por tu propia URL persistente.
  3. En la base de datos guarda solo tu propia URL persistente; nunca guardes el hotlink temp.hiapi.ai.
  4. Antes de descargar, verifica que no ha expirado: descarga solo si now < expireAt; si ya expiró, pasa directamente a la lógica de reejecución.
  5. Si ha expirado, solo puedes reejecutar la tarea. No existe eso de «cambiar el taskId por un enlace nuevo»; si vuelves a hacer GET del mismo taskId, obtienes la misma url inválida. Para volver a obtener un resultado descargable, solo puedes enviar la tarea de nuevo (se vuelve a facturar).
  6. Controla el orden de descarga en tareas por lotes: la primera tarea que termina del lote es también la que tiene el expireAt más temprano. Descarga en cuanto termine cada una, no esperes a que termine todo el lote para descargar en bloque; de lo contrario, las que terminaron antes pueden estar al borde de expirar.

Ejemplo mínimo de verificación

Confirma que tu key funciona, que la interfaz de tareas devuelve resultados y observa con tus propios ojos el expireAt. Usa el id desnudo del modelo (z-image, no añadas sufijos como /text-to-image, dará 400):

# 1. Enviar la tarea
curl -s https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "z-image",
    "input": { "prompt": "a red apple on a wooden table" }
  }'
# → {"code":200,"data":{"taskId":"tk-hiapi-..."},"message":"success"}

# 2. Con el taskId del paso anterior, sondear el estado terminal y leer url y expireAt
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxx \
  -H "Authorization: Bearer $HIAPI_API_KEY"
# cuando data.status pase a "success", ya tienes data.output[0].url / .expireAt

Si falla la autenticación, devuelve HTTP 401 + {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}}; eso es un problema de la key, no de expiración. Si no encuentra la tarea, devuelve {"code":404,"data":null,"message":"task not found"}; normalmente es que el taskId está mal copiado o se usó la key de otro entorno.

Enlaces relacionados

FAQ

P: ¿Qué formato tiene exactamente expireAt? R: Marca de tiempo Unix entera (segundos), por ejemplo 1792432981, no una cadena ISO. Para saber si ha expirado, usa now >= expireAt.

P: Una vez que el enlace ha expirado, ¿puedo volver a hacer GET de la tarea para «renovar» el enlace? R: No. Si vuelves a hacer GET del mismo taskId, obtienes la misma url original y el mismo expireAt original; el objeto ya se ha eliminado. Para volver a obtener un resultado descargable, solo puedes reejecutar la tarea (se vuelve a facturar).

P: Tras la expiración, ¿el acceso devuelve 403 o 404? R: Devuelve HTTP 404, y el body es una página text/html de Cloudflare «Not Found», no bytes de imagen ni un AccessDenied XML. Si tu lógica de descarga solo acepta 200, recuerda tratar «todo lo que no sea 200 como fallo».

P: ¿La validez es siempre de 7 días? R: En la muestra medida son 7 días (604800 segundos), pero guíate por el expireAt de cada salida; no codifiques los días a fuego en el código.

P: ¿Cuál es la forma más segura de descargar un lote de tareas? R: Por orden de finalización: lo que primero llega a success es lo primero cuyos bytes se descargan. Las salidas que terminan antes tienen también el expireAt más temprano; dejarlo todo para una descarga en bloque al final hace fácil caer en la proximidad de la expiración.

Artículos relacionados

Ver todos los artículos →

Empieza a crear con artefactos de IA persistentes

Consigue tu API key y llama a modelos de imagen, vídeo y audio a través de una única API basada en tasks.

Leer la documentación de la API