REST API — генерация изображений, видео и LLM по API-ключу

Публичный API InPersona: генерация изображений и видео, OpenAI-совместимый чат с LLM, вебхуки, временное хранилище файлов. Авторизация по ключу ips_…, цены в кредитах — как на сайте.

Что такое публичный API#

Публичный API InPersona (/api/public/v1) даёт программный доступ ко всем генеративным моделям платформы: изображения, видео и LLM. Он устроен как у fal.ai или Replicate: вы отправляете задачу, получаете job id, а результат забираете поллингом или вебхуком.

  • Base URL: https://inpersona.ru/api/public/v1
  • Авторизация: заголовок Authorization: Bearer ips_… (API-ключ)
  • Цены: те же кредиты и те же тарифы, что и при генерации на сайте — API не дороже и не дешевле
  • Доступность: тарифы Pro и Business (фича api_access)

Если вам нужен доступ для AI-агентов (Claude, Cursor, Hermes) — посмотрите также MCP-сервер: это тот же функционал, но по протоколу MCP.

API-ключи#

Ключи выпускаются на странице Настройки → API-токены. Формат — ips_ + 43 символа; секрет показывается один раз при создании. У каждого ключа есть:

  • Права (scopes): read — чтение (поллинг, каталоги, баланс), generate — генерация (списывает кредиты). Для работы с API нужны оба.
  • Срок жизни — опциональная дата истечения (до 365 дней).
  • Лимиты кредитов — опциональные потолки трат: за календарный месяц (UTC) и за всё время жизни ключа. При достижении лимита запросы отклоняются с ошибкой token_spend_limit_exceeded (403); неудачные генерации возвращают кредиты и в кошелёк, и в лимит ключа.
  • Секрет подписи вебхуков (whsec_…) — показывается вместе с ключом при создании и всегда доступен (и перевыпускается) на той же странице настроек. Нужен для проверки подписей вебхуков, см. ниже.

Ключ можно отозвать в любой момент — все клиенты с ним сразу начнут получать 401.

Быстрый старт#

# 1. Отправляем задачу
curl -s https://inpersona.ru/api/public/v1/images/generations \
  -H "Authorization: Bearer ips_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Женская кожаная сумка на мраморном столе, мягкий утренний свет",
    "model": "google/gemini-3-pro-image-preview-fast",
    "aspect_ratio": "1:1",
    "resolution": "high"
  }'
# → 202 { "id": "img_abc123…", "status": "queued", "credits_spent": 1.5, … }

# 2. Поллим до status = completed (каждые 3–10 секунд)
curl -s https://inpersona.ru/api/public/v1/jobs/img_abc123… \
  -H "Authorization: Bearer ips_ВАШ_КЛЮЧ"
# → { "status": "completed", "output": { "image_url": "https://s3.inpersona.ru/…" }, … }

Объект job#

Все генерации возвращают единый объект job:

{
  "id": "img_5KpXk9…",
  "kind": "image",
  "status": "queued",
  "credits_spent": 1.5,
  "created_at": "2026-08-18T12:00:00Z",
  "completed_at": null,
  "output": null,
  "error": null
}
  • id — префиксованный идентификатор: img_… (изображение), vid_… (видео), chat_… (LLM-вызов — возвращается в заголовке ответа X-InPersona-Job-Id, терминален сразу по завершении).
  • statusqueuedprocessingcompleted | failed.
  • output — при completed: для изображений { image_url, thumbnail_url, width, height, seed }, для видео { video_url, poster_url, width, height, duration_seconds, fps, file_size_bytes }.
  • error — при failed: { code, message }. Кредиты за неудачную генерацию возвращаются автоматически.

Эндпоинты#

POST /images/generations#

Асинхронная генерация изображения. Возвращает 202 + job (img_…).

ПараметрТипОбязателенОписание
promptstringдаТекстовый промпт
modelstringнетId модели из GET /models (по умолчанию google/gemini-3-pro-image-preview-fast)
aspect_ratiostringнет1:1 (по умолчанию), 3:4, 4:3, 16:9, 9:16 и др.
resolutionstringнетstandard, high (по умолчанию), ultra
reference_image_urlsstring[]нетПубличные URL референсов (лимит зависит от модели — см. capabilities.max_references в GET /models). Загружайте через POST /files
webhook_urlstringнетHTTPS-URL для уведомления о завершении

