Если задача — разобраться, как подключить api нейросети к сайту, главный вопрос возникает не вокруг самого HTTP-запроса, а вокруг того, где он выполняется. Браузер пользователя не должен получать секретный ключ: запрос к API лучше отправлять со своего сервера, а клиенту возвращать только нужные данные. В этом материале разберём схему на PHP и на серверном Node.js, ограничение запросов для пользователей, отображение прогресса и выдачу готового результата.
Для работы используется API с базовым адресом https://genius-bot.ru/wp-json/genius/v1. Ключ выдаётся после входа в разделе API и показывается один раз, поэтому его сразу нужно сохранить в конфигурации сервера, а не вставлять в JavaScript-код страницы.
API работает через обычные HTTP-запросы. Есть отдельные маршруты для списка операций, баланса, загрузки файлов, запуска задачи и проверки её состояния, а для завершённых задач можно дополнительно использовать webhook. Такая структура позволяет оставить браузеру только интерфейс, а всю работу с авторизацией и API выполнять на бэкенде.
Почему ключ нельзя отдавать браузеру
Самая частая ошибка при интеграции — поместить ключ API в JavaScript и сделать запрос напрямую из браузера. Например, разработчик может написать fetch(), добавить заголовок Authorization и передать ключ в коде страницы. Проблема в том, что код JavaScript и сетевые запросы доступны самому пользователю.
Даже если ключ не лежит в отдельном файле, его можно обнаружить через исходный код, DevTools или вкладку Network. После этого другой человек сможет выполнять запросы от имени вашего ключа. Для API, где операции оплачиваются за запуск, это означает ещё и возможность расходования вашего баланса.
Авторизация выполняется заголовком:
Authorization: Bearer <ключ>
Ключ хранится на сервере. Браузер отправляет, например, POST /api/generate на ваш сайт, сервер проверяет пользователя и формирует запрос к API с секретным заголовком. В ответ сервер возвращает браузеру только идентификатор задачи, состояние, ссылку на результат или сообщение об ошибке.
Именно поэтому формулировка «api нейросети на сайте» на практике означает не обязательно прямое подключение браузера к внешнему API. Надёжнее разделить систему на три части: интерфейс, собственный сервер и API. Ключ находится только во второй части.
Есть и ещё одна причина не хранить секрет в клиентском коде: ограничение в 60 запросов в минуту действует на ключ. Если ключ доступен браузеру, контролировать его использование исключительно средствами вашего интерфейса становится значительно сложнее.

