← Developer API

Документация Developer API

Генерация картинок и видео, запросы к языковым моделям — из вашего кода, n8n или любого HTTP-клиента. Тарифы как в студии, деньги с того же баланса.

С чего начать

Через API доступно то же, что и в студии: генерация картинок, видео и запросы к языковым моделям. Тарифы одинаковые, деньги списываются с того же баланса, что и при работе через сайт. Отдельного счёта для API нет.

API устроен по правилам REST: вы отправляете HTTP-запрос с ключом в заголовке и получаете JSON в ответ. Никаких SDK ставить не обязательно — хватит curl, requests, axios или узла HTTP в n8n.

Шаг 1. Получить ключ

Зайдите в Настройки → API и нажмите «Создать ключ». Секрет показывается один раз — мы храним только его отпечаток и восстановить исходную строку не можем. Потеряли — отзывайте ключ и создавайте новый.

Ключ — это доступ к вашим деньгам

Любой, у кого есть ключ, тратит ваш баланс. Не кладите его в публичный репозиторий, во фронтенд и в переписку. Для подрядчика заводите отдельный ключ — его можно отозвать, не трогая остальные.

Шаг 2. Проверить, что ключ работает

Самый безопасный первый запрос — узнать баланс. Он ничего не стоит и сразу показывает, принят ли ключ.

Проверка ключа
export ARK_KEY="ark_live_..."

curl -sS -H "Authorization: Bearer $ARK_KEY" \
  https://arckep.ru/api/v1/balance

В ответе — остаток в рублях. Если пришла ошибка с кодом UNAUTHORIZED, значит ключ скопирован не полностью или уже отозван.

Шаг 3. Первая генерация

Картинки и видео делаются асинхронно: запрос возвращается сразу, а результат появляется позже. В ответ приходит номер задачи, по которому можно спрашивать статус.

Создать картинку
curl -sS -X POST https://arckep.ru/api/v1/images/generations \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "model": "gemini-3.1-flash-image",
    "prompt": "красное яблоко на мраморном столе"
  }'
Узнать статус (id из ответа выше)
curl -sS -H "Authorization: Bearer $ARK_KEY" \
  https://arckep.ru/api/v1/images/generations/123
ЧтоЗначение
Адрес APIhttps://arckep.ru/api/v1
АвторизацияAuthorization: Bearer ark_live_…
ФорматJSON; загрузка файлов — multipart
Валютарубли, суммы приходят строкой
Версия для LLM-агентовllms.txt

Ключи и права доступа

Ключ передаётся в заголовке Authorization с префиксом Bearer. Все ключи начинаются с ark_live_.

http
Authorization: Bearer ark_live_<ваш секрет>

Права (scopes)

У каждого ключа есть набор прав. Это защита от чужой ошибки: ключ, выданный для картинок, не сможет потратить деньги на видео или на языковые модели.

ПравоЧто разрешает
images:writeСоздавать картинки, смотреть их статус и список
videos:writeТо же для видео
chat:writeЗапросы к языковым моделям. По умолчанию НЕ выдаётся — включайте осознанно
balance:readСмотреть баланс
uploads:writeЗагружать файлы. Также работает, если есть право на картинки или видео

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

Не хватило права

Придёт ошибка FORBIDDEN, и в поле details.required будет указано, какого именно права не хватает. Добавить право существующему ключу нельзя — создайте новый с нужным набором.

Управление ключами из кода

Обычно ключи создают в интерфейсе, но это можно делать и программно — из сессии студии (обычная авторизация на сайте, не API-ключ).

МетодАдресЧто делает
POST/api/developer/keysСоздать. В ответе один раз приходят и сам ключ, и секрет для проверки вебхуков
GET/api/developer/keysСписок ключей без секретов
PATCH/api/developer/keys/{id}Переименовать или сменить адрес вебхука
POST/api/developer/keys/{id}/rotate-webhook-secretВыдать новый секрет вебхука; старый перестаёт действовать
DELETE/api/developer/keys/{id}Отозвать ключ немедленно и навсегда

