HiAPI
Blog

Tutorial

Cómo llamar a muse-image-1.0/image-to-image en la API de HiAPI: curl, Python y un ejemplo de solicitud funcional

Oct 31, 2026
奶油色底上的多层薄纸叠层插画,一块陶红色纸层作点缀,构图克制均衡,为教程文章封面图。

Qué envía realmente una solicitud de imagen a imagen (image-to-image)

Antes de escribir la primera línea de código, conviene tener claro qué espera una llamada de imagen a imagen. A diferencia de una solicitud puramente de texto a imagen, donde el prompt es la única entrada creativa, muse-image-1.0/image-to-image requiere una imagen de origen junto con instrucciones sobre cómo transformarla.

Como mínimo, tu carga útil de solicitud incluye:

  • model: el identificador muse-image-1.0/image-to-image.
  • image: la imagen de origen que quieres editar. Normalmente se envía como una cadena codificada en base64 o como una URL de acceso público, según la forma que acepte el endpoint. Consulta el esquema de entrada del modelo para confirmar el nombre exacto del campo y el formato.
  • prompt: la descripción textual de la edición que quieres realizar. Sé específico sobre qué cambiar y qué conservar.
  • Parámetros opcionales como strength o guidance (si el modelo los expone) que te permiten controlar cuánto se aleja la salida de la imagen de origen. El conjunto exacto de parámetros varía según el modelo, así que confírmalo en la referencia de la API en lugar de darlo por supuesto.

El cambio mental clave es este: no estás generando desde cero, sino proporcionando un punto de partida y una dirección. Un prompt vago como "make it better" producirá resultados impredecibles porque el modelo tiene demasiada libertad. En cambio, un prompt como "replace the sky with a sunset and keep the buildings unchanged" le da al modelo una restricción clara.

Además, confirma de antemano las restricciones de la imagen: formatos admitidos, tamaño máximo y tamaño de archivo. Si tu imagen de origen es un PNG de 12 MB, puede que necesites reducirla o convertirla a JPEG antes de que se acepte la solicitud. Procesar correctamente la imagen antes de enviarla te ahorra un viaje de ida y vuelta y un mensaje de error confuso.

Una vez que tengas la imagen de origen y un prompt de edición claro, ya puedes hacer tu primera llamada.

Realiza tu primera solicitud con curl

La forma más rápida de validar una integración es enviar la solicitud directamente desde la terminal. La ruta exacta del endpoint y el esquema de la carga útil se definen en la referencia de la API del modelo; el ejemplo siguiente muestra la forma general para que puedas adaptarlo a la especificación actual.

curl -X POST https://api.hiapi.ai/v1/images/edits \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "muse-image-1.0/image-to-image",
    "image": "https://your-bucket.example.com/source.jpg",
    "prompt": "replace the sky with a warm sunset, keep the buildings unchanged",
    "strength": 0.6
  }'

Sustituye $HIAPI_API_KEY por una variable de entorno en lugar de pegar la clave directamente. Si tu herramienta lo admite, exporta la clave en la sesión de shell para que no quede en el historial de comandos.

La respuesta devuelta incluirá un identificador de trabajo. Como la edición de imágenes consume muchos recursos de cálculo, la API normalmente no devuelve la imagen terminada en la misma respuesta. En su lugar, recibirás un ID de trabajo y un estado. Guarda ese ID: lo usarás en el siguiente paso para consultar el resultado.

Si la llamada falla, lee con atención el cuerpo de la respuesta de error. Un 400 suele indicar un formato de carga útil incorrecto o un formato de imagen no admitido. Un 401 significa que la clave falta o es incorrecta. Un 429 significa que estás enviando solicitudes demasiado rápido. Corregir estos problemas en la terminal es mucho más fácil que depurarlos en una aplicación completa.

La misma solicitud implementada en Python