Примечание: разрешение ultra гейтится тарифом (фича high_resolution) — запрос выше вашего плана вернёт 403 feature_required.

POST /videos/generations#

Асинхронная генерация видео. Возвращает 202 + job (vid_…).

ПараметрТипОбязателенОписание
modelstringдаId видеомодели из GET /models
operationstringнетt2v (по умолчанию), i2v, r2v, v2v, frame_to_frame
prompt / negative_promptstringзависит от моделиОписание сцены
duration_secondsintegerдаДлительность (допустимые значения — в capabilities модели)
resolutionstringнет480p, 720p, 1080p, 4k — по capabilities
aspect_ratiostringнетПо умолчанию — первый поддерживаемый моделью
audio_enabledbooleanнетТолько для моделей с поддержкой аудио
reference_image_urlsstring[]нетРеференсы (для i2v / r2v)
frame_imagesobject[]нет[{ "url": "…", "position": "first" }] для frame_to_frame
reference_video_urlsstring[]нетИсходное видео для v2v
provider_settingsobjectнетПасстру для модель-специфичных настроек (до 4 КБ)
auto_polish_enabledbooleanнетПо умолчанию true: промпт автоматически дорабатывается перед отправкой. Передайте false, чтобы отправить промпт как есть
webhook_urlstringнетHTTPS-URL для уведомления

Видео генерируется от десятков секунд до нескольких минут — поллите каждые 5–15 секунд или используйте вебхук.

GET /jobs/{id}#

Единый поллинг любой задачи по её id (img_… / vid_… / chat_…). Требует scope read.

POST /chat/completions#

OpenAI-совместимый доступ к LLM. Любой OpenAI SDK работает после замены base_url:

from openai import OpenAI

client = OpenAI(
    base_url="https://inpersona.ru/api/public/v1",
    api_key="ips_ВАШ_КЛЮЧ",
)

resp = client.chat.completions.create(
    model="minimax/minimax-m2",          # id из GET /models → llm_models
    messages=[{"role": "user", "content": "Придумай слоган для бренда керамики"}],
    stream=False,                         # stream=True — SSE-стриминг
)
print(resp.choices[0].message.content)
  • Поддерживаются model, messages, max_tokens (потолок 16384), temperature, response_format, stream.
  • Каждый ответ несёт поллящийся id задачи в заголовке X-InPersona-Job-Id (chat_…, работает с GET /jobs/{id}).
  • Тарификация metered: реальная стоимость запроса у провайдера конвертируется в кредиты по внутреннему курсу $0.10 = 1 кредит. Для stream: false итог приходит в usage.cost_credits; дробные остатки копятся и списываются целыми кредитами. Если кошелёк не покрывает счёт целиком, списывается всё доступное, а остаток становится долгом — новые запросы получают 402 до пополнения.
  • Особенности стриминга: чанки проксируются как есть, поэтому usage.cost_credits в стрим НЕ подставляется (читайте job по X-InPersona-Job-Id); при ошибке провайдера посреди стрима приходит один чанк data: {"error": …} и затем data: [DONE]. Стримы ограничены 120 секундами и 2 конкурентными потоками на аккаунт (too_many_streams, 429).
  • Каталог доступных LLM — в GET /models (секция llm_models). Эндпоинт также отдаёт OpenAI-формат {"object":"list","data":[…]}, так что client.models.list() работает.

POST /files — временное хранилище#

Загрузка входных файлов (референсы, кадры, аудио). Файлы хранятся 7 дней, затем автоматически удаляются — это хранилище для входных данных, а не архив. Результаты генераций хранятся постоянно.

Принимает одно из:

  • multipart-поле file;
  • JSON { "data": "data:image/jpeg;base64,…" };
  • JSON { "url": "https://…" } — файл скачает сервер.
curl -s https://inpersona.ru/api/public/v1/files \
  -H "Authorization: Bearer ips_ВАШ_КЛЮЧ" \
  -F "file=@product.jpg"
# → 201 { "id": "file_42", "url": "https://s3.inpersona.ru/…", "expires_at": "2026-08-25T…" }