Каталог моделей

Список моделей задаётся на сервере и меняется. Не зашивайте названия из этой страницы в код — спрашивайте каталог у API. Это единственный достоверный источник: там же приходят поддерживаемые пропорции, разрешения, длительности и цены.

bash
curl -sS -H "Authorization: Bearer $ARK_KEY" \
  https://arckep.ru/api/v1/models

Каждая запись имеет поле type: image, video или chat. Оно говорит, в какой раздел API её отправлять.

Фрагмент ответа
{
  "object": "list",
  "data": [
    {
      "id": "gemini-3.1-flash-image",
      "type": "image",
      "modes": ["generate", "edit_with_reference"],
      "aspect_ratios": ["1:1", "16:9", "9:16"],
      "resolutions": ["1K", "2K", "4K"],
      "max_images": 1,
      "supports_thinking": true
    },
    {
      "id": "wan2.7-t2v",
      "type": "video",
      "name": "Wan 2.7 T2V",
      "modes": ["t2v"],
      "max_duration": 10,
      "pricing_hint": "… ₽/с"
    },
    {
      "id": "openai/gpt-5.4-mini",
      "type": "chat",
      "context_window": 400000,
      "capabilities": ["vision", "function_calling", "streaming"],
      "pricing_rub_per_1m": { "input": 82.5, "output": 495.0 }
    }
  ]
}

Почему модели нет в списке

В студии моделей больше, чем в API. Наружу открыты только те, для которых проверен расчёт стоимости. Новые появляются после проверки. Если нужной модели нет — напишите нам, это не техническое ограничение.

Баланс

Показывает тот же остаток, что и в личном кабинете. Если вы состоите в организации и она активна, вернётся баланс организации — с него же и списывается.

json
{
  "balance_rub": "150.00",
  "is_corporate": false,
  "corporate_name": null
}

Суммы всюду приходят строками, а не числами. Это сделано намеренно: дробные рубли в формате JSON-числа теряют точность при округлении. Разбирайте их как десятичные, а не как float.

Загрузка файлов

Чтобы использовать своё изображение как основу для генерации, загрузите его к нам и передайте полученную ссылку. Можно передавать и свою публичную HTTPS-ссылку, но загруженные к нам файлы надёжнее: провайдеры иногда не могут скачать файл со стороннего хостинга.

bash
curl -sS -X POST https://arckep.ru/api/v1/uploads \
  -H "Authorization: Bearer $ARK_KEY" \
  -F "file=@./reference.png"
Ответ
{
  "key": "images/42/9f3c….png",
  "url": "https://…подписанная-ссылка…"
}
ПараметрЗначение
Имя поля формыfile
Максимальный размер10 МБ
Типыизображения (JPEG, PNG, WebP и другие), а также MP4 и WebM
Стоимостьбесплатно

Ссылка временная

Поле url живёт около суток. Для долгих сценариев сохраняйте key — по нему всегда можно получить свежую ссылку. Не записывайте подписанные ссылки в свою базу как постоянные.

Картинки

Генерация асинхронная. Запрос возвращает 202 и номер задачи сразу — картинки ещё нет. Готовый результат забирают опросом статуса или через вебхук.

Создание

bash
curl -sS -X POST https://arckep.ru/api/v1/images/generations \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "предметная съёмка керамической кружки, мягкий студийный свет",
    "n": 2,
    "size": "1024x1024",
    "quality": "low"
  }'
