HiAPI
Блог

Руководство

Как использовать flux-2-klein-9b/image-to-image через API hiapi: curl, Python и рабочий запрос

Jul 30, 2026
Как использовать flux-2-klein-9b/image-to-image через API hiapi: curl, Python и рабочий запрос

Этот учебный материал показывает, как вызвать режим image-to-image модели flux-2-klein-9b через единый API задач hiapi: точную структуру запроса, рабочие команды curl, Python-скрипт, который можно сразу вставить в бэкенд, и одну особенность схемы, на которую вы наткнётесь, если переходите с других моделей на платформе.

После прочтения у вас будет скрипт, который принимает URL одного референсного изображения и промпт редактирования, а возвращает URL готового изображения для скачивания.

Что вам понадобится

  • Аккаунт hiapi и API key. Его можно получить в консоли hiapi.
  • Ровно одно референсное изображение, размещённое по публичному HTTPS-URL. В отличие от некоторых image-to-image моделей на платформе, которые принимают несколько референсных изображений, массив image_urls у flux-2-klein-9b ограничен одним элементом — подробности ниже.
  • Текстовый промпт, описывающий редактирование или преобразование.

Все запросы отправляются на единый эндпоинт задач, ключ передаётся в заголовке Authorization:

POST https://api.hiapi.ai/v1/tasks
Authorization: Bearer sk-<your-key>
Content-Type: application/json

Структура запроса (и одно поле, на котором легко ошибиться)

Идентификатор модели — flux-2-klein-9b/image-to-image — передавайте его как есть, точно так, как он указан в /v1/models. Его схема input строгая:

Поле Тип Обязательно Описание
prompt string Да Инструкция для редактирования.
image_urls string[] Да Ровно один URL референсного изображения — minItems и maxItems равны 1.
aspect_ratio string Нет Одно из значений: 1:1, 4:3, 3:4, 16:9, 9:16.
seed integer Нет Зафиксируйте его, чтобы воспроизводить результат.
output_format string Нет Одно из значений: jpeg, png, webp.

Прежде чем писать код, стоит узнать две вещи:

  1. image_urls принимает только один URL, а не диапазон.Если вы построили общий код отправки вокруг модели, принимающей 1–14 референсных изображений, отправка двух URL здесь будет отклонена — maxItems: got 2, want 1. Эта модель выполняет редактирование по одному изображению, а не слияние нескольких референсов.
  2. Лишние поля отклоняются, а не игнорируются.resolution, size, n, strength, image_size и num_images — все они распространены в других моделях изображений на платформе — здесь возвращают 400 INVALID_REQUEST: additional properties '<field>' not allowed. Не переиспользуйте payload другой модели, не проверив схему этой модели.

Минимальный рабочий пример: curl

Создайте задачу:

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-2-klein-9b/image-to-image",
    "input": {
      "prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
      "image_urls": ["https://your-cdn.example.com/subject.png"],
      "aspect_ratio": "3:4",
      "output_format": "png"
    }
  }'

После успешной отправки в data возвращается id задачи:

{"code": 200, "data": {"taskId": "tk-hiapi-..."}}

Опрашивайте результат:

curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... \
  -H "Authorization: Bearer sk-<your-key>"

Во время выполнения data.status последовательно проходит queued → handling → archiving. По завершении он переключается на "success", а изображение появляется в data.output:

{
  "code": 200,
  "data": {
    "status": "success",
    "output": [{"url": "https://.../result.png", "expireAt": "..."}]
  }
}

URL вывода живёт недолго (обратите внимание на expireAt). Скачайте байты сразу и сохраните их у себя — никогда не используйте горячие ссылки и не сохраняйте исходный URL.

Python для продакшена: отправка, опрос, скачивание

import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}


def edit_image(prompt: str, image_url: str,
               aspect_ratio: str = "1:1", output_format: str = "png") -> bytes:
    # 1. Создать задачу — image_urls этой модели принимает ровно один URL
    resp = requests.post(API_BASE, headers=HEADERS, json={
        "model": "flux-2-klein-9b/image-to-image",
        "input": {
            "prompt": prompt,
            "image_urls": [image_url],
            "aspect_ratio": aspect_ratio,
            "output_format": output_format,
        },
    }, timeout=60)
    body = resp.json()
    if resp.status_code != 200 or not (body.get("data") or {}).get("taskId"):
        raise RuntimeError(f"create failed [{resp.status_code}]: {body}")
    task_id = body["data"]["taskId"]

    # 2. Опрашивать до терминального состояния
    deadline = time.time() + 600
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS,
                            timeout=30).json().get("data") or {}
        if task.get("status") == "success":
            outputs = task.get("output") or []
            if not outputs or not outputs[0].get("url"):
                raise RuntimeError(f"task {task_id} succeeded but returned no output URL")
            # 3. Скачать сразу — URL истекает
            img = requests.get(outputs[0]["url"], timeout=120)
            img.raise_for_status()
            return img.content
        if task.get("status") == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(5)
    raise TimeoutError(f"task {task_id} still running after 600s")