Возвращённый url сразу валиден в reference_image_urls, frame_images и других полях. Изображения нормализуются автоматически (HEIC/WebP → JPEG, до 4096px). Лимиты: изображения до 20 МБ, multipart-видео до 200 МБ; вариант url скачивается сервером с потолком 50 МБ. Не используйте URL после expires_at.

GET /files/{id} (scope read) возвращает метаданные файла; принимаются обе формы id — file_42 и 42.

GET /models#

Каталог моделей: image_models и video_models с полной матрицей capabilities (форматы, разрешения, длительности, лимиты референсов), llm_models — доступные LLM. Кэшируйте на время сессии.

GET /credits#

Баланс кошелька, полная таблица цен в кредитах (та же, что на сайте) и снапшот лимитов вашего ключа:

{
  "balance": { "total": 250.0, "monthly": 200.0, "rollover": 30.0, "purchased": 20.0 },
  "pricing": { "…": "…" },
  "token": { "credit_limit_monthly": 100.0, "credits_spent_month": 12.5, "…": "…" }
}

Вебхуки#

Передайте webhook_url при создании задачи — по завершении (успех или ошибка) InPersona отправит POST:

{
  "event": "job.completed",
  "data": { "id": "img_…", "status": "completed", "output": { "…": "…" } }
}

Подпись. Каждая доставка подписана заголовком X-InPersona-Signature: t=<unix>,v1=<hex>, где v1 = HMAC-SHA256(secret, "<t>." + body). Секрет (whsec_…) привязан к вашему API-ключу — показывается при создании и всегда доступен (и перевыпускается) в Настройках → API-токены. Отклоняйте доставки, чей t отличается от текущего времени больше чем на ±5 минут (защита от replay); доверяйте только подписанному телу — остальные заголовки информационные. Проверка:

import hmac, hashlib

def verify(signature_header, body, secret):
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.{body}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Ретраи. Успех — любой ответ 2xx (соединение за 5 с, ответ за 10 с). При неудаче — до 7 попыток суммарно с нарастающими паузами между ними (30 с → 2 мин → 10 мин → 30 мин → 2 ч → 6 ч), затем доставка помечается exhausted. Вебхуки — best-effort: источник истины всегда GET /jobs/{id}.

Требования к URL: только HTTPS, публичный хост (приватные/локальные адреса и сам inpersona.ru отклоняются).

Идемпотентность#

Все POST-эндпоинты принимают заголовок Idempotency-Key (8–255 символов; ключи вне диапазона молча игнорируются). Повтор с тем же ключом и телом вернёт закэшированный ответ вместо повторного списания; повтор с другим телом — ошибку idempotency_mismatch; конкурентный дубликат в полёте — 409 concurrent_request. Ответы с ошибкой освобождают ключ сразу, так что исправленный повтор с тем же ключом сработает. Ключи живут 24 часа. Для chat/completions работает только при stream: false.

Ошибки#

Единый формат:

{ "error": { "type": "invalid_request_error", "code": "model_not_found", "message": "…", "param": "model" } }
HTTPtypeТипичные code
401authentication_errorapi_key_required, invalid_api_key
402insufficient_creditsinsufficient_credits (+ поля required, available)
403permission_errorinsufficient_scope, feature_required, token_spend_limit_exceeded, workflow_token_not_allowed
404not_found_errornot_found
409invalid_request_errorconcurrent_request (тот же Idempotency-Key в полёте)
422invalid_request_errormodel_not_found, missing_parameter, invalid_operation, invalid_messages, messages_too_large, invalid_webhook_url, prompt_blocked, file_too_large, file_empty, unsupported_format, unsafe_url, upload_error, validation_error, idempotency_mismatch
429rate_limit_errorrate_limited (+ заголовок Retry-After), upstream_rate_limited, too_many_streams
500api_errorinternal_error
502api_errorupstream_error — ошибка провайдера модели

Упавшие задачи несут error.code: "generation_failed" и человекочитаемый error.message.

Лимиты запросов#

На один ключ в минуту: 120 запросов всего по API, из них до 20 генераций (images + videos), до 10 вызовов chat/completions, до 20 загрузок файлов. При превышении — 429 с заголовком Retry-After.

Цены#

Каждая генерация стоит столько же кредитов, сколько на сайте, — таблица в GET /creditspricing. LLM тарифицируется по фактической стоимости провайдера ($0.10 = 1 кредит). Кредиты за неудачные генерации возвращаются автоматически.