ПолеТипОписание
modelстрокаОбязательно. Id из каталога с типом image
promptстрокаОбязательно. От 1 до 10 000 символов
nчислоСколько картинок, 1–8. Синоним — num_images; если заданы оба, побеждает num_images
aspect_ratioстрокаПропорции: 1:1, 16:9, 9:16, 3:2, 2:3 и другие. Что поддерживает модель — в каталоге
sizeстрокаДля совместимости с OpenAI: 1024x1024 и подобные превращаются в пропорции, если aspect_ratio не задан
resolutionстрокаНапример 1K, 2K, 4K — если модель это умеет
qualityстрокаУровень качества у моделей, где он есть
thinkingда/нетРежим «подумать подольше». Дороже, доступен не у всех моделей
reference_image_urlsсписок ссылокИсходники для редактирования или переноса стиля
webhook_urlстрокаКуда прислать уведомление именно об этой задаче (перекрывает адрес ключа)

С опорным изображением

Некоторые модели умеют редактировать присланную картинку или держать один и тот же объект на разных кадрах. Для них передайте ссылки в reference_image_urls.

bash
curl -sS -X POST https://arckep.ru/api/v1/images/generations \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3-pro-image",
    "prompt": "тот же объект, снаружи, закатный свет",
    "reference_image_urls": ["https://…ссылка-из-uploads…"],
    "webhook_url": "https://hooks.example.com/arckep"
  }'

Модели-редакторы требуют исходник

Модели редактирования и апскейла без reference_image_urls вернут ошибку сразу, не списав денег. В каталоге у них стоит признак requires_reference_image.

Статус и результат

bash
curl -sS -H "Authorization: Bearer $ARK_KEY" \
  https://arckep.ru/api/v1/images/generations/123
Готовая задача
{
  "id": 123,
  "object": "image.generation",
  "status": "completed",
  "model": "gemini-3.1-flash-image",
  "created_at": "2026-07-27T12:00:00+00:00",
  "completed_at": "2026-07-27T12:00:24+00:00",
  "cost_rub": "4.24",
  "charged_from": "personal",
  "error": null,
  "output": {
    "images": [
      { "url": "https://arckep.ru/…", "expires_in": null },
      { "url": "https://…подписанная…", "expires_in": 86400 }
    ]
  }
}
СтатусЧто значит
queuedПринято, ждёт очереди
processingМодель работает
completedГотово, ссылки в output
failedНе получилось, причина в error, деньги возвращены

Поле output заполняется только у завершённых задач. У части ссылок есть expires_in — срок жизни в секундах, примерно сутки. Если ссылка протухла, запросите статус задачи ещё раз и получите свежую.

Сколько будет стоить

Цену можно узнать заранее, ничего не запуская и не списывая. Считается тем же механизмом, что и реальное списание.

bash
curl -sS -X POST https://arckep.ru/api/v1/images/estimate \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.1-flash-image", "num_images": 1, "resolution": "1K"}'

Список своих задач

Возвращает только задачи, созданные через API. История из веб-студии сюда не попадает никогда — даже если ключ принадлежит тому же аккаунту.

bash
curl -sS -H "Authorization: Bearer $ARK_KEY" \
  "https://arckep.ru/api/v1/images/generations?limit=20"

Для листания передавайте before_id — номер самой старой задачи из предыдущей страницы. Признак has_more подскажет, есть ли ещё.

Видео

Устроено так же, как картинки: запрос возвращается сразу с номером задачи, готовый ролик появляется позже. Разница в сроках — видео считается от десятков секунд до десятков минут, а у тяжёлых моделей дольше.

Режимы

РежимЧто делаетЧто нужно передать
t2vВидео по текстовому описаниютолько prompt
i2vОживить картинкуобязательно image_url
r2vПо опорным материалам: персонаж, движение, голосreference_image_urls / reference_video_urls / reference_audio_urls
editПеределать готовое видеоvideo_url с исходным роликом
autoРежим определяется по тому, что вы прислализначение по умолчанию

Не каждая модель умеет всё. Доступные режимы конкретной модели перечислены в каталоге в поле modes. Если модель называется с окончанием -i2v, она работает только от картинки, и текстовый режим для неё вернёт ошибку.

Создание