Прокси на своём сервере: минимальная схема
Минимальная схема выглядит так: пользователь отправляет данные вашему серверу, сервер добавляет Bearer-авторизацию и передаёт запрос API, после чего возвращает результат клиенту. Такой промежуточный слой часто называют прокси, хотя технически это может быть обычный endpoint вашего приложения.
Для генерации задача запускается через POST /generate. Сначала можно получить список доступных операций через GET /services, чтобы интерфейс сайта не содержал вручную поддерживаемый список. Остаток баланса проверяется через GET /balance.
Ниже минимальный пример запроса через curl. Конкретные поля тела запроса зависят от выбранной операции, поэтому в примере показан сам HTTP-механизм вызова маршрута.
curl -X POST "https://genius-bot.ru/wp-json/genius/v1/generate" \
-H "Authorization: Bearer $GENIUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"service": "image",
"prompt": "Минималистичная иллюстрация рабочего стола"
}'
Переменная GENIUS_API_KEY здесь находится в окружении сервера, а не в HTML или JavaScript. Это важное различие: пользователю можно показать кнопку «Создать изображение», но не значение переменной, с помощью которой сервер авторизуется.
Для PHP простая серверная функция может выглядеть следующим образом:
<?php
$apiKey = getenv('GENIUS_API_KEY');
$payload = [
'service' => 'image',
'prompt' => 'Минималистичная иллюстрация рабочего стола'
];
$ch = curl_init(
'https://genius-bot.ru/wp-json/genius/v1/generate'
);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE)
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
http_response_code($status);
header('Content-Type: application/json; charset=utf-8');
echo $response;
Такой код не следует помещать в публичную директорию как готовую страницу с доступным ключом. Он должен выполняться на сервере, а клиентский endpoint вашего приложения должен решать, кому разрешён запуск операции.
Если приложение написано на Node.js, принцип тот же. Сначала получаем ключ из переменных окружения, затем отправляем серверный запрос:
const response = await fetch(
'https://genius-bot.ru/wp-json/genius/v1/generate',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GENIUS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
service: 'image',
prompt: 'Минималистичная иллюстрация рабочего стола'
})
}
);
const data = await response.json();
Для интеграции с Python можно использовать requests. Здесь также нет необходимости передавать секрет в браузер:
import os
import requests
url = "https://genius-bot.ru/wp-json/genius/v1/generate"
headers = {
"Authorization": f"Bearer {os.environ['GENIUS_API_KEY']}",
"Content-Type": "application/json",
}
payload = {
"service": "image",
"prompt": "Минималистичная иллюстрация рабочего стола",
}
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
print(response.json())
Кроме маршрута /generate, в API есть POST /uploads для загрузки файла и получения ссылки. Это удобно для сценариев, где пользователь сначала отправляет исходное изображение, а затем сервер передаёт полученную ссылку в операцию обработки.
После запуска API возвращает идентификатор задачи. Его можно использовать с GET /tasks/{id}, чтобы получить текущее состояние и результат. Для длинных операций это лучше, чем держать один HTTP-запрос открытым до самого окончания генерации.
| Операция | Цена за запуск |
|---|---|
| Картинка по описанию (image) | 9 ₽ |
| Оживить фото (photo-video) | 25 ₽ |
| Изменить фото по описанию (image-edit) | 35 ₽ |
| Увеличить качество фото (upscale) | 50 ₽ |
| Видео по описанию (video) | 119 ₽ |
| Создать музыку (music) | 59 ₽ |
| Расшифровка записи (stt) | 10 ₽ |
| Говорящий аватар (avatar) | 120 ₽ |
| Убрать вокал (vocal) | 45 ₽ |
| Убрать шум (denoise) | 36 ₽ |
| Звук по описанию (sfx) | 9 ₽ |
| Озвучка текста (tts) | 18 ₽ за 1000 знаков |
Цены в таблице указаны за один запуск, кроме озвучки, где расчёт идёт за 1000 знаков. Для самого сайта это означает, что перед запуском полезно показывать пользователю стоимость выбранной операции и учитывать её при проектировании ограничений.
Ограничение по пользователю и по сессии
Лимита на уровне одного API-ключа недостаточно для пользовательского приложения. API ограничивает частоту до 60 запросов в минуту на ключ, но ваш сайт может дополнительно установить собственные правила: например, ограничить число запусков для конкретного аккаунта или временно заблокировать повторную отправку одной формы.
Удобная схема — создавать запись о каждой операции в собственной базе данных. В ней можно хранить внутренний ID пользователя, ID задачи API, выбранную операцию, время запуска и состояние. Тогда сервер понимает, кто инициировал задачу, и не доверяет данным, пришедшим из браузера.
Ограничение по сессии подходит для простого публичного интерфейса. Например, после запуска сервер записывает в сессию время последнего запроса и не принимает новый запуск в течение заданного вами интервала. Для авторизованных пользователей лучше использовать их внутренний идентификатор и хранить счётчик в базе или другом серверном хранилище.
Важно разделять два лимита. Ограничение вашего сайта отвечает на вопрос «сколько операций разрешено этому пользователю», а ограничение API — на вопрос «сколько запросов допускается для данного ключа». Одно не заменяет другое.
Сервер также должен проверять параметры до обращения к API. Например, нельзя принимать от клиента произвольный URL callback, если он вообще не нужен вашему приложению, и нельзя позволять пользователю самостоятельно присылать значение заголовка Authorization. Авторизация всегда должна формироваться сервером.
Для платных операций полезно предусмотреть защиту от двойного клика. Если пользователь нажал «Создать» два раза, браузер может отправить два запроса, а значит, приложение потенциально запустит две операции. Кнопку можно временно блокировать на клиенте, но окончательная защита от дублей должна находиться на сервере.