Una vez que la llamada curl funcione, conviértela en una pequeña función de Python. Usar requests mantiene el ejemplo con pocas dependencias y fácil de portar.

import os
import requests

HIAPI_API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = "https://api.hiapi.ai/v1/images/edits"

def submit_edit(image_url: str, prompt: str, strength: float = 0.6) -> str:
    payload = {
        "model": "muse-image-1.0/image-to-image",
        "image": image_url,
        "prompt": prompt,
        "strength": strength,
    }
    headers = {
        "Authorization": f"Bearer {HIAPI_API_KEY}",
        "Content-Type": "application/json",
    }
    response = requests.post(BASE_URL, json=payload, headers=headers, timeout=30)
    response.raise_for_status()
    data = response.json()
    return data["id"]

Algunas notas sobre esta función:

  • El parámetro timeout evita que el script se quede colgado indefinidamente si la red se estanca. Para una llamada de envío, 30 segundos es holgado, pero ajústalo según sea necesario.
  • raise_for_status() convierte los errores HTTP en excepciones, de modo que puedes capturarlos en un solo lugar.
  • Devolver solo el ID del trabajo mantiene la función enfocada. La lógica de sondeo debe ir en una función separada.

Si prefieres enviar la imagen de origen como bytes en lugar de una URL, lee el archivo, codifícalo en base64 y coloca el resultado en el campo image. Antes de cambiar, confirma el formato esperado en la documentación, ya que estas dos codificaciones no siempre son intercambiables.

Esta función se mantiene deliberadamente mínima. En un servicio real, añadirías registros, manejo de reintentos y validación de tipos, pero el patrón central de la solicitud es exactamente el que ves aquí.

Manejo de trabajos asíncronos y sondeo de resultados

La edición de imágenes lleva tiempo —normalmente desde unos pocos segundos hasta decenas de segundos, según el prompt y la resolución. La API lo gestiona aceptando tu solicitud, devolviendo un ID de trabajo y procesando el trabajo en segundo plano. Tu tarea es comprobar repetidamente hasta que el estado llegue a un estado terminal.

El sondeo suena simple, pero un bucle ingenuo que se dispara cada 100 ms desperdicia cuota y puede activar límites de velocidad. Usa un intervalo razonable y un límite de reintentos:

import time
import requests

def poll_job(job_id: str, interval: float = 2.0, max_attempts: int = 30) -> dict:
    url = f"https://api.hiapi.ai/v1/jobs/{job_id}"
    headers = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
    for attempt in range(max_attempts):
        response = requests.get(url, headers=headers, timeout=15)
        response.raise_for_status()
        data = response.json()
        status = data["status"]
        if status == "succeeded":
            return data
        if status == "failed":
            raise RuntimeError(data.get("error", "job failed"))
        time.sleep(interval)
    raise TimeoutError(f"job {job_id} did not finish after {max_attempts} attempts")

Un intervalo de dos segundos es un punto de partida razonable. Si tus trabajos suelen completarse en cinco segundos, puedes empezar a sondear con más frecuencia y luego aumentar el intervalo gradualmente. Si suelen tardar más, reduce la frecuencia de sondeo para evitar llamadas innecesarias.

El límite de reintentos es tu red de seguridad. Sin él, un trabajo atascado podría entrar en un bucle infinito. Al alcanzar el límite, lanza un error explícito para que quien llama sepa que la operación superó el tiempo de espera, en lugar de quedarse colgado en silencio.

En producción, considera mover el sondeo a un worker en segundo plano o a una cola, para que el hilo que maneja la solicitud no quede bloqueado esperando a que termine el renderizado de la imagen.

Descarga y almacenamiento de la imagen de salida

Cuando el trabajo alcanza el estado succeeded, la respuesta contiene una o varias imágenes de salida, normalmente proporcionadas como URLs. Tu siguiente paso es descargar la imagen y almacenarla de forma persistente.