По тексту
curl -sS -X POST https://arckep.ru/api/v1/videos/generations \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "model": "wan2.7-t2v",
    "prompt": "кинематографичный облёт дроном над туманными горами",
    "mode": "t2v",
    "duration_seconds": 5,
    "aspect_ratio": "16:9",
    "with_audio": true
  }'
Из картинки
curl -sS -X POST https://arckep.ru/api/v1/videos/generations \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan2.7-i2v",
    "prompt": "медленный наезд камеры, естественное движение",
    "mode": "i2v",
    "image_url": "https://…ссылка-из-uploads…",
    "duration_seconds": 5
  }'
ПолеПо умолчаниюОписание
modelОбязательно, из каталога с типом video
promptОписание сцены, до 10 000 символов
modeautot2v, i2v, r2v, edit или auto
duration_seconds5От 1 до 30. Предел конкретной модели смотрите в каталоге
aspect_ratio16:9Пропорции кадра
resolutionНапример 720p или 1080p, если модель различает
with_audioдаЗвук у моделей, которые его умеют
image_urlСтартовый кадр. Обязателен для i2v
last_frame_image_urlФинальный кадр — у моделей с интерполяцией
reference_image_urlsОпорные изображения: персонаж, стиль, предметы
reference_video_urlsОпорное видео: движение, монтаж
reference_audio_urlsОпорный звук: озвучка, синхрон губ
video_urlИсходник для режима редактирования
negative_promptЧего не должно быть в кадре
seedДля повторяемости результата
webhook_urlАдрес уведомления для этой задачи

Видео оплачивается вперёд

В отличие от картинок, деньги за видео списываются сразу при постановке задачи. Если провайдер не смог её принять или ролик в итоге не получился, сумма возвращается на баланс автоматически.

Автоматическая длительность

У части моделей длительность выбирает сама модель. Для таких запросов передайте auto_duration. Резервируется максимальная длительность, а после готовности лишнее возвращается — вы платите за фактический хронометраж.

Статус и результат

Готовая задача
{
  "id": 456,
  "object": "video.generation",
  "status": "completed",
  "model": "wan2.7-t2v",
  "cost_rub": "25.00",
  "charged_from": "personal",
  "error": null,
  "output": {
    "video_url": "https://…подписанная…",
    "thumbnail_url": "https://…подписанная…",
    "duration_seconds": 5
  }
}

Статус берётся из нашей базы: мы сами опрашиваем провайдера в фоне, вам не нужно держать соединение или повторять запрос к нему. Ссылки на видео и превью — временные, примерно на сутки; за свежими просто запросите задачу снова.

Предварительный расчёт цены — POST /api/v1/videos/estimate с моделью, длительностью, разрешением и признаком звука. Список своих задач и листание работают так же, как у картинок.

Языковые модели (чат)

Единственная синхронная часть API: ответ приходит в том же запросе. Формат совместим с OpenAI, поэтому подходят готовые библиотеки — достаточно подменить адрес и ключ.

Нужно право chat:write

По умолчанию новый ключ его не получает. Создавая ключ, укажите это право явно — иначе запрос вернёт FORBIDDEN.

Обычный запрос

bash
curl -sS -X POST https://arckep.ru/api/v1/chat/completions \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-mini",
    "messages": [
      {"role": "system", "content": "Отвечай кратко."},
      {"role": "user", "content": "Сколько будет 2+2?"}
    ],
    "stream": false,
    "temperature": 0.2,
    "max_tokens": 256
  }'
Ответ
{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "model": "openai/gpt-5.4-mini",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "4" },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 24,
    "completion_tokens": 1,
    "total_tokens": 25,
    "cost_rub": "0.0123"
  }
}

Поле usage.cost_rub — наше дополнение к формату OpenAI: сколько списано за этот запрос в рублях. Приходит после фактического списания.

Потоковый ответ