Показ прогресса на странице без перезагрузки
После запуска задачи не обязательно держать пользователя на странице с индикатором бесконечной загрузки. Сервер может сразу вернуть собственный идентификатор операции и ID задачи API, после чего браузер периодически обращается к вашему endpoint со статусом.
Схема получается такой: POST /my-api/generate → сервер вызывает POST /generate → сохраняет ID задачи → браузер получает ID своей операции → JavaScript запрашивает GET /my-api/tasks/{id}. Ваш сервер в свою очередь проверяет принадлежность задачи пользователю и обращается к GET /tasks/{id}.
Не стоит отдавать браузеру прямой доступ к /tasks/{id} с API-ключом. Иначе защита, построенная для запуска, снова обходится на этапе проверки статуса.
На клиенте можно использовать простой интервал:
async function waitForTask(id) {
while (true) {
const response = await fetch(`/api/tasks/${id}`);
const task = await response.json();
document.querySelector('#status').textContent =
task.status;
if (task.status === 'completed' ||
task.status === 'failed') {
return task;
}
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
Интервал в 3 секунды здесь является примером архитектуры, а не требованием API. Его можно изменить под интерфейс и нагрузку. При большом количестве пользователей лучше продумать серверное кэширование состояния, чтобы каждый просмотр страницы не создавал лишние обращения.
Вместо опроса API можно использовать webhook. При запуске операции передаётся необязательный параметр callback_url, после чего при готовности задачи на этот адрес приходит POST. Сервер принимает уведомление, обновляет запись в базе, а страница пользователя может периодически проверять уже вашу базу данных.
Webhook особенно удобен для длительных операций. При этом endpoint webhook также должен быть серверным, а полученные данные нужно проверять и сопоставлять с ожидаемой задачей, прежде чем помечать её завершённой.
Где хранить результат и как отдавать его пользователю
Результат операции лучше отделять от состояния задачи. В базе данных достаточно хранить ID пользователя, внутренний ID операции, ID задачи API, статус и данные о результате. Сам тяжёлый файл не обязательно помещать в реляционную таблицу.
Для файлового сценария сначала можно использовать POST /uploads, который загружает файл и возвращает ссылку. После завершения обработки приложение получает результат через состояние задачи и уже решает, как показывать его пользователю.
Если результат представляет собой ссылку, не нужно без необходимости проксировать весь файл через PHP или Node.js. Можно сохранить ссылку на сервере и выдать её только после проверки того, что текущий пользователь имеет право получить соответствующую операцию.
Для приватных пользовательских файлов имеет смысл использовать внутренний маршрут вроде /download/12345. Сервер сначала проверяет сессию и владельца записи, затем отдаёт файл или перенаправляет на разрешённый адрес. Так URL с результатом не становится единственным механизмом контроля доступа.
При этом нужно учитывать срок жизни внешних ссылок, если конкретная операция его устанавливает. Если приложение рассчитывает хранить результаты долго, это следует проектировать отдельно от самого вызова API, поскольку API предоставляет операции запуска и получения состояния, а способ долговременного хранения результата является задачей вашего приложения.
Для первых тестов не требуется строить сложное хранилище. Достаточно записать ID задачи и статус в базу, вывести результат после завершения и проверить полный цикл: загрузка файла, запуск, ожидание, получение результата и выдача пользователю.

Проверка перед выкладкой
Перед публикацией сайта сначала проверьте, что секрет действительно отсутствует в клиентском коде. Откройте исходный HTML, JavaScript-файлы и вкладку Network в браузере. В запросах из браузера не должно быть значения Authorization: Bearer ... с вашим реальным ключом.
Затем проверьте серверный маршрут отдельно. Он должен отклонять запросы без авторизации вашего приложения, проверять входные параметры и самостоятельно добавлять API-ключ. Пользователь не должен иметь возможности заменить ваш ключ собственным значением через поле формы или HTTP-заголовок.
Проверьте и ошибочные сценарии: API недоступно, задача завершилась ошибкой, пользователь обновил страницу во время обработки, браузер повторил запрос, а файл не прошёл проверку. В каждом случае запись о задаче должна оставаться согласованной с фактическим состоянием.
Отдельно проверьте частоту запросов. У ключа есть ограничение не более 60 запросов в минуту, поэтому бесконечный polling с коротким интервалом для каждого открытого клиента — плохая конструкция. Интервалы, серверное кэширование и webhook позволяют не превращать проверку статуса в поток лишних запросов.
Также стоит проверить баланс через GET /balance и список операций через GET /services. Для самого сервиса предусмотрена оплата за запуск без абонентской платы и без минимального платежа, а при выпуске первого ключа на баланс начисляется 50 ₽ для пробы. Эти данные можно учитывать в административной части сайта, не показывая пользователю секретный ключ.
Если для сайта нужен чат, у API есть совместимые с OpenAI маршруты POST /chat/completions и GET /models. В клиентской библиотеке для такого сценария достаточно заменить base_url и ключ; тариф составляет 40 ₽ за миллион токенов запроса и 400 ₽ за миллион токенов ответа.
Итого схема подключения получается простой: браузер знает только API вашего приложения, сервер хранит секрет, сервер запускает операцию, база связывает задачу с пользователем, а статус доставляется через polling или webhook. Это и есть практический способ сделать api нейросети на сайте без публикации ключа.
Частые вопросы
Можно ли поставить ключ API прямо в JavaScript?
Технически браузер сможет отправить такой запрос, но секрет перестанет быть секретом. Любой пользователь сможет увидеть ключ в коде страницы или сетевых запросах, поэтому авторизацию следует выполнять на сервере.
Где получить ключ?
Ключ выдаётся после входа в разделе API. Он показывается один раз, поэтому после выпуска его нужно сохранить на сервере, например в переменной окружения, а не в репозитории или публичном JavaScript-файле.
Нужно ли постоянно ждать ответ от /generate?
Нет. После запуска можно сохранить ID задачи и проверять её через GET /tasks/{id}. Для уведомления о готовности также предусмотрен необязательный callback_url, на который приходит POST.
Как не дать одному пользователю запускать операции бесконечно?
В собственном приложении добавьте ограничение по аккаунту или сессии и сохраняйте историю запусков. Дополнительно учитывайте ограничение API — не более 60 запросов в минуту на ключ.
Можно ли загружать исходные файлы?
Для этого предусмотрен POST /uploads. Он принимает файл и возвращает ссылку, которую затем можно использовать в последующей логике вашего приложения.
Где посмотреть доступные операции и цены?
Маршрут GET /services возвращает список операций и цен. Полная информация для получения ключа доступна в разделе API и ключей.