def download_image(result: dict, dest_path: str) -> None:
    image_url = result["output"][0]["url"]
    response = requests.get(image_url, timeout=30)
    response.raise_for_status()
    with open(dest_path, "wb") as f:
        f.write(response.content)

Algunas consideraciones prácticas:

  • Las URLs de salida pueden ser temporales. Trátalas como recursos de corta duración y descárgalas a tiempo, en lugar de almacenar la URL como única referencia.
  • Escribe el archivo en modo binario. Las respuestas de imagen no son texto; abrirlo con "w" corrompería los datos.
  • Antes de declarar el éxito, verifica que el archivo no esté vacío. Una descarga de cero bytes suele significar que la URL ha caducado o que la solicitud fue bloqueada.

Para cualquier escenario más allá de un script local, transmite la descarga directamente a tu proveedor de almacenamiento en lugar de guardarla primero en disco. La mayoría de los SDK de almacenamiento de objetos aceptan objetos tipo archivo o flujos de bytes, así que puedes canalizar response.raw o response.content a la llamada de carga sin un archivo intermedio.

Por último, registra el ID del trabajo junto con el objeto almacenado. Si la salida se corrompe más adelante, o si necesitas reproducir una edición concreta, el ID del trabajo junto con la entrada original te permiten rastrear lo que se envió.

Puntos de fallo comunes y cómo solucionarlos

La mayoría de los problemas de integración pertenecen a unas pocas categorías. Aquí tienes cómo diagnosticar cada una.

La solicitud se rechaza antes de crear el trabajo

Un error 400 al enviar suele indicar una carga útil incorrecta. Comprueba que model sea exactamente muse-image-1.0/image-to-image, que image sea accesible o esté codificada correctamente en base64, y que prompt sea una cadena no vacía. Si la imagen es una URL, confirma que sea de acceso público: un bucket privado o una URL firmada caducada no superarán la validación.

El trabajo falla durante el procesamiento

Si el sondeo devuelve el estado failed, lee el mensaje de error del cuerpo de la respuesta. Las causas comunes incluyen una imagen demasiado grande, un prompt que infringe la política de contenido o valores de parámetros fuera del rango permitido. Reduce el tamaño de la imagen y simplifica el prompt para aislar la causa.

Tiempo de espera agotado en el sondeo

Si alcanzas el límite de reintentos sin un estado terminal, puede que el trabajo realmente necesite más tiempo o que esté atascado. Primero, aumenta max_attempts y confirma el tiempo de procesamiento típico en la referencia de la API. Si el trabajo sigue agotando el tiempo de espera, comprueba si la imagen de origen es anormalmente grande. No elimines simplemente el límite de reintentos: los reintentos acotados evitan bucles infinitos silenciosos.

Límites de velocidad

Una respuesta 429 significa que estás enviando solicitudes demasiado rápido. Retrocede, respeta cualquier cabecera retry-after y escalona los envíos concurrentes. Si estás procesando un lote de imágenes, procésalas de forma secuencial o usa un límite de concurrencia menor en lugar de enviarlas todas a la vez.

Salida ausente o truncada

Si el trabajo tiene éxito pero el archivo descargado está vacío, es posible que la URL de salida haya caducado entre el sondeo y la descarga. Descarga inmediatamente después del éxito. Si la imagen está corrupta, confirma que escribiste en modo binario y que no aplicaste ninguna codificación de texto al flujo.

La depuración es más fácil cuando registras la carga útil de la solicitud, el ID del trabajo y las transiciones de estado. Con estas tres piezas de información, casi cualquier fallo se puede rastrear hasta su origen.

Con la forma de la solicitud, los patrones de curl y Python, la lógica de sondeo y el paso de descarga, tienes un pipeline funcional para usar muse-image-1.0/image-to-image en la API de HiAPI. Empieza con el ejemplo de terminal, llévalo a un script y añade reintentos y registros una vez que la ruta normal sea estable.

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