С stream: true ответ отдаётся кусками в формате server-sent events, как у OpenAI. Поток заканчивается строкой data: [DONE], а в предпоследнем куске приходит usage с итоговой стоимостью.

bash
curl -sSN -X POST https://arckep.ru/api/v1/chat/completions \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"xai/grok-4.3","messages":[{"role":"user","content":"Привет"}],"stream":true}'

Через библиотеку OpenAI

python
from openai import OpenAI

client = OpenAI(
    api_key="ark_live_…",           # ключ должен иметь право chat:write
    base_url="https://arckep.ru/api/v1",
)

r = client.chat.completions.create(
    model="openai/gpt-5.4-mini",
    messages=[{"role": "user", "content": "Привет"}],
)
print(r.choices[0].message.content)

Вызов функций

Модель может попросить вызвать вашу функцию. Опишите функции в поле tools в формате OpenAI. Выполняете их вы у себя — мы ничего не запускаем, только передаём запрос и ответ.

json
{
  "model": "openai/gpt-5.4-mini",
  "messages": [{"role": "user", "content": "Какая погода в Берлине?"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Текущая погода по городу",
      "parameters": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  }],
  "tool_choice": "auto"
}

Если модель решила вызвать функцию, в ответе будет finish_reason: tool_calls и список вызовов. Выполните их и продолжите диалог, добавив сообщения с ролью tool и полем tool_call_id. Несколько вызовов за один ход поддерживаются.

Только обычные функции

Встроенные инструменты провайдеров — интерпретатор кода, поиск по файлам и прочие — отклоняются. Они тарифицируются отдельно и по чужим правилам. Разрешены только описания с type: function.

Поиск в интернете

Флаг web_search: true включает встроенный поиск провайдера — модель сама ищет свежие данные. Работает у Anthropic, Google и xAI. У обычных моделей OpenAI такого режима в этом формате нет: запрос вернёт ошибку 400 с пояснением, а не молча выполнится без поиска.

bash
curl -sS -X POST https://arckep.ru/api/v1/chat/completions \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google-ai-studio/gemini-3.5-flash",
    "messages": [{"role": "user", "content": "Что сегодня в новостях технологий?"}],
    "web_search": true,
    "max_tokens": 1024
  }'

Поиск оплачивается отдельно

Помимо токенов провайдер берёт плату за каждый поисковый запрос, и она включается в стоимость. Какие модели поддерживают поиск — видно в каталоге, в поле capabilities.

Ограничения запроса

ОграничениеЗначение
Длина одного сообщения100 000 символов
Количество сообщений64
Суммарный объём200 000 символов, включая описания функций
max_tokensне больше 16 384
Количество функцийдо 32
Одновременных потоков на ключ5

Картинки в сообщениях не принимаются — для генерации изображений есть отдельный раздел API. Диалоги через API не сохраняются в истории веб-студии: остаются только записи о списаниях.

Деньги

Цены такие же, как в студии. Списывается с вашего личного баланса, а если вы состоите в активной организации — с её счёта; в ответе это видно в поле charged_from.

Картинки: сначала резерв, потом сверка

При постановке задачи с баланса резервируется расчётная стоимость. Когда картинка готова, сумма сверяется с фактической: разница либо доплачивается, либо возвращается. Если генерация не удалась или сработал фильтр безопасности — резерв возвращается целиком.

Резерв берётся до обращения к провайдеру. Поэтому несколько параллельных задач не могут суммарно потратить больше, чем есть на балансе.

Видео: оплата вперёд

Здесь сумма списывается сразу при постановке задачи. Если провайдер не принял задачу или ролик в итоге не получился, деньги возвращаются автоматически — отдельно просить возврат не нужно.

Чат: оплата по факту

Заранее списать нельзя — стоимость зависит от длины ответа, которую заранее никто не знает. Поэтому перед запросом проверяется, что баланса хватает на предполагаемую стоимость, а списание происходит после ответа, по фактическому расходу токенов.