if __name__ == "__main__":
    png = edit_image(
        "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
        "https://your-cdn.example.com/subject.png",
        aspect_ratio="3:4",
    )
    with open("edited.png", "wb") as f:
        f.write(png)
    print(f"saved edited.png ({len(png)} bytes)")

Воспроизводимое редактирование с помощью seed

Передайте целое число seed, и модель будет использовать его в этой генерации — это полезно, когда вы хотите оставить всё остальное неизменным и провести A/B-тестирование формулировки промпта:

{
  "model": "flux-2-klein-9b/image-to-image",
  "input": {
    "prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
    "image_urls": ["https://your-cdn.example.com/subject.png"],
    "seed": 42
  }
}

Если его опустить, модель будет выбирать seed случайно при каждом вызове — именно это и нужно для обычного продакшен-трафика: когда вам нужны разные варианты результата, меняйте промпт, а не seed.

Callback вместо опроса

Для скриптов и очередей воркеров опрос подходит. Для веб-бэкенда зарегистрируйте callback при создании задачи, чтобы hiapi отправлял вам терминальное состояние — объект callback находится на верхнем уровне запроса, рядом с model:

{
  "model": "flux-2-klein-9b/image-to-image",
  "input": { "...": "..." },
  "callback": {
    "url": "https://your-app.example.com/hooks/hiapi",
    "when": "final"
  }
}

С "when": "final" вы получите ровно один POST, когда задача достигнет состояния успеха или ошибки. У обработчика два правила:

  • Сделайте его идемпотентным.Стройте логику обработки по ключу task id, чтобы повторно доставленный webhook не обрабатывался дважды. Сохраняйте task id при отправке и рассматривайте callback как «пойди и заверши», а не как единственный источник истины.
  • Сохраняйте путь сверки.Если в момент срабатывания callback ваш эндпоинт недоступен, периодический обход неразрешённых id и GET /v1/tasks/<id> позволит поймать всё пропущенное.

Если ваш callback, похоже, так и не приходит, пройдитесь по материалу почему ваш callback задачи hiapi не срабатывает — там в порядке вероятности разобраны типичные причины сбоев.

Обработка ошибок: три ключевых сценария сбоя

При создании возвращается 401 permission_denied — ваш ключ не может использовать эту модель. Тело ответа выглядит так:

{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}

Если вы с этим столкнулись, проверьте материал недействительный API key или посмотрите права ключа на модели в консоли.

При создании возвращается 400 INVALID_REQUEST — ваш input не соответствует схеме этой модели. Сообщение прямо указывает на нарушающее поле (например, <root>: additional properties 'resolution' not allowed или image_urls: maxItems: got 2, want 1). Это постоянные ошибки — повторная отправка того же payload будет всегда терпеть неудачу; исправьте поле и отправьте заново.

Задача возвращает status: "fail" — создание прошло успешно, но генерация не удалась (референсный URL, который бэкенд не может получить, политика контента, ёмкость вышестоящего сервиса). Поля задачи error.code и error.message скажут, что именно. В отличие от 400, некоторые из них временные, и стоит один раз повторить с экспоненциальной задержкой.

Также полезно знать: GET по несуществующему или истёкшему id задачи возвращает 404 task not found — при сверке это следует считать терминальным состоянием, а не «всё ещё ожидает».

Куда двигаться дальше

Частые вопросы

Могу ли я передать несколько референсных изображений в flux-2-klein-9b image-to-image? Нет. У image_urls и minItems, и maxItems равны 1 — ровно один URL, ни больше ни меньше. Если вам нужно слияние нескольких референсов, посмотрите модели, схема которых допускает более широкий диапазон, например seedream-5.0-lite.

Почему я получаю «additional properties 'resolution' not allowed»? Схема input API задач зависит от модели. flux-2-klein-9b/image-to-image вообще не принимает поля resolution или size — размер вывода следует за входным изображением и aspect_ratio. Удалите любые поля размера, перенесённые из payload другой модели.

Могу ли я отправить референсное изображение в base64, а не по URL? Схема этой модели принимает image_urls — массив URL. Разместите изображение по доступному HTTPS-URL (подойдёт любой CDN, объектное хранилище или pre-signed URL) и передайте этот URL.

Что на самом деле контролирует seed? Одинаковые seed, prompt и референсное изображение воспроизводят одинаковый результат генерации — это полезно для контролируемых A/B-тестов промптов. В обычном трафике опускайте его, чтобы каждый вызов получал свой случайный seed.

Тот же код подходит для flux-2-klein-9b text-to-image? Эндпоинт и процесс те же, но у text-to-image своя схема input (без image_urls). Замените id модели на flux-2-klein-9b/text-to-image и уберите референсное изображение — затем проверьте тестовым вызовом, поскольку обязательные поля различаются по режимам.

Сколько стоит каждое изображение? Цена рассчитывается по использованию за каждую генерацию. Смотрите актуальные тарифы на странице цен.

Похожие статьи

Все статьи →

Начните работать с постоянными артефактами ИИ

Получите API-ключ и вызывайте модели изображений, видео и аудио через один API на основе tasks.

Читать документацию API