Баланс может уйти в небольшой минус

Если ответ уже получен, он будет оплачен, даже когда денег на балансе не хватило — иначе мы бы отдавали чужую работу бесплатно. Уйти в глубокий минус не получится: следующий запрос не пройдёт проверку баланса.

Узнать цену заранее

Для картинок и видео есть расчёт без запуска: POST /images/estimate и POST /videos/estimate. Считают тем же кодом, что и настоящее списание, поэтому расхождений быть не должно.

Для чата предварительной оценки нет — ориентируйтесь на цены за миллион токенов из каталога моделей (поле pricing_rub_per_1m).

Не хватает денег

Придёт код INSUFFICIENT_BALANCE и HTTP 402. В details будет, сколько нужно и сколько есть. Пополнить — в личном кабинете.

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

Ограничения частоты

ОграничениеПо умолчанию
Запросов в минуту60 на ключ
Запросов в сутки500 на ключ
Одновременных задач (картинки и видео)5 на ключ
Одновременных потоков чата5 на ключ

При превышении приходит HTTP 429 с кодом RATE_LIMIT_EXCEEDED. В details указано, какой именно предел сработал — по этому полю удобно решать, ждать секунду или минуту. Лимиты считаются на ключ, поэтому не раздавайте один ключ десяти воркерам.

Защита от двойного списания

При создании картинки или видео передавайте заголовок Idempotency-Key с уникальным значением, например UUID. Если запрос оборвётся по таймауту и вы повторите его с тем же ключом, задача не создастся заново — вы получите тот же ответ и тот же номер задачи.

http
Idempotency-Key: 6f1c0a4e-6c1a-4c94-a0f2-6f0b6f0f5f1a
СитуацияЧто произойдёт
Тот же ключ, запрос уже выполнен (в пределах ~2 минут)Вернётся тот же ответ, повторного списания нет
Тот же ключ, первый запрос ещё выполняетсяHTTP 409 — подождите и повторите
Ключ не переданЗапрос выполнится обычным образом

Для чата заголовок не нужен: ответ приходит сразу, повторять нечего.

Как узнать, что готово

Есть два способа: спрашивать статус самому или получить уведомление на свой адрес. Для промышленных сценариев лучше уведомления — они не тратят лимит запросов и приходят сразу.

СпособКогда подходитКак часто спрашивать
Опрос статусаСкрипты, разовые задачи, отладкаКартинки — раз в 2–5 секунд, видео — раз в 5–15 секунд
ВебхукиПостоянные интеграции, n8n, серверные сценариине нужно — придёт само

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

Вебхуки

Мы отправляем POST-запрос на ваш адрес, когда задача завершилась — успешно или нет. Адрес задаётся один раз для ключа или отдельно для каждой задачи полем webhook_url.

Какие события приходят

СобытиеКогда
image.generation.completedКартинка готова
image.generation.failedКартинка не получилась, деньги возвращены
video.generation.completedВидео готово
video.generation.failedВидео не получилось, деньги возвращены
Пример тела запроса
{
  "id": 123,
  "object": "image.generation",
  "status": "completed",
  "model": "gemini-3.1-flash-image",
  "cost_rub": "4.24",
  "error": null,
  "output": {
    "images": [{ "url": "https://arckep.ru/…" }]
  }
}

Проверка подлинности

Вместе с телом мы присылаем подпись. Она доказывает, что запрос отправили мы, а не кто-то, кто узнал ваш адрес. Секрет для проверки выдаётся один раз при создании ключа — там же, где сам ключ.

ЗаголовокЗначение
X-Arckep-SignatureHMAC-SHA256 от сырого тела запроса, в hex
X-Arckep-EventНазвание события
X-Arckep-Delivery-IdНомер доставки — одинаковый у повторов
User-AgentArckep-Webhooks/1.0
Проверка подписи, Python
import hmac, hashlib

def verify(secret: str, raw_body: bytes, signature_hex: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_hex or "")

Считайте подпись от сырых байтов

Подпись берётся от тела ровно в том виде, в каком оно пришло. Если сначала разобрать JSON, а потом собрать обратно, порядок полей и пробелы изменятся, и подпись не сойдётся.

Секрет можно сменить, не трогая сам ключ: POST /api/developer/keys/{id}/rotate-webhook-secret. Новый секрет тоже показывается один раз, старый сразу перестаёт работать.

Повторы и надёжность

  • Отвечайте кодом 2xx как можно быстрее, тяжёлую работу делайте потом у себя.
  • Если ваш сервер не ответил или вернул ошибку, доставка повторяется с нарастающими паузами, до 5 попыток.
  • Очередь доставок хранится в базе, поэтому перезапуск нашего сервиса ничего не теряет.
  • Один и тот же номер доставки может прийти несколько раз — учитывайте это и не выполняйте действие дважды.

Требования к адресу

Адрес должен быть на HTTPS и вести на публичный хост. Локальные и внутренние адреса отклоняются при сохранении — это защита от использования нашего сервера как посредника для доступа к чужим внутренним сетям.

  • Только HTTPS.
  • Нельзя localhost, внутренние диапазоны IP, служебные адреса и домены вроде .local или .internal.
  • Проверка идёт после разрешения имени, так что подменить домен на внутренний IP не выйдет.

Если нужно принимать вебхуки на машине без публичного адреса, используйте туннель (ngrok, Cloudflare Tunnel) — он даёт настоящий HTTPS-адрес.

Ошибки

Все ошибки приходят в одном формате. Ориентируйтесь в коде на поле error_code, а не на текст сообщения: формулировки мы улучшаем, коды не меняем.

json
{
  "error_code": "INSUFFICIENT_BALANCE",
  "message": "Insufficient personal balance: need 15.00 ₽, available 5.00 ₽…",
  "details": {
    "required": 15.0,
    "available": 5.0,
    "is_corporate": false,
    "currency": "RUB"
  }
}

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

КодHTTPЧто случилосьЧто делать
UNAUTHORIZED401Ключ не передан, неверен или отозванПроверьте заголовок Authorization и сам ключ. Секрет виден только при создании — если потерян, заведите новый
FORBIDDEN403У ключа нет нужного права либо аккаунт заблокированПосмотрите details.required и создайте ключ с этим правом
INSUFFICIENT_BALANCE402Не хватает денег на операциюПополните баланс. В details — сколько нужно и сколько есть
INVALID_MODEL400Модель недоступна через API или в id опечаткаВозьмите id из каталога моделей
INVALID_PARAMETER400Одно из полей имеет недопустимое значениеСмотрите details.field и details.reason
VALIDATION_ERROR400 / 422Тело запроса не подходит под схему: превышены лимиты, неверная комбинация полейПрочитайте message — там указано конкретное нарушение
SAFETY_BLOCKED400Контент отклонён фильтрами провайдераСмягчите формулировку или замените исходные материалы
RESOURCE_NOT_FOUND404Задача не найдена: чужой номер или это генерация из веб-студииИспользуйте номер из ответа на создание
REQUEST_IN_PROGRESS409Запрос с таким Idempotency-Key ещё выполняетсяПодождите и повторите — или возьмите новый ключ идемпотентности для новой задачи
FILE_TOO_LARGE413Файл больше допустимогоСожмите файл, предел — 10 МБ
RATE_LIMIT_EXCEEDED429Превышена частота или число одновременных задачСделайте паузу; в details указано, какой предел сработал
GENERATION_FAILED500Ошибка на стороне провайдера или у насПовторите с задержкой. Деньги за неудавшуюся задачу возвращаются
SERVICE_UNAVAILABLE503API временно отключёнПовторите позже

Как обрабатывать правильно

ГруппаПовторять?
400, 402, 403, 404, 413Нет. Запрос надо исправить или пополнить баланс
429Да, но с паузой и увеличением интервала
500, 503Да, с нарастающей задержкой
409Да, через несколько секунд — исходный запрос ещё выполняется

Не считайте деньги списанными по факту ошибки

Если запрос завершился ошибкой, деньги либо не списывались, либо уже возвращены. Настоящую сумму всегда показывает сама задача в поле cost_rub и история операций в личном кабинете.

Готовые рецепты

Картинка от начала до конца

Скрипт загружает исходник, ставит задачу с защитой от двойного списания и ждёт результат.

bash
export ARK_KEY="ark_live_..."
export BASE="https://arckep.ru/api/v1"

# 1. Загрузить исходник (если нужен)
UP=$(curl -sS -X POST "$BASE/uploads" \
  -H "Authorization: Bearer $ARK_KEY" \
  -F "file=@./input.png")
REF_URL=$(echo "$UP" | jq -r .url)

# 2. Поставить задачу
JOB=$(curl -sS -X POST "$BASE/images/generations" \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d "{
    \"model\": \"gemini-3.1-flash-image\",
    \"prompt\": \"кот-космонавт, студийный свет\",
    \"reference_image_urls\": [\"$REF_URL\"]
  }")
ID=$(echo "$JOB" | jq -r .id)

# 3. Ждать результат
while true; do
  S=$(curl -sS -H "Authorization: Bearer $ARK_KEY" "$BASE/images/generations/$ID")
  ST=$(echo "$S" | jq -r .status)
  echo "$S" | jq -c '{status, cost_rub, error}'
  [ "$ST" = "completed" ] && echo "$S" | jq -r '.output.images[].url' && break
  [ "$ST" = "failed" ] && echo "Ошибка: $(echo "$S" | jq -r .error)" && break
  sleep 3
done

Видео по тексту

bash
curl -sS -X POST "$BASE/videos/generations" \
  -H "Authorization: Bearer $ARK_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "model": "veo-3.1-fast-generate-preview",
    "prompt": "волны на пляже, золотой час",
    "mode": "t2v",
    "duration_seconds": 5
  }'

Приём вебхука на Python

python
import hmac, hashlib
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()
SECRET = "…секрет вебхука, показан один раз при создании ключа…"

@app.post("/hooks/arckep")
async def arckep_hook(request: Request):
    raw = await request.body()            # именно сырые байты
    signature = request.headers.get("X-Arckep-Signature", "")
    expected = hmac.new(SECRET.encode(), raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        raise HTTPException(status_code=401, detail="bad signature")

    event = request.headers.get("X-Arckep-Event")
    payload = await request.json()
    # Отвечаем быстро, обработку ставим в очередь
    enqueue(event, payload)
    return {"ok": True}

Подключение в n8n

  • Узел HTTP Request, метод POST, адрес https://arckep.ru/api/v1/images/generations.
  • Авторизация: заголовок Authorization со значением Bearer ark_live_…. Храните ключ в Credentials, а не в теле узла.
  • Дальше либо узел ожидания и повторный запрос статуса, либо узел Webhook как приёмник уведомления — второй вариант надёжнее и не тратит лимит.

Языковая модель через библиотеку OpenAI

python
from openai import OpenAI

client = OpenAI(api_key="ark_live_…", base_url="https://arckep.ru/api/v1")

stream = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Объясни рекурсию за три предложения"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

Если что-то не работает

Начните с GET /api/v1/balance — он проверяет ключ, не тратя денег. Затем GET /api/v1/models — убедитесь, что модель есть в списке и её id написан точно. Большинство проблем на старте — это опечатка в id модели или недостающее право у ключа.

Остались вопросы

Пишите на почту googlmen1057@gmail.com — приложите номер задачи и код ошибки, так разберёмся быстрее. Машинная версия этого руководства для AI-агентов доступна по адресу arckep.ru/llms.